Procházet zdrojové kódy

refactor: reformat theme-related components

Adam před 3 měsíci
rodič
revize
a159d33a9c

+ 1 - 1
backend/themes.json

@@ -1,5 +1,5 @@
 {
-  "activeTheme": "forest",
+  "activeTheme": "default",
   "customThemes": [],
   "builtInThemes": [
     {

+ 400 - 348
src/App.jsx

@@ -1,389 +1,441 @@
-import React, { useState, useEffect } from 'react';
-import { BrowserRouter as Router, Routes, Route, Link, useParams } from 'react-router-dom';
-import MarkdownIt from 'markdown-it';
-import { full as emoji } from 'markdown-it-emoji';
+import React, { useState, useEffect } from "react";
+import {
+    BrowserRouter as Router,
+    Routes,
+    Route,
+    Link,
+    useParams,
+} from "react-router-dom";
+import MarkdownIt from "markdown-it";
+import { full as emoji } from "markdown-it-emoji";
 import footnote from "markdown-it-footnote";
-import DOMPurify from 'dompurify';
-import { AuthProvider, useAuth } from './contexts/AuthContext';
-import { ThemeProvider } from './contexts/ThemeContext';
-import AdminDashboard from './components/AdminDashboard';
-import PostEditor from './components/PostEditor';
-import LoginForm from './components/LoginForm';
-import ProtectedRoute from './components/ProtectedRoute';
-import ThemesManager from './components/ThemesManager';
-import ThemeEditor from './components/ThemeEditor';
+import DOMPurify from "dompurify";
+import { AuthProvider, useAuth } from "./contexts/AuthContext";
+import { ThemeProvider } from "./contexts/ThemeContext";
+import AdminDashboard from "./components/AdminDashboard";
+import PostEditor from "./components/PostEditor";
+import LoginForm from "./components/LoginForm";
+import ProtectedRoute from "./components/ProtectedRoute";
+import ThemesManager from "./components/ThemesManager";
+import ThemeEditor from "./components/ThemeEditor";
 
 const scrollableTablesPlugin = (md) => {
-  const defaultRenderOpen = md.renderer.rules.table_open || function (tokens, idx, options, env, self) {
-    return self.renderToken(tokens, idx, options);
-  };
-
-  const defaultRenderClose = md.renderer.rules.table_close || function (tokens, idx, options, env, self) {
-    return self.renderToken(tokens, idx, options);
-  };
-
-  md.renderer.rules.table_open = function (tokens, idx, options, env, self) {
-    return '<div class="overflow-x-auto">' + defaultRenderOpen(tokens, idx, options, env, self);
-  };
+    const defaultRenderOpen =
+        md.renderer.rules.table_open ||
+        function (tokens, idx, options, env, self) {
+            return self.renderToken(tokens, idx, options);
+        };
+
+    const defaultRenderClose =
+        md.renderer.rules.table_close ||
+        function (tokens, idx, options, env, self) {
+            return self.renderToken(tokens, idx, options);
+        };
+
+    md.renderer.rules.table_open = function (tokens, idx, options, env, self) {
+        return (
+            '<div class="overflow-x-auto">' +
+            defaultRenderOpen(tokens, idx, options, env, self)
+        );
+    };
 
-  md.renderer.rules.table_close = function (tokens, idx, options, env, self) {
-    return defaultRenderClose(tokens, idx, options, env, self) + '</div>';
-  };
+    md.renderer.rules.table_close = function (tokens, idx, options, env, self) {
+        return defaultRenderClose(tokens, idx, options, env, self) + "</div>";
+    };
 };
 
 const md = new MarkdownIt({
-  html: true,        // Enable HTML tags in source
-  linkify: true,     // Auto-convert URL-like text to links
-  typographer: true, // Enable some language-neutral replacement + quotes beautification
-  breaks: false      // Convert '\n' in paragraphs into <br>
+    html: true, // Enable HTML tags in source
+    linkify: true, // Auto-convert URL-like text to links
+    typographer: true, // Enable some language-neutral replacement + quotes beautification
+    breaks: false, // Convert '\n' in paragraphs into <br>
 })
-.use(scrollableTablesPlugin)  // Keep our table scrolling enhancement
-.use(emoji)                   // GitHub-style emoji :emoji_name:
-.use(footnote);               // Standard footnotes [^1]
+    .use(scrollableTablesPlugin) // Keep our table scrolling enhancement
+    .use(emoji) // GitHub-style emoji :emoji_name:
+    .use(footnote); // Standard footnotes [^1]
 
-const API_BASE = 'https://goonblog.thevakhovske.eu.org/api';
+const API_BASE = "https://goonblog.thevakhovske.eu.org/api";
 
 // Navigation Header Component
 function NavHeader() {
-  const { isAdmin, user, logout } = useAuth();
-
-  const handleLogout = async () => {
-    await logout();
-  };
-
-  return (
-    <header className="headercontainer py-6 border-b theme-border flex items-center justify-between">
-      <div className="text-2xl font-bold theme-text">
-        <Link to="/"><span className="theme-primary">Goon</span>Blog</Link>
-      </div>
-      <nav>
-        <ul className="flex space-x-4 items-center">
-          <li>
-            <Link
-              to="/"
-              className="theme-text-secondary hover:theme-text transition-colors duration-200 font-medium"
-            >
-              Home
-            </Link>
-          </li>
-          {isAdmin && (
-            <li>
-              <Link
-                to="/admin"
-                className="theme-primary hover:theme-secondary transition-colors duration-200 font-medium"
-              >
-                Admin
-              </Link>
-            </li>
-          )}
-          {user ? (
-            <li className="flex items-center space-x-2">
-              <span className="text-sm theme-text-secondary">Welcome, {user.username}</span>
-              <button
-                onClick={handleLogout}
-                className="text-red-600 hover:text-red-800 transition-colors duration-200 font-medium text-sm"
-              >
-                Logout
-              </button>
-            </li>
-          ) : (
-            <li>
-              <Link
-                to="/login"
-                className="theme-primary hover:theme-secondary transition-colors duration-200 font-medium"
-              >
-                Login
-              </Link>
-            </li>
-          )}
-        </ul>
-      </nav>
-    </header>
-  );
+    const { isAdmin, user, logout } = useAuth();
+
+    const handleLogout = async () => {
+        await logout();
+    };
+
+    return (
+        <header className="headercontainer py-6 border-b theme-border flex items-center justify-between">
+            <div className="text-2xl font-bold theme-text">
+                <Link to="/">
+                    <span className="theme-primary">Goon</span>Blog
+                </Link>
+            </div>
+            <nav>
+                <ul className="flex space-x-4 items-center">
+                    <li>
+                        <Link
+                            to="/"
+                            className="theme-text-secondary hover:theme-text transition-colors duration-200 font-medium"
+                        >
+                            Home
+                        </Link>
+                    </li>
+                    {isAdmin && (
+                        <li>
+                            <Link
+                                to="/admin"
+                                className="theme-primary hover:theme-secondary transition-colors duration-200 font-medium"
+                            >
+                                Admin
+                            </Link>
+                        </li>
+                    )}
+                    {user ? (
+                        <li className="flex items-center space-x-2">
+                            <span className="text-sm theme-text-secondary">
+                                Welcome, {user.username}
+                            </span>
+                            <button
+                                onClick={handleLogout}
+                                className="text-red-600 hover:text-red-800 transition-colors duration-200 font-medium text-sm"
+                            >
+                                Logout
+                            </button>
+                        </li>
+                    ) : (
+                        <li>
+                            <Link
+                                to="/login"
+                                className="theme-primary hover:theme-secondary transition-colors duration-200 font-medium"
+                            >
+                                Login
+                            </Link>
+                        </li>
+                    )}
+                </ul>
+            </nav>
+        </header>
+    );
 }
 
 // Blog Home Component
 function BlogHome() {
-  const [posts, setPosts] = useState([]);
-  const [loading, setLoading] = useState(true);
-  const [error, setError] = useState(null);
-
-  useEffect(() => {
-    async function getTingyun() {
-      setLoading(true);
-      try {
-        const response = await fetch(`${API_BASE}/posts`);
-        if (!response.ok) throw new Error(`Failed to fetch posts: ${response.statusText}`);
-        const postsData = await response.json();
-        setPosts(postsData);
-      } catch (e) {
-        console.error("Error fetching posts:", e);
-        setError("Failed to load posts. Please check if the backend server is running.");
-      } finally {
-        setLoading(false);
-      }
+    const [posts, setPosts] = useState([]);
+    const [loading, setLoading] = useState(true);
+    const [error, setError] = useState(null);
+
+    useEffect(() => {
+        async function getTingyun() {
+            setLoading(true);
+            try {
+                const response = await fetch(`${API_BASE}/posts`);
+                if (!response.ok)
+                    throw new Error(
+                        `Failed to fetch posts: ${response.statusText}`,
+                    );
+                const postsData = await response.json();
+                setPosts(postsData);
+            } catch (e) {
+                console.error("Error fetching posts:", e);
+                setError(
+                    "Failed to load posts. Please check if the backend server is running.",
+                );
+            } finally {
+                setLoading(false);
+            }
+        }
+        getTingyun();
+    }, []);
+
+    if (loading) {
+        return (
+            <div className="min-h-screen theme-bg flex items-center justify-center">
+                <div className="text-center">
+                    <div className="animate-spin rounded-full h-12 w-12 border-b-2 theme-primary mx-auto"></div>
+                    <p className="mt-4 theme-text-secondary">
+                        Loading posts...
+                    </p>
+                </div>
+            </div>
+        );
     }
-    getTingyun();
-  }, []);
 
-  if (loading) {
-    return (
-      <div className="min-h-screen bg-white flex items-center justify-center">
-        <div className="text-center">
-          <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto"></div>
-          <p className="mt-4 text-gray-600">Loading posts...</p>
-        </div>
-      </div>
-    );
-  }
+    if (error) {
+        return (
+            <div className="min-h-screen theme-bg flex items-center justify-center">
+                <div className="text-center">
+                    <div className="bg-red-50 border border-red-200 rounded-lg p-6">
+                        <h2 className="text-red-800 font-semibold mb-2">
+                            Error
+                        </h2>
+                        <p className="text-red-600">{error}</p>
+                    </div>
+                </div>
+            </div>
+        );
+    }
 
-  if (error) {
     return (
-      <div className="min-h-screen bg-white flex items-center justify-center">
-        <div className="text-center">
-          <div className="bg-red-50 border border-red-200 rounded-lg p-6">
-            <h2 className="text-red-800 font-semibold mb-2">Error</h2>
-            <p className="text-red-600">{error}</p>
-          </div>
+        <div className="min-h-screen theme-bg font-sans theme-text antialiased flex flex-col">
+            <div className="max-w-5xl mx-auto w-full flex-grow">
+                <NavHeader />
+
+                <main className="py-10 px-4 sm:px-6 lg:px-8">
+                    <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
+                        {posts.map((post) => (
+                            <div
+                                key={post.slug}
+                                className="group cursor-pointer theme-surface border theme-border rounded-xl hover:border-blue-400 transition-colors duration-200 p-6 flex flex-col justify-between h-full"
+                            >
+                                <div>
+                                    <h2 className="text-xl font-semibold theme-text group-hover:theme-primary transition-colors duration-200 mb-2">
+                                        <Link to={`/posts/${post.slug}`}>
+                                            {post.title}
+                                        </Link>
+                                    </h2>
+                                </div>
+                                <div className="flex-grow mt-4">
+                                    <div className="theme-text-secondary leading-relaxed">
+                                        {post.description}
+                                    </div>
+                                </div>
+                                <div className="mt-4">
+                                    <Link
+                                        to={`/posts/${post.slug}`}
+                                        className="theme-primary font-medium hover:underline focus:outline-none"
+                                    >
+                                        Read more →
+                                    </Link>
+                                </div>
+                            </div>
+                        ))}
+                    </div>
+                </main>
+            </div>
         </div>
-      </div>
     );
-  }
-
-  return (
-    <div className="min-h-screen theme-bg font-sans theme-text antialiased flex flex-col">
-      <div className="max-w-5xl mx-auto w-full flex-grow">
-        <NavHeader />
-
-        <main className="py-10 px-4 sm:px-6 lg:px-8">
-          <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
-            {posts.map((post) => (
-              <div
-                key={post.slug}
-                className="group cursor-pointer theme-surface border theme-border rounded-xl hover:border-blue-400 transition-colors duration-200 p-6 flex flex-col justify-between h-full"
-              >
-                <div>
-                  <h2 className="text-xl font-semibold theme-text group-hover:theme-primary transition-colors duration-200 mb-2">
-                    <Link to={`/posts/${post.slug}`}>
-                      {post.title}
-                    </Link>
-                  </h2>
-                </div>
-                <div className="flex-grow mt-4">
-                  <div className="theme-text-secondary leading-relaxed">
-                    {post.description}
-                  </div>
-                </div>
-                <div className="mt-4">
-                  <Link 
-                    to={`/posts/${post.slug}`}
-                    className="theme-primary font-medium hover:underline focus:outline-none"
-                  >
-                    Read more →
-                  </Link>
-                </div>
-              </div>
-            ))}
-          </div>
-        </main>
-      </div>
-    </div>
-  );
 }
 
 // Post View Component
 function PostView() {
-  const { slug } = useParams();
-  const [post, setPost] = useState(null);
-  const [loading, setLoading] = useState(true);
-  const [error, setError] = useState(null);
-
-  useEffect(() => {
-    async function fetchPost() {
-      try {
-        setLoading(true);
-        const response = await fetch(`${API_BASE}/posts/${slug}`);
-        if (!response.ok) throw new Error('Post not found');
-        const postData = await response.json();
-        setPost(postData);
-        
-        // Update document title
-        document.title = postData.title || 'GoonBlog';
-      } catch (e) {
-        console.error('Error fetching post:', e);
-        setError(e.message);
-      } finally {
-        setLoading(false);
-      }
+    const { slug } = useParams();
+    const [post, setPost] = useState(null);
+    const [loading, setLoading] = useState(true);
+    const [error, setError] = useState(null);
+
+    useEffect(() => {
+        async function fetchPost() {
+            try {
+                setLoading(true);
+                const response = await fetch(`${API_BASE}/posts/${slug}`);
+                if (!response.ok) throw new Error("Post not found");
+                const postData = await response.json();
+                setPost(postData);
+
+                // Update document title
+                document.title = postData.title || "GoonBlog";
+            } catch (e) {
+                console.error("Error fetching post:", e);
+                setError(e.message);
+            } finally {
+                setLoading(false);
+            }
+        }
+
+        if (slug) {
+            fetchPost();
+        }
+    }, [slug]);
+
+    useEffect(() => {
+        // Reset title when component unmounts
+        return () => {
+            document.title = "GoonBlog - A Retard's Thoughts";
+        };
+    }, []);
+
+    if (loading) {
+        return (
+            <div className="min-h-screen theme-bg flex items-center justify-center">
+                <div className="text-center">
+                    <div className="animate-spin rounded-full h-12 w-12 border-b-2 theme-primary mx-auto"></div>
+                    <p className="mt-4 theme-text-secondary">Loading post...</p>
+                </div>
+            </div>
+        );
     }
-    
-    if (slug) {
-      fetchPost();
+
+    if (error || !post) {
+        return (
+            <div className="min-h-screen theme-bg flex items-center justify-center">
+                <div className="text-center">
+                    <h2 className="text-2xl font-bold theme-text mb-2">
+                        Post Not Found
+                    </h2>
+                    <p className="theme-text-secondary mb-4">
+                        {error || "The requested post could not be found."}
+                    </p>
+                    <Link
+                        to="/"
+                        className="theme-primary hover:theme-secondary font-medium"
+                    >
+                        ← Back to Home
+                    </Link>
+                </div>
+            </div>
+        );
     }
-  }, [slug]);
 
-  useEffect(() => {
-    // Reset title when component unmounts
-    return () => {
-      document.title = 'GoonBlog - A Retard\'s Thoughts';
+    const conceiveFoxFromSemen = (rawMarkdown) => {
+        let processedText = rawMarkdown;
+        let tags = null;
+        let imageCredit = null;
+        let imageSrc = null;
+        let imageAlt = null;
+        let customQuestion = null;
+
+        const tagsRegex = /tags: (.*)/;
+        const tagsMatch = processedText.match(tagsRegex);
+        if (tagsMatch) {
+            tags = tagsMatch[1].split(",").map((tag) => tag.trim());
+            processedText = processedText.replace(tagsRegex, "").trim();
+        }
+
+        const imageRegex = /!\[(.*?)\]\((.*?)\)\n_Image credit: (.*?)_/;
+        const imageMatch = processedText.match(imageRegex);
+        if (imageMatch) {
+            imageAlt = imageMatch[1];
+            imageSrc = imageMatch[2];
+            imageCredit = imageMatch[3];
+            processedText = processedText.replace(imageRegex, "").trim();
+        }
+
+        const questionRegex = /\?\?\? "(.*?)"/;
+        const questionMatch = processedText.match(questionRegex);
+        if (questionMatch) {
+            customQuestion = questionMatch[1];
+            processedText = processedText.replace(questionRegex, "").trim();
+        }
+        processedText = processedText
+            .replace(/^title:.*$/m, "")
+            .replace(/^desc:.*$/m, "");
+        return {
+            processedText,
+            tags,
+            imageSrc,
+            imageAlt,
+            imageCredit,
+            customQuestion,
+        };
     };
-  }, []);
 
-  if (loading) {
-    return (
-      <div className="min-h-screen bg-white flex items-center justify-center">
-        <div className="text-center">
-          <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto"></div>
-          <p className="mt-4 text-gray-600">Loading post...</p>
-        </div>
-      </div>
-    );
-  }
+    const { processedText } = conceiveFoxFromSemen(post.content);
+    const htmlContent = md.render(processedText);
+    const sanitizedHtml = DOMPurify.sanitize(htmlContent);
 
-  if (error || !post) {
     return (
-      <div className="min-h-screen bg-white flex items-center justify-center">
-        <div className="text-center">
-          <h2 className="text-2xl font-bold text-gray-900 mb-2">Post Not Found</h2>
-          <p className="text-gray-600 mb-4">{error || 'The requested post could not be found.'}</p>
-          <Link 
-            to="/"
-            className="text-blue-600 hover:text-blue-800 font-medium"
-          >
-            ← Back to Home
-          </Link>
+        <div className="min-h-screen theme-bg font-sans theme-text antialiased flex flex-col">
+            <div className="max-w-5xl mx-auto w-full flex-grow">
+                <NavHeader />
+
+                <main className="py-10 px-4 sm:px-6 lg:px-8">
+                    <div className="w-full">
+                        <div className="theme-surface theme-text border theme-border rounded-xl p-8 md:p-12 lg:p-16">
+                            <Link
+                                to="/"
+                                className="theme-text-secondary hover:theme-text transition-colors duration-200 mb-6 flex items-center"
+                            >
+                                ← Back to Home
+                            </Link>
+
+                            <div className="mb-8">
+                                <h1 className="text-3xl md:text-4xl font-bold theme-text mb-2 leading-tight">
+                                    {post.title}
+                                </h1>
+                                <div className="text-lg italic font-light theme-text-secondary">
+                                    {post.description}
+                                </div>
+                            </div>
+
+                            <hr className="theme-border mb-8" />
+
+                            <div
+                                className="markdown-content theme-text leading-relaxed text-lg"
+                                dangerouslySetInnerHTML={{
+                                    __html: sanitizedHtml,
+                                }}
+                            />
+                        </div>
+                    </div>
+                </main>
+            </div>
         </div>
-      </div>
     );
-  }
-
-  const conceiveFoxFromSemen = (rawMarkdown) => {
-    let processedText = rawMarkdown;
-    let tags = null;
-    let imageCredit = null;
-    let imageSrc = null;
-    let imageAlt = null;
-    let customQuestion = null;
-
-    const tagsRegex = /tags: (.*)/;
-    const tagsMatch = processedText.match(tagsRegex);
-    if (tagsMatch) {
-      tags = tagsMatch[1].split(',').map(tag => tag.trim());
-      processedText = processedText.replace(tagsRegex, '').trim();
-    }
-
-    const imageRegex = /!\[(.*?)\]\((.*?)\)\n_Image credit: (.*?)_/;
-    const imageMatch = processedText.match(imageRegex);
-    if (imageMatch) {
-      imageAlt = imageMatch[1];
-      imageSrc = imageMatch[2];
-      imageCredit = imageMatch[3];
-      processedText = processedText.replace(imageRegex, '').trim();
-    }
-
-    const questionRegex = /\?\?\? "(.*?)"/;
-    const questionMatch = processedText.match(questionRegex);
-    if (questionMatch) {
-      customQuestion = questionMatch[1];
-      processedText = processedText.replace(questionRegex, '').trim();
-    }
-    processedText = processedText.replace(/^title:.*$/m, '').replace(/^desc:.*$/m, '');
-    return {
-      processedText,
-      tags,
-      imageSrc,
-      imageAlt,
-      imageCredit,
-      customQuestion
-    };
-  };
-
-  const { processedText } = conceiveFoxFromSemen(post.content);
-  const htmlContent = md.render(processedText);
-  const sanitizedHtml = DOMPurify.sanitize(htmlContent);
-
-  return (
-    <div className="min-h-screen theme-bg font-sans theme-text antialiased flex flex-col">
-      <div className="max-w-5xl mx-auto w-full flex-grow">
-        <NavHeader />
-
-        <main className="py-10 px-4 sm:px-6 lg:px-8">
-          <div className="w-full">
-            <div className="theme-surface theme-text border theme-border rounded-xl p-8 md:p-12 lg:p-16">
-              <Link
-                to="/"
-                className="theme-text-secondary hover:theme-text transition-colors duration-200 mb-6 flex items-center"
-              >
-                ← Back to Home
-              </Link>
-
-              <div className="mb-8">
-                <h1 className="text-3xl md:text-4xl font-bold theme-text mb-2 leading-tight">
-                  {post.title}
-                </h1>
-                <div className="text-lg italic font-light theme-text-secondary">
-                  {post.description}
-                </div>
-              </div>
-
-              <hr className="theme-border mb-8" />
-
-              <div
-                className="markdown-content theme-text leading-relaxed text-lg"
-                dangerouslySetInnerHTML={{ __html: sanitizedHtml }}
-              />
-            </div>
-          </div>
-        </main>
-      </div>
-    </div>
-  );
 }
 
 function App() {
-  return (
-    <Router>
-      <AuthProvider>
-        <ThemeProvider>
-        <Routes>
-          <Route path="/" element={<BlogHome />} />
-          <Route path="/posts/:slug" element={<PostView />} />
-          <Route path="/login" element={<LoginForm />} />
-          <Route path="/admin" element={
-            <ProtectedRoute>
-              <AdminDashboard />
-            </ProtectedRoute>
-          } />
-          <Route path="/admin/post/new" element={
-            <ProtectedRoute>
-              <PostEditor />
-            </ProtectedRoute>
-          } />
-          <Route path="/admin/post/:slug/edit" element={
-            <ProtectedRoute>
-              <PostEditor />
-            </ProtectedRoute>
-          } />
-          <Route path="/admin/themes" element={
-            <ProtectedRoute>
-              <ThemesManager />
-            </ProtectedRoute>
-          } />
-          <Route path="/admin/themes/new" element={
-            <ProtectedRoute>
-              <ThemeEditor />
-            </ProtectedRoute>
-          } />
-          <Route path="/admin/themes/:themeId/edit" element={
-            <ProtectedRoute>
-              <ThemeEditor />
-            </ProtectedRoute>
-          } />
-        </Routes>
-        </ThemeProvider>
-      </AuthProvider>
-    </Router>
-  );
+    return (
+        <Router>
+            <AuthProvider>
+                <ThemeProvider>
+                    <Routes>
+                        <Route path="/" element={<BlogHome />} />
+                        <Route path="/posts/:slug" element={<PostView />} />
+                        <Route path="/login" element={<LoginForm />} />
+                        <Route
+                            path="/admin"
+                            element={
+                                <ProtectedRoute>
+                                    <AdminDashboard />
+                                </ProtectedRoute>
+                            }
+                        />
+                        <Route
+                            path="/admin/post/new"
+                            element={
+                                <ProtectedRoute>
+                                    <PostEditor />
+                                </ProtectedRoute>
+                            }
+                        />
+                        <Route
+                            path="/admin/post/:slug/edit"
+                            element={
+                                <ProtectedRoute>
+                                    <PostEditor />
+                                </ProtectedRoute>
+                            }
+                        />
+                        <Route
+                            path="/admin/themes"
+                            element={
+                                <ProtectedRoute>
+                                    <ThemesManager />
+                                </ProtectedRoute>
+                            }
+                        />
+                        <Route
+                            path="/admin/themes/new"
+                            element={
+                                <ProtectedRoute>
+                                    <ThemeEditor />
+                                </ProtectedRoute>
+                            }
+                        />
+                        <Route
+                            path="/admin/themes/:themeId/edit"
+                            element={
+                                <ProtectedRoute>
+                                    <ThemeEditor />
+                                </ProtectedRoute>
+                            }
+                        />
+                    </Routes>
+                </ThemeProvider>
+            </AuthProvider>
+        </Router>
+    );
 }
 
 export default App;

+ 333 - 243
src/components/AdminDashboard.jsx

@@ -1,264 +1,354 @@
-import React, { useState, useEffect } from 'react';
-import { Link } from 'react-router-dom';
-import { useTheme } from '../contexts/ThemeContext';
+import React, { useState, useEffect } from "react";
+import { Link } from "react-router-dom";
+import { useTheme } from "../contexts/ThemeContext";
 
-const API_BASE = 'https://goonblog.thevakhovske.eu.org/api';
+const API_BASE = "https://goonblog.thevakhovske.eu.org/api";
 
 function AdminDashboard() {
-  const [posts, setPosts] = useState([]);
-  const [loading, setLoading] = useState(true);
-  const [error, setError] = useState(null);
-  const { currentTheme, allThemes } = useTheme();
+    const [posts, setPosts] = useState([]);
+    const [loading, setLoading] = useState(true);
+    const [error, setError] = useState(null);
+    const { currentTheme, allThemes } = useTheme();
 
-  useEffect(() => {
-    fetchPosts();
-  }, []);
+    useEffect(() => {
+        fetchPosts();
+    }, []);
 
-  const fetchPosts = async () => {
-    try {
-      setLoading(true);
-      const response = await fetch(`${API_BASE}/posts`, {
-        credentials: 'include'
-      });
-      if (!response.ok) throw new Error('Failed to fetch posts');
-      const data = await response.json();
-      setPosts(data);
-    } catch (err) {
-      setError(err.message);
-    } finally {
-      setLoading(false);
+    const fetchPosts = async () => {
+        try {
+            setLoading(true);
+            const response = await fetch(`${API_BASE}/posts`, {
+                credentials: "include",
+            });
+            if (!response.ok) throw new Error("Failed to fetch posts");
+            const data = await response.json();
+            setPosts(data);
+        } catch (err) {
+            setError(err.message);
+        } finally {
+            setLoading(false);
+        }
+    };
+
+    const deletePost = async (slug) => {
+        if (!confirm(`Are you sure you want to delete this post?`)) return;
+
+        try {
+            const response = await fetch(`${API_BASE}/posts/${slug}`, {
+                method: "DELETE",
+                credentials: "include",
+            });
+
+            if (!response.ok) throw new Error("Failed to delete post");
+
+            // Remove from local state
+            setPosts(posts.filter((post) => post.slug !== slug));
+        } catch (err) {
+            setError(err.message);
+        }
+    };
+
+    if (loading) {
+        return (
+            <div className="min-h-screen theme-bg flex items-center justify-center">
+                <div className="text-center">
+                    <div className="animate-spin rounded-full h-12 w-12 border-b-2 theme-primary mx-auto"></div>
+                    <p className="mt-4 theme-text-secondary">
+                        Loading posts...
+                    </p>
+                </div>
+            </div>
+        );
     }
-  };
 
-  const deletePost = async (slug) => {
-    if (!confirm(`Are you sure you want to delete this post?`)) return;
-    
-    try {
-      const response = await fetch(`${API_BASE}/posts/${slug}`, {
-        method: 'DELETE',
-        credentials: 'include'
-      });
-      
-      if (!response.ok) throw new Error('Failed to delete post');
-      
-      // Remove from local state
-      setPosts(posts.filter(post => post.slug !== slug));
-    } catch (err) {
-      setError(err.message);
+    if (error) {
+        return (
+            <div className="min-h-screen theme-bg flex items-center justify-center">
+                <div className="text-center">
+                    <div className="theme-surface border theme-border rounded-lg p-6">
+                        <h2 className="theme-text font-semibold mb-2">Error</h2>
+                        <p className="theme-text-secondary">{error}</p>
+                        <button
+                            onClick={fetchPosts}
+                            className="mt-4 btn-theme-primary text-white px-4 py-2 rounded"
+                        >
+                            Retry
+                        </button>
+                    </div>
+                </div>
+            </div>
+        );
     }
-  };
 
-  if (loading) {
     return (
-      <div className="min-h-screen bg-gray-50 flex items-center justify-center">
-        <div className="text-center">
-          <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto"></div>
-          <p className="mt-4 text-gray-600">Loading posts...</p>
-        </div>
-      </div>
-    );
-  }
+        <div className="min-h-screen theme-bg">
+            <div className="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
+                {/* Header */}
+                <div className="theme-surface shadow rounded-lg mb-6">
+                    <div className="px-6 py-4 border-b theme-border flex justify-between items-center">
+                        <div>
+                            <h1 className="text-2xl font-bold theme-text">
+                                Admin Dashboard
+                            </h1>
+                            <p className="theme-text-secondary">
+                                Manage your blog posts
+                            </p>
+                        </div>
+                        <div className="flex space-x-3">
+                            <Link
+                                to="/"
+                                className="btn-theme-secondary text-white px-4 py-2 rounded-lg transition-colors"
+                            >
+                                View Blog
+                            </Link>
+                            <Link
+                                to="/admin/themes"
+                                className="btn-theme-primary text-white px-4 py-2 rounded-lg transition-colors"
+                            >
+                                Theme Manager
+                            </Link>
+                            <Link
+                                to="/admin/post/new"
+                                className="btn-theme-primary text-white px-4 py-2 rounded-lg transition-colors"
+                            >
+                                New Post
+                            </Link>
+                        </div>
+                    </div>
+                </div>
 
-  if (error) {
-    return (
-      <div className="min-h-screen bg-gray-50 flex items-center justify-center">
-        <div className="text-center">
-          <div className="bg-red-50 border border-red-200 rounded-lg p-6">
-            <h2 className="text-red-800 font-semibold mb-2">Error</h2>
-            <p className="text-red-600">{error}</p>
-            <button 
-              onClick={fetchPosts}
-              className="mt-4 bg-red-600 text-white px-4 py-2 rounded hover:bg-red-700"
-            >
-              Retry
-            </button>
-          </div>
-        </div>
-      </div>
-    );
-  }
+                {/* Stats */}
+                <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-6">
+                    <div className="theme-surface rounded-lg shadow p-6">
+                        <div className="flex items-center">
+                            <div className="p-3 rounded-full theme-bg-primary">
+                                <svg
+                                    className="w-6 h-6 theme-primary"
+                                    fill="currentColor"
+                                    viewBox="0 0 20 20"
+                                >
+                                    <path d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
+                                </svg>
+                            </div>
+                            <div className="ml-4">
+                                <p className="text-sm font-medium theme-text-secondary">
+                                    Total Posts
+                                </p>
+                                <p className="text-2xl font-bold theme-text">
+                                    {posts.length}
+                                </p>
+                            </div>
+                        </div>
+                    </div>
 
-  return (
-    <div className="min-h-screen bg-gray-50">
-      <div className="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
-        {/* Header */}
-        <div className="bg-white shadow rounded-lg mb-6">
-          <div className="px-6 py-4 border-b border-gray-200 flex justify-between items-center">
-            <div>
-              <h1 className="text-2xl font-bold text-gray-900">Admin Dashboard</h1>
-              <p className="text-gray-600">Manage your blog posts</p>
-            </div>
-            <div className="flex space-x-3">
-              <Link
-                to="/"
-                className="bg-gray-600 text-white px-4 py-2 rounded-lg hover:bg-gray-700 transition-colors"
-              >
-                View Blog
-              </Link>
-              <Link
-                to="/admin/themes"
-                className="bg-purple-600 text-white px-4 py-2 rounded-lg hover:bg-purple-700 transition-colors"
-              >
-                Theme Manager
-              </Link>
-              <Link
-                to="/admin/post/new"
-                className="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition-colors"
-              >
-                New Post
-              </Link>
-            </div>
-          </div>
-        </div>
+                    <div className="theme-surface rounded-lg shadow p-6">
+                        <div className="flex items-center">
+                            <div className="p-3 rounded-full theme-bg-primary">
+                                <svg
+                                    className="w-6 h-6 theme-primary"
+                                    fill="currentColor"
+                                    viewBox="0 0 20 20"
+                                >
+                                    <path
+                                        fillRule="evenodd"
+                                        d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
+                                        clipRule="evenodd"
+                                    />
+                                </svg>
+                            </div>
+                            <div className="ml-4">
+                                <p className="text-sm font-medium theme-text-secondary">
+                                    Published
+                                </p>
+                                <p className="text-2xl font-bold theme-text">
+                                    {posts.length}
+                                </p>
+                            </div>
+                        </div>
+                    </div>
 
-        {/* Stats */}
-        <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-6">
-          <div className="bg-white rounded-lg shadow p-6">
-            <div className="flex items-center">
-              <div className="p-3 rounded-full bg-blue-100">
-                <svg className="w-6 h-6 text-blue-600" fill="currentColor" viewBox="0 0 20 20">
-                  <path d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
-                </svg>
-              </div>
-              <div className="ml-4">
-                <p className="text-sm font-medium text-gray-600">Total Posts</p>
-                <p className="text-2xl font-bold text-gray-900">{posts.length}</p>
-              </div>
-            </div>
-          </div>
-          
-          <div className="bg-white rounded-lg shadow p-6">
-            <div className="flex items-center">
-              <div className="p-3 rounded-full bg-green-100">
-                <svg className="w-6 h-6 text-green-600" fill="currentColor" viewBox="0 0 20 20">
-                  <path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clipRule="evenodd" />
-                </svg>
-              </div>
-              <div className="ml-4">
-                <p className="text-sm font-medium text-gray-600">Published</p>
-                <p className="text-2xl font-bold text-gray-900">{posts.length}</p>
-              </div>
-            </div>
-          </div>
+                    <div className="theme-surface rounded-lg shadow p-6">
+                        <div className="flex items-center">
+                            <div className="p-3 rounded-full theme-bg-primary">
+                                <svg
+                                    className="w-6 h-6 theme-primary"
+                                    fill="currentColor"
+                                    viewBox="0 0 20 20"
+                                >
+                                    <path d="M13 6a3 3 0 11-6 0 3 3 0 016 0zM18 8a2 2 0 11-4 0 2 2 0 014 0zM14 15a4 4 0 00-8 0v3h8v-3z" />
+                                </svg>
+                            </div>
+                            <div className="ml-4">
+                                <p className="text-sm font-medium theme-text-secondary">
+                                    Recent
+                                </p>
+                                <p className="text-2xl font-bold theme-text">
+                                    {
+                                        posts.filter(
+                                            (post) =>
+                                                new Date(post.createdAt) >
+                                                new Date(
+                                                    Date.now() -
+                                                        7 * 24 * 60 * 60 * 1000,
+                                                ),
+                                        ).length
+                                    }
+                                </p>
+                            </div>
+                        </div>
+                    </div>
 
-          <div className="bg-white rounded-lg shadow p-6">
-            <div className="flex items-center">
-              <div className="p-3 rounded-full bg-purple-100">
-                <svg className="w-6 h-6 text-purple-600" fill="currentColor" viewBox="0 0 20 20">
-                  <path d="M13 6a3 3 0 11-6 0 3 3 0 016 0zM18 8a2 2 0 11-4 0 2 2 0 014 0zM14 15a4 4 0 00-8 0v3h8v-3z" />
-                </svg>
-              </div>
-              <div className="ml-4">
-                <p className="text-sm font-medium text-gray-600">Recent</p>
-                <p className="text-2xl font-bold text-gray-900">
-                  {posts.filter(post => new Date(post.createdAt) > new Date(Date.now() - 7*24*60*60*1000)).length}
-                </p>
-              </div>
-            </div>
-          </div>
+                    <div className="theme-surface rounded-lg shadow p-6">
+                        <div className="flex items-center">
+                            <div className="p-3 rounded-full theme-bg-primary">
+                                <svg
+                                    className="w-6 h-6 theme-primary"
+                                    fill="currentColor"
+                                    viewBox="0 0 20 20"
+                                >
+                                    <path d="M7 2a1 1 0 011 1v1h3a1 1 0 110 2H9.578a.402.402 0 01-.4.402l-1.13.043a.402.402 0 01-.426-.402H4a1 1 0 110-2h3V3a1 1 0 011-1zM4 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 000-2H4zM10 9a1 1 0 100 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 100-2h-1z" />
+                                </svg>
+                            </div>
+                            <div className="ml-4">
+                                <p className="text-sm font-medium theme-text-secondary">
+                                    Active Theme
+                                </p>
+                                <p className="text-lg font-bold theme-text">
+                                    {currentTheme?.name || "Loading..."}
+                                </p>
+                                <p className="text-xs theme-text-secondary">
+                                    {allThemes.length} themes available
+                                </p>
+                            </div>
+                        </div>
+                    </div>
+                </div>
 
-          <div className="bg-white rounded-lg shadow p-6">
-            <div className="flex items-center">
-              <div className="p-3 rounded-full bg-indigo-100">
-                <svg className="w-6 h-6 text-indigo-600" fill="currentColor" viewBox="0 0 20 20">
-                  <path d="M7 2a1 1 0 011 1v1h3a1 1 0 110 2H9.578a.402.402 0 01-.4.402l-1.13.043a.402.402 0 01-.426-.402H4a1 1 0 110-2h3V3a1 1 0 011-1zM4 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 000-2H4zM10 9a1 1 0 100 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 100-2h-1z" />
-                </svg>
-              </div>
-              <div className="ml-4">
-                <p className="text-sm font-medium text-gray-600">Active Theme</p>
-                <p className="text-lg font-bold text-gray-900">
-                  {currentTheme?.name || 'Loading...'}
-                </p>
-                <p className="text-xs text-gray-500">
-                  {allThemes.length} themes available
-                </p>
-              </div>
-            </div>
-          </div>
-        </div>
+                {/* Posts Table */}
+                <div className="theme-surface shadow rounded-lg">
+                    <div className="px-6 py-4 border-b theme-border">
+                        <h2 className="text-lg font-semibold theme-text">
+                            All Posts
+                        </h2>
+                    </div>
 
-        {/* Posts Table */}
-        <div className="bg-white shadow rounded-lg">
-          <div className="px-6 py-4 border-b border-gray-200">
-            <h2 className="text-lg font-semibold text-gray-900">All Posts</h2>
-          </div>
-          
-          {posts.length === 0 ? (
-            <div className="px-6 py-12 text-center">
-              <svg className="mx-auto h-12 w-12 text-gray-400" stroke="currentColor" fill="none" viewBox="0 0 48 48">
-                <path d="M34 40h10v-4a6 6 0 00-10.712-3.714M34 40H14m20 0v-4a9.971 9.971 0 00-.712-3.714M14 40H4v-4a6 6 0 0110.713-3.714M14 40v-4c0-1.313.253-2.566.713-3.714m0 0A9.971 9.971 0 0118 28a9.971 9.971 0 014 4.286" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round" />
-              </svg>
-              <h3 className="mt-2 text-sm font-medium text-gray-900">No posts</h3>
-              <p className="mt-1 text-sm text-gray-500">Get started by creating your first post.</p>
-              <div className="mt-6">
-                <Link
-                  to="/admin/post/new"
-                  className="inline-flex items-center px-4 py-2 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700"
-                >
-                  Create Post
-                </Link>
-              </div>
-            </div>
-          ) : (
-            <div className="overflow-x-auto">
-              <table className="min-w-full divide-y divide-gray-200">
-                <thead className="bg-gray-50">
-                  <tr>
-                    <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Title</th>
-                    <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Description</th>
-                    <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Created</th>
-                    <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Updated</th>
-                    <th className="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th>
-                  </tr>
-                </thead>
-                <tbody className="bg-white divide-y divide-gray-200">
-                  {posts.map((post) => (
-                    <tr key={post.slug} className="hover:bg-gray-50">
-                      <td className="px-6 py-4 whitespace-nowrap">
-                        <div className="text-sm font-medium text-gray-900">{post.title}</div>
-                        <div className="text-sm text-gray-500">{post.slug}</div>
-                      </td>
-                      <td className="px-6 py-4">
-                        <div className="text-sm text-gray-900 max-w-xs truncate">{post.description}</div>
-                      </td>
-                      <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
-                        {new Date(post.createdAt).toLocaleDateString()}
-                      </td>
-                      <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
-                        {new Date(post.updatedAt).toLocaleDateString()}
-                      </td>
-                      <td className="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
-                        <div className="flex justify-end space-x-2">
-                          <Link
-                            to={`/posts/${post.slug}`}
-                            className="text-blue-600 hover:text-blue-900"
-                          >
-                            View
-                          </Link>
-                          <Link
-                            to={`/admin/post/${post.slug}/edit`}
-                            className="text-indigo-600 hover:text-indigo-900"
-                          >
-                            Edit
-                          </Link>
-                          <button
-                            onClick={() => deletePost(post.slug)}
-                            className="text-red-600 hover:text-red-900"
-                          >
-                            Delete
-                          </button>
+                    {posts.length === 0 ? (
+                        <div className="px-6 py-12 text-center">
+                            <svg
+                                className="mx-auto h-12 w-12 theme-text-secondary"
+                                stroke="currentColor"
+                                fill="none"
+                                viewBox="0 0 48 48"
+                            >
+                                <path
+                                    d="M34 40h10v-4a6 6 0 00-10.712-3.714M34 40H14m20 0v-4a9.971 9.971 0 00-.712-3.714M14 40H4v-4a6 6 0 0110.713-3.714M14 40v-4c0-1.313.253-2.566.713-3.714m0 0A9.971 9.971 0 0118 28a9.971 9.971 0 014 4.286"
+                                    strokeWidth={2}
+                                    strokeLinecap="round"
+                                    strokeLinejoin="round"
+                                />
+                            </svg>
+                            <h3 className="mt-2 text-sm font-medium theme-text">
+                                No posts
+                            </h3>
+                            <p className="mt-1 text-sm theme-text-secondary">
+                                Get started by creating your first post.
+                            </p>
+                            <div className="mt-6">
+                                <Link
+                                    to="/admin/post/new"
+                                    className="inline-flex items-center px-4 py-2 border border-transparent shadow-sm text-sm font-medium rounded-md text-white btn-theme-primary"
+                                >
+                                    Create Post
+                                </Link>
+                            </div>
+                        </div>
+                    ) : (
+                        <div className="overflow-x-auto">
+                            <table className="min-w-full divide-y theme-border">
+                                <thead className="theme-bg">
+                                    <tr>
+                                        <th className="px-6 py-3 text-left text-xs font-medium theme-text-secondary uppercase tracking-wider">
+                                            Title
+                                        </th>
+                                        <th className="px-6 py-3 text-left text-xs font-medium theme-text-secondary uppercase tracking-wider">
+                                            Description
+                                        </th>
+                                        <th className="px-6 py-3 text-left text-xs font-medium theme-text-secondary uppercase tracking-wider">
+                                            Created
+                                        </th>
+                                        <th className="px-6 py-3 text-left text-xs font-medium theme-text-secondary uppercase tracking-wider">
+                                            Updated
+                                        </th>
+                                        <th className="px-6 py-3 text-right text-xs font-medium theme-text-secondary uppercase tracking-wider">
+                                            Actions
+                                        </th>
+                                    </tr>
+                                </thead>
+                                <tbody className="theme-surface divide-y theme-border">
+                                    {posts.map((post) => (
+                                        <tr
+                                            key={post.slug}
+                                            className="hover:theme-bg"
+                                        >
+                                            <td className="px-6 py-4 whitespace-nowrap">
+                                                <div className="text-sm font-medium theme-text">
+                                                    {post.title}
+                                                </div>
+                                                <div className="text-sm theme-text-secondary">
+                                                    {post.slug}
+                                                </div>
+                                            </td>
+                                            <td className="px-6 py-4">
+                                                <div className="text-sm theme-text max-w-xs truncate">
+                                                    {post.description}
+                                                </div>
+                                            </td>
+                                            <td className="px-6 py-4 whitespace-nowrap text-sm theme-text-secondary">
+                                                {new Date(
+                                                    post.createdAt,
+                                                ).toLocaleDateString()}
+                                            </td>
+                                            <td className="px-6 py-4 whitespace-nowrap text-sm theme-text-secondary">
+                                                {new Date(
+                                                    post.updatedAt,
+                                                ).toLocaleDateString()}
+                                            </td>
+                                            <td className="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
+                                                <div className="flex justify-end space-x-2">
+                                                    <Link
+                                                        to={`/posts/${post.slug}`}
+                                                        className="theme-primary"
+                                                    >
+                                                        View
+                                                    </Link>
+                                                    <Link
+                                                        to={`/admin/post/${post.slug}/edit`}
+                                                        className="theme-secondary"
+                                                    >
+                                                        Edit
+                                                    </Link>
+                                                    <button
+                                                        onClick={() =>
+                                                            deletePost(
+                                                                post.slug,
+                                                            )
+                                                        }
+                                                        className="theme-accent"
+                                                    >
+                                                        Delete
+                                                    </button>
+                                                </div>
+                                            </td>
+                                        </tr>
+                                    ))}
+                                </tbody>
+                            </table>
                         </div>
-                      </td>
-                    </tr>
-                  ))}
-                </tbody>
-              </table>
+                    )}
+                </div>
             </div>
-          )}
         </div>
-      </div>
-    </div>
-  );
+    );
 }
 
-export default AdminDashboard;
+export default AdminDashboard;

+ 153 - 138
src/components/LoginForm.jsx

@@ -1,153 +1,168 @@
-import React, { useState } from 'react';
-import { useAuth } from '../contexts/AuthContext';
-import { useNavigate, useLocation } from 'react-router-dom';
+import React, { useState } from "react";
+import { useAuth } from "../contexts/AuthContext";
+import { useNavigate, useLocation } from "react-router-dom";
 
 function LoginForm() {
-  const [formData, setFormData] = useState({
-    username: '',
-    password: ''
-  });
-  const [error, setError] = useState('');
-  const [loading, setLoading] = useState(false);
+    const [formData, setFormData] = useState({
+        username: "",
+        password: "",
+    });
+    const [error, setError] = useState("");
+    const [loading, setLoading] = useState(false);
 
-  const { login } = useAuth();
-  const navigate = useNavigate();
-  const location = useLocation();
+    const { login } = useAuth();
+    const navigate = useNavigate();
+    const location = useLocation();
 
-  // Redirect to intended location after login, or to admin dashboard
-  const redirectTo = location.state?.from?.pathname || '/admin';
+    // Redirect to intended location after login, or to admin dashboard
+    const redirectTo = location.state?.from?.pathname || "/admin";
 
-  const handleChange = (e) => {
-    const { name, value } = e.target;
-    setFormData(prev => ({ ...prev, [name]: value }));
-  };
+    const handleChange = (e) => {
+        const { name, value } = e.target;
+        setFormData((prev) => ({ ...prev, [name]: value }));
+    };
 
-  const handleSubmit = async (e) => {
-    e.preventDefault();
-    setLoading(true);
-    setError('');
+    const handleSubmit = async (e) => {
+        e.preventDefault();
+        setLoading(true);
+        setError("");
 
-    if (!formData.username.trim() || !formData.password) {
-      setError('Please enter both username and password');
-      setLoading(false);
-      return;
-    }
+        if (!formData.username.trim() || !formData.password) {
+            setError("Please enter both username and password");
+            setLoading(false);
+            return;
+        }
 
-    try {
-      const result = await login(formData.username.trim(), formData.password);
-      
-      if (result.success) {
-        navigate(redirectTo, { replace: true });
-      } else {
-        setError(result.error || 'Login failed');
-      }
-    } catch (err) {
-      setError('An unexpected error occurred');
-    } finally {
-      setLoading(false);
-    }
-  };
+        try {
+            const result = await login(
+                formData.username.trim(),
+                formData.password,
+            );
 
-  return (
-    <div className="min-h-screen bg-gray-50 flex items-center justify-center py-12 px-4 sm:px-6 lg:px-8">
-      <div className="max-w-md w-full space-y-8">
-        <div>
-          <h2 className="mt-6 text-center text-3xl font-extrabold text-gray-900">
-            Sign in to Admin Panel
-          </h2>
-          <p className="mt-2 text-center text-sm text-gray-600">
-            Access the <span className="text-blue-600 font-semibold">Goon</span>Blog admin interface
-          </p>
-        </div>
-        
-        <form className="mt-8 space-y-6" onSubmit={handleSubmit}>
-          <div className="rounded-md shadow-sm -space-y-px">
-            <div>
-              <label htmlFor="username" className="sr-only">
-                Username
-              </label>
-              <input
-                id="username"
-                name="username"
-                type="text"
-                autoComplete="username"
-                required
-                value={formData.username}
-                onChange={handleChange}
-                className="relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-t-md focus:outline-none focus:ring-blue-500 focus:border-blue-500 focus:z-10 sm:text-sm"
-                placeholder="Username"
-                disabled={loading}
-              />
-            </div>
-            <div>
-              <label htmlFor="password" className="sr-only">
-                Password
-              </label>
-              <input
-                id="password"
-                name="password"
-                type="password"
-                autoComplete="current-password"
-                required
-                value={formData.password}
-                onChange={handleChange}
-                className="relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-b-md focus:outline-none focus:ring-blue-500 focus:border-blue-500 focus:z-10 sm:text-sm"
-                placeholder="Password"
-                disabled={loading}
-              />
-            </div>
-          </div>
+            if (result.success) {
+                navigate(redirectTo, { replace: true });
+            } else {
+                setError(result.error || "Login failed");
+            }
+        } catch (err) {
+            setError("An unexpected error occurred");
+        } finally {
+            setLoading(false);
+        }
+    };
 
-          {error && (
-            <div className="rounded-md bg-red-50 p-4">
-              <div className="flex">
-                <div className="flex-shrink-0">
-                  <svg className="h-5 w-5 text-red-400" viewBox="0 0 20 20" fill="currentColor">
-                    <path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clipRule="evenodd" />
-                  </svg>
+    return (
+        <div className="min-h-screen theme-bg flex items-center justify-center py-12 px-4 sm:px-6 lg:px-8">
+            <div className="max-w-md w-full space-y-8">
+                <div>
+                    <h2 className="mt-6 text-center text-3xl font-extrabold theme-text">
+                        Sign in to Admin Panel
+                    </h2>
+                    <p className="mt-2 text-center text-sm theme-text-secondary">
+                        Access the{" "}
+                        <span className="theme-primary font-semibold">
+                            Goon
+                        </span>
+                        Blog admin interface
+                    </p>
                 </div>
-                <div className="ml-3">
-                  <h3 className="text-sm font-medium text-red-800">
-                    Error
-                  </h3>
-                  <div className="mt-1 text-sm text-red-700">
-                    {error}
-                  </div>
-                </div>
-              </div>
-            </div>
-          )}
 
-          <div>
-            <button
-              type="submit"
-              disabled={loading}
-              className="group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed"
-            >
-              {loading ? (
-                <div className="flex items-center">
-                  <div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white mr-2"></div>
-                  Signing in...
-                </div>
-              ) : (
-                'Sign in'
-              )}
-            </button>
-          </div>
+                <form className="mt-8 space-y-6" onSubmit={handleSubmit}>
+                    <div className="rounded-md shadow-sm -space-y-px">
+                        <div>
+                            <label htmlFor="username" className="sr-only">
+                                Username
+                            </label>
+                            <input
+                                id="username"
+                                name="username"
+                                type="text"
+                                autoComplete="username"
+                                required
+                                value={formData.username}
+                                onChange={handleChange}
+                                className="relative block w-full px-3 py-2 border theme-border placeholder-gray-500 theme-text rounded-t-md focus:outline-none focus:ring-blue-500 focus:border-blue-500 focus:z-10 sm:text-sm"
+                                placeholder="Username"
+                                disabled={loading}
+                            />
+                        </div>
+                        <div>
+                            <label htmlFor="password" className="sr-only">
+                                Password
+                            </label>
+                            <input
+                                id="password"
+                                name="password"
+                                type="password"
+                                autoComplete="current-password"
+                                required
+                                value={formData.password}
+                                onChange={handleChange}
+                                className="relative block w-full px-3 py-2 border theme-border placeholder-gray-500 theme-text rounded-b-md focus:outline-none focus:ring-blue-500 focus:border-blue-500 focus:z-10 sm:text-sm"
+                                placeholder="Password"
+                                disabled={loading}
+                            />
+                        </div>
+                    </div>
+
+                    {error && (
+                        <div className="rounded-md bg-red-50 p-4">
+                            <div className="flex">
+                                <div className="flex-shrink-0">
+                                    <svg
+                                        className="h-5 w-5 text-red-400"
+                                        viewBox="0 0 20 20"
+                                        fill="currentColor"
+                                    >
+                                        <path
+                                            fillRule="evenodd"
+                                            d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z"
+                                            clipRule="evenodd"
+                                        />
+                                    </svg>
+                                </div>
+                                <div className="ml-3">
+                                    <h3 className="text-sm font-medium text-red-800">
+                                        Error
+                                    </h3>
+                                    <div className="mt-1 text-sm text-red-700">
+                                        {error}
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                    )}
+
+                    <div>
+                        <button
+                            type="submit"
+                            disabled={loading}
+                            className="group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white btn-theme-primary disabled:opacity-50 disabled:cursor-not-allowed"
+                        >
+                            {loading ? (
+                                <div className="flex items-center">
+                                    <div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white mr-2"></div>
+                                    Signing in...
+                                </div>
+                            ) : (
+                                "Sign in"
+                            )}
+                        </button>
+                    </div>
 
-          <div className="text-center">
-            <button
-              type="button"
-              onClick={() => navigate('/')}
-              className="text-sm text-gray-600 hover:text-gray-900"
-            >
-              ← Back to Blog
-            </button>
-          </div>
-        </form>
-      </div>
-    </div>
-  );
+                    <div className="text-center">
+                        <button
+                            type="button"
+                            onClick={() => navigate("/")}
+                            className="text-sm theme-text-secondary hover:theme-text"
+                        >
+                            ← Back to Blog
+                        </button>
+                    </div>
+                </form>
+            </div>
+        </div>
+    );
 }
 
-export default LoginForm;
+export default LoginForm;

+ 336 - 253
src/components/PostEditor.jsx

@@ -1,282 +1,365 @@
-import React, { useState, useEffect } from 'react';
-import { useNavigate, useParams, Link } from 'react-router-dom';
-import MDEditor from '@uiw/react-md-editor';
-import '@uiw/react-md-editor/markdown-editor.css';
-import '@uiw/react-markdown-preview/markdown.css';
-const API_BASE = 'https://goonblog.thevakhovske.eu.org/api';
+import React, { useState, useEffect } from "react";
+import { useNavigate, useParams, Link } from "react-router-dom";
+import MDEditor from "@uiw/react-md-editor";
+import "@uiw/react-md-editor/markdown-editor.css";
+import "@uiw/react-markdown-preview/markdown.css";
+const API_BASE = "https://goonblog.thevakhovske.eu.org/api";
 
 // Note: MDEditor handles its own markdown processing for the editor interface
 // The final blog rendering uses the MarkdownIt instance in App.jsx
 // This separation prevents footnote duplication issues
 
 function PostEditor() {
-  const navigate = useNavigate();
-  const { slug } = useParams();
-  const isEditing = !!slug;
+    const navigate = useNavigate();
+    const { slug } = useParams();
+    const isEditing = !!slug;
 
-  const [formData, setFormData] = useState({
-    title: '',
-    description: '',
-    content: '',
-    tags: ''
-  });
-  
-  const [loading, setLoading] = useState(isEditing);
-  const [saving, setSaving] = useState(false);
-  const [error, setError] = useState(null);
+    const [formData, setFormData] = useState({
+        title: "",
+        description: "",
+        content: "",
+        tags: "",
+    });
 
-  useEffect(() => {
-    if (isEditing) {
-      fetchPost();
-    }
-  }, [slug, isEditing]);
+    const [loading, setLoading] = useState(isEditing);
+    const [saving, setSaving] = useState(false);
+    const [error, setError] = useState(null);
 
-  const fetchPost = async () => {
-    try {
-      setLoading(true);
-      const response = await fetch(`${API_BASE}/posts/${slug}`, {
-        credentials: 'include'
-      });
-      if (!response.ok) throw new Error('Failed to fetch post');
-      
-      const post = await response.json();
-      
-      // Extract content without frontmatter
-      let content = post.content;
-      content = content.replace(/^title:.*$/m, '');
-      content = content.replace(/^desc:.*$/m, '');
-      content = content.replace(/^tags:.*$/m, '');
-      content = content.replace(/^\n+/, ''); // Remove leading newlines
-      
-      setFormData({
-        title: post.title,
-        description: post.description,
-        content: content.trim(),
-        tags: post.tags ? post.tags.join(', ') : ''
-      });
-    } catch (err) {
-      setError(err.message);
-    } finally {
-      setLoading(false);
-    }
-  };
+    useEffect(() => {
+        if (isEditing) {
+            fetchPost();
+        }
+    }, [slug, isEditing]);
 
-  const handleInputChange = (field, value) => {
-    setFormData(prev => ({ ...prev, [field]: value }));
-  };
+    const fetchPost = async () => {
+        try {
+            setLoading(true);
+            const response = await fetch(`${API_BASE}/posts/${slug}`, {
+                credentials: "include",
+            });
+            if (!response.ok) throw new Error("Failed to fetch post");
 
-  const handleSubmit = async (e) => {
-    e.preventDefault();
-    if (!formData.title.trim() || !formData.content.trim()) {
-      setError('Title and content are required');
-      return;
-    }
+            const post = await response.json();
 
-    try {
-      setSaving(true);
-      setError(null);
+            // Extract content without frontmatter
+            let content = post.content;
+            content = content.replace(/^title:.*$/m, "");
+            content = content.replace(/^desc:.*$/m, "");
+            content = content.replace(/^tags:.*$/m, "");
+            content = content.replace(/^\n+/, ""); // Remove leading newlines
 
-      const payload = {
-        title: formData.title.trim(),
-        description: formData.description.trim(),
-        content: formData.content.trim(),
-        tags: formData.tags.split(',').map(tag => tag.trim()).filter(tag => tag)
-      };
+            setFormData({
+                title: post.title,
+                description: post.description,
+                content: content.trim(),
+                tags: post.tags ? post.tags.join(", ") : "",
+            });
+        } catch (err) {
+            setError(err.message);
+        } finally {
+            setLoading(false);
+        }
+    };
 
-      const url = isEditing 
-        ? `${API_BASE}/posts/${slug}`
-        : `${API_BASE}/posts`;
-      
-      const method = isEditing ? 'PUT' : 'POST';
+    const handleInputChange = (field, value) => {
+        setFormData((prev) => ({ ...prev, [field]: value }));
+    };
 
-      const response = await fetch(url, {
-        method,
-        headers: {
-          'Content-Type': 'application/json',
-        },
-        credentials: 'include',
-        body: JSON.stringify(payload),
-      });
+    const handleSubmit = async (e) => {
+        e.preventDefault();
+        if (!formData.title.trim() || !formData.content.trim()) {
+            setError("Title and content are required");
+            return;
+        }
 
-      if (!response.ok) {
-        const errorData = await response.json();
-        throw new Error(errorData.error || 'Failed to save post');
-      }
+        try {
+            setSaving(true);
+            setError(null);
 
-      const savedPost = await response.json();
-      navigate(`/admin`);
-    } catch (err) {
-      setError(err.message);
-    } finally {
-      setSaving(false);
-    }
-  };
+            const payload = {
+                title: formData.title.trim(),
+                description: formData.description.trim(),
+                content: formData.content.trim(),
+                tags: formData.tags
+                    .split(",")
+                    .map((tag) => tag.trim())
+                    .filter((tag) => tag),
+            };
 
+            const url = isEditing
+                ? `${API_BASE}/posts/${slug}`
+                : `${API_BASE}/posts`;
 
-  if (loading) {
-    return (
-      <div className="min-h-screen bg-gray-50 flex items-center justify-center">
-        <div className="text-center">
-          <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto"></div>
-          <p className="mt-4 text-gray-600">Loading post...</p>
-        </div>
-      </div>
-    );
-  }
+            const method = isEditing ? "PUT" : "POST";
 
-  return (
-    <div className="min-h-screen bg-gray-50">
-      <div className="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
-        {/* Header */}
-        <div className="bg-white shadow rounded-lg mb-6">
-          <div className="px-6 py-4 border-b border-gray-200 flex justify-between items-center">
-            <div>
-              <h1 className="text-2xl font-bold text-gray-900">
-                {isEditing ? 'Edit Post' : 'Create New Post'}
-              </h1>
-              <p className="text-gray-600">
-                {isEditing ? 'Update your existing post' : 'Write a new blog post'}
-              </p>
-            </div>
-            <div className="flex space-x-3">
-              <Link
-                to="/admin"
-                className="bg-gray-600 text-white px-4 py-2 rounded-lg hover:bg-gray-700 transition-colors"
-              >
-                Back to Admin
-              </Link>
-            </div>
-          </div>
-        </div>
+            const response = await fetch(url, {
+                method,
+                headers: {
+                    "Content-Type": "application/json",
+                },
+                credentials: "include",
+                body: JSON.stringify(payload),
+            });
 
-        {error && (
-          <div className="mb-6 bg-red-50 border border-red-200 rounded-lg p-4">
-            <div className="flex">
-              <div className="flex-shrink-0">
-                <svg className="h-5 w-5 text-red-400" viewBox="0 0 20 20" fill="currentColor">
-                  <path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clipRule="evenodd" />
-                </svg>
-              </div>
-              <div className="ml-3">
-                <h3 className="text-sm font-medium text-red-800">Error</h3>
-                <p className="mt-1 text-sm text-red-700">{error}</p>
-              </div>
-            </div>
-          </div>
-        )}
+            if (!response.ok) {
+                const errorData = await response.json();
+                throw new Error(errorData.error || "Failed to save post");
+            }
+
+            const savedPost = await response.json();
+            navigate(`/admin`);
+        } catch (err) {
+            setError(err.message);
+        } finally {
+            setSaving(false);
+        }
+    };
 
-        <form onSubmit={handleSubmit} className="space-y-6">
-          {/* Basic Info */}
-          <div className="bg-white shadow rounded-lg">
-            <div className="px-6 py-4 border-b border-gray-200">
-              <h2 className="text-lg font-semibold text-gray-900">Post Information</h2>
+    if (loading) {
+        return (
+            <div className="min-h-screen theme-bg flex items-center justify-center">
+                <div className="text-center">
+                    <div className="animate-spin rounded-full h-12 w-12 border-b-2 theme-primary mx-auto"></div>
+                    <p className="mt-4 theme-text-secondary">Loading post...</p>
+                </div>
             </div>
-            <div className="px-6 py-4 space-y-6">
-              <div>
-                <label htmlFor="title" className="block text-sm font-medium text-gray-700 mb-1">
-                  Title *
-                </label>
-                <input
-                  type="text"
-                  id="title"
-                  required
-                  value={formData.title}
-                  onChange={(e) => handleInputChange('title', e.target.value)}
-                  className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
-                  placeholder="Enter post title..."
-                />
-              </div>
+        );
+    }
 
-              <div>
-                <label htmlFor="description" className="block text-sm font-medium text-gray-700 mb-1">
-                  Description
-                </label>
-                <input
-                  type="text"
-                  id="description"
-                  value={formData.description}
-                  onChange={(e) => handleInputChange('description', e.target.value)}
-                  className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
-                  placeholder="Short description or excerpt..."
-                />
-              </div>
+    return (
+        <div className="min-h-screen theme-bg">
+            <div className="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
+                {/* Header */}
+                <div className="theme-surface shadow rounded-lg mb-6">
+                    <div className="px-6 py-4 border-b theme-border flex justify-between items-center">
+                        <div>
+                            <h1 className="text-2xl font-bold theme-text">
+                                {isEditing ? "Edit Post" : "Create New Post"}
+                            </h1>
+                            <p className="theme-text-secondary">
+                                {isEditing
+                                    ? "Update your existing post"
+                                    : "Write a new blog post"}
+                            </p>
+                        </div>
+                        <div className="flex space-x-3">
+                            <Link
+                                to="/admin"
+                                className="btn-theme-secondary text-white px-4 py-2 rounded-lg transition-colors"
+                            >
+                                Back to Admin
+                            </Link>
+                        </div>
+                    </div>
+                </div>
 
-              <div>
-                <label htmlFor="tags" className="block text-sm font-medium text-gray-700 mb-1">
-                  Tags
-                </label>
-                <input
-                  type="text"
-                  id="tags"
-                  value={formData.tags}
-                  onChange={(e) => handleInputChange('tags', e.target.value)}
-                  className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
-                  placeholder="tag1, tag2, tag3..."
-                />
-                <p className="mt-1 text-sm text-gray-500">Separate tags with commas</p>
-              </div>
-            </div>
-          </div>
+                {error && (
+                    <div className="mb-6 bg-red-50 border border-red-200 rounded-lg p-4">
+                        <div className="flex">
+                            <div className="flex-shrink-0">
+                                <svg
+                                    className="h-5 w-5 text-red-400"
+                                    viewBox="0 0 20 20"
+                                    fill="currentColor"
+                                >
+                                    <path
+                                        fillRule="evenodd"
+                                        d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z"
+                                        clipRule="evenodd"
+                                    />
+                                </svg>
+                            </div>
+                            <div className="ml-3">
+                                <h3 className="text-sm font-medium text-red-800">
+                                    Error
+                                </h3>
+                                <p className="mt-1 text-sm text-red-700">
+                                    {error}
+                                </p>
+                            </div>
+                        </div>
+                    </div>
+                )}
 
-          {/* Content Editor */}
-          <div className="bg-white shadow rounded-lg">
-            <div className="px-6 py-4 border-b border-gray-200">
-              <h2 className="text-lg font-semibold text-gray-900">Content</h2>
-              <p className="text-sm text-gray-600 mt-1">
-                Use the WYSIWYG editor below. Click the tabs to switch between Edit and Preview modes.
-              </p>
-            </div>
-            
-            <div className="px-6 py-4">
-              <div className="border border-gray-300 rounded-lg overflow-hidden">
-                <MDEditor
-                  value={formData.content}
-                  onChange={(value) => handleInputChange('content', value || '')}
-                  height={500}
-                  preview="edit"
-                  hideToolbar={false}
-                  data-color-mode="light"
-                  visibleDragBar={false}
-                  textareaProps={{
-                    placeholder: 'Write your post content in Markdown...',
-                    style: { fontSize: '14px', fontFamily: 'ui-monospace, monospace' },
-                    required: true
-                  }}
-                />
-              </div>
-              <div className="mt-3 text-xs text-gray-500 bg-blue-50 p-3 rounded-lg">
-                <p><strong>WYSIWYG Editor Features:</strong></p>
-                <ul className="list-disc list-inside mt-1 space-y-1">
-                  <li>Toggle between <strong>Edit</strong>, <strong>Preview</strong>, and <strong>Live</strong> modes using the tabs</li>
-                  <li>Use toolbar buttons for quick formatting (bold, italic, headers, lists, etc.)</li>
-                  <li>Standard markdown features: tables, footnotes, emoji (:emoji:), HTML support</li>
-                  <li>Collapsible content: Use <code>&lt;details&gt;&lt;summary&gt;</code> HTML tags</li>
-                  <li>Drag the divider to resize edit/preview panes in Live mode</li>
-                </ul>
-              </div>
-            </div>
-          </div>
+                <form onSubmit={handleSubmit} className="space-y-6">
+                    {/* Basic Info */}
+                    <div className="theme-surface shadow rounded-lg">
+                        <div className="px-6 py-4 border-b theme-border">
+                            <h2 className="text-lg font-semibold theme-text">
+                                Post Information
+                            </h2>
+                        </div>
+                        <div className="px-6 py-4 space-y-6">
+                            <div>
+                                <label
+                                    htmlFor="title"
+                                    className="block text-sm font-medium theme-text mb-1"
+                                >
+                                    Title *
+                                </label>
+                                <input
+                                    type="text"
+                                    id="title"
+                                    required
+                                    value={formData.title}
+                                    onChange={(e) =>
+                                        handleInputChange(
+                                            "title",
+                                            e.target.value,
+                                        )
+                                    }
+                                    className="w-full px-3 py-2 border theme-border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
+                                    placeholder="Enter post title..."
+                                />
+                            </div>
+
+                            <div>
+                                <label
+                                    htmlFor="description"
+                                    className="block text-sm font-medium theme-text mb-1"
+                                >
+                                    Description
+                                </label>
+                                <input
+                                    type="text"
+                                    id="description"
+                                    value={formData.description}
+                                    onChange={(e) =>
+                                        handleInputChange(
+                                            "description",
+                                            e.target.value,
+                                        )
+                                    }
+                                    className="w-full px-3 py-2 border theme-border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
+                                    placeholder="Short description or excerpt..."
+                                />
+                            </div>
 
-          {/* Actions */}
-          <div className="flex justify-end space-x-4">
-            <Link
-              to="/admin"
-              className="px-6 py-2 border border-gray-300 rounded-lg text-gray-700 hover:bg-gray-50"
-            >
-              Cancel
-            </Link>
-            <button
-              type="submit"
-              disabled={saving}
-              className="px-6 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed"
-            >
-              {saving ? 'Saving...' : (isEditing ? 'Update Post' : 'Create Post')}
-            </button>
-          </div>
-        </form>
-      </div>
-    </div>
-  );
+                            <div>
+                                <label
+                                    htmlFor="tags"
+                                    className="block text-sm font-medium theme-text mb-1"
+                                >
+                                    Tags
+                                </label>
+                                <input
+                                    type="text"
+                                    id="tags"
+                                    value={formData.tags}
+                                    onChange={(e) =>
+                                        handleInputChange(
+                                            "tags",
+                                            e.target.value,
+                                        )
+                                    }
+                                    className="w-full px-3 py-2 border theme-border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
+                                    placeholder="tag1, tag2, tag3..."
+                                />
+                                <p className="mt-1 text-sm theme-text-secondary">
+                                    Separate tags with commas
+                                </p>
+                            </div>
+                        </div>
+                    </div>
+
+                    {/* Content Editor */}
+                    <div className="theme-surface shadow rounded-lg">
+                        <div className="px-6 py-4 border-b theme-border">
+                            <h2 className="text-lg font-semibold theme-text">
+                                Content
+                            </h2>
+                            <p className="text-sm theme-text-secondary mt-1">
+                                Use the WYSIWYG editor below. Click the tabs to
+                                switch between Edit and Preview modes.
+                            </p>
+                        </div>
+
+                        <div className="px-6 py-4">
+                            <div className="border theme-border rounded-lg overflow-hidden">
+                                <MDEditor
+                                    value={formData.content}
+                                    onChange={(value) =>
+                                        handleInputChange(
+                                            "content",
+                                            value || "",
+                                        )
+                                    }
+                                    height={500}
+                                    preview="edit"
+                                    hideToolbar={false}
+                                    data-color-mode="light"
+                                    visibleDragBar={false}
+                                    textareaProps={{
+                                        placeholder:
+                                            "Write your post content in Markdown...",
+                                        style: {
+                                            fontSize: "14px",
+                                            fontFamily:
+                                                "ui-monospace, monospace",
+                                        },
+                                        required: true,
+                                    }}
+                                />
+                            </div>
+                            <div className="mt-3 text-xs theme-text-secondary bg-blue-50 p-3 rounded-lg">
+                                <p>
+                                    <strong>WYSIWYG Editor Features:</strong>
+                                </p>
+                                <ul className="list-disc list-inside mt-1 space-y-1">
+                                    <li>
+                                        Toggle between <strong>Edit</strong>,{" "}
+                                        <strong>Preview</strong>, and{" "}
+                                        <strong>Live</strong> modes using the
+                                        tabs
+                                    </li>
+                                    <li>
+                                        Use toolbar buttons for quick formatting
+                                        (bold, italic, headers, lists, etc.)
+                                    </li>
+                                    <li>
+                                        Standard markdown features: tables,
+                                        footnotes, emoji (:emoji:), HTML support
+                                    </li>
+                                    <li>
+                                        Collapsible content: Use{" "}
+                                        <code>
+                                            &lt;details&gt;&lt;summary&gt;
+                                        </code>{" "}
+                                        HTML tags
+                                    </li>
+                                    <li>
+                                        Drag the divider to resize edit/preview
+                                        panes in Live mode
+                                    </li>
+                                </ul>
+                            </div>
+                        </div>
+                    </div>
+
+                    {/* Actions */}
+                    <div className="flex justify-end space-x-4">
+                        <Link
+                            to="/admin"
+                            className="px-6 py-2 border theme-border rounded-lg theme-text hover:theme-bg"
+                        >
+                            Cancel
+                        </Link>
+                        <button
+                            type="submit"
+                            disabled={saving}
+                            className="px-6 py-2 btn-theme-primary text-white rounded-lg disabled:opacity-50 disabled:cursor-not-allowed"
+                        >
+                            {saving
+                                ? "Saving..."
+                                : isEditing
+                                  ? "Update Post"
+                                  : "Create Post"}
+                        </button>
+                    </div>
+                </form>
+            </div>
+        </div>
+    );
 }
 
-export default PostEditor;
+export default PostEditor;

+ 60 - 46
src/components/ProtectedRoute.jsx

@@ -1,55 +1,69 @@
-import React from 'react';
-import { Navigate, useLocation } from 'react-router-dom';
-import { useAuth } from '../contexts/AuthContext';
+import React from "react";
+import { Navigate, useLocation } from "react-router-dom";
+import { useAuth } from "../contexts/AuthContext";
 
 function ProtectedRoute({ children }) {
-  const { isAuthenticated, isAdmin, loading } = useAuth();
-  const location = useLocation();
+    const { isAuthenticated, isAdmin, loading } = useAuth();
+    const location = useLocation();
 
-  // Show loading spinner while checking authentication
-  if (loading) {
-    return (
-      <div className="min-h-screen bg-gray-50 flex items-center justify-center">
-        <div className="text-center">
-          <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto"></div>
-          <p className="mt-4 text-gray-600">Loading...</p>
-        </div>
-      </div>
-    );
-  }
+    // Show loading spinner while checking authentication
+    if (loading) {
+        return (
+            <div className="min-h-screen theme-bg flex items-center justify-center">
+                <div className="text-center">
+                    <div className="animate-spin rounded-full h-12 w-12 border-b-2 theme-primary mx-auto"></div>
+                    <p className="mt-4 theme-text-secondary">Loading...</p>
+                </div>
+            </div>
+        );
+    }
 
-  // If not authenticated, redirect to login with return path
-  if (!isAuthenticated) {
-    return <Navigate to="/login" state={{ from: location }} replace />;
-  }
+    // If not authenticated, redirect to login with return path
+    if (!isAuthenticated) {
+        return <Navigate to="/login" state={{ from: location }} replace />;
+    }
 
-  // If authenticated but not admin, show access denied
-  if (!isAdmin) {
-    return (
-      <div className="min-h-screen bg-gray-50 flex items-center justify-center">
-        <div className="text-center">
-          <div className="bg-red-50 border border-red-200 rounded-lg p-8">
-            <div className="flex justify-center mb-4">
-              <svg className="h-12 w-12 text-red-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
-                <path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.732-.833-2.5 0L4.268 18.5C3.498 20.333 4.458 22 6.002 22z" />
-              </svg>
+    // If authenticated but not admin, show access denied
+    if (!isAdmin) {
+        return (
+            <div className="min-h-screen theme-bg flex items-center justify-center">
+                <div className="text-center">
+                    <div className="theme-surface border theme-border rounded-lg p-8">
+                        <div className="flex justify-center mb-4">
+                            <svg
+                                className="h-12 w-12 text-red-400"
+                                fill="none"
+                                stroke="currentColor"
+                                viewBox="0 0 24 24"
+                            >
+                                <path
+                                    strokeLinecap="round"
+                                    strokeLinejoin="round"
+                                    strokeWidth="2"
+                                    d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.732-.833-2.5 0L4.268 18.5C3.498 20.333 4.458 22 6.002 22z"
+                                />
+                            </svg>
+                        </div>
+                        <h2 className="text-xl font-semibold text-red-800 mb-2">
+                            Access Denied
+                        </h2>
+                        <p className="text-red-600 mb-4">
+                            You don't have permission to access this page.
+                        </p>
+                        <button
+                            onClick={() => (window.location.href = "/")}
+                            className="btn-theme-primary text-white px-4 py-2 rounded"
+                        >
+                            Return to Blog
+                        </button>
+                    </div>
+                </div>
             </div>
-            <h2 className="text-xl font-semibold text-red-800 mb-2">Access Denied</h2>
-            <p className="text-red-600 mb-4">You don't have permission to access this page.</p>
-            <button
-              onClick={() => window.location.href = '/'}
-              className="bg-red-600 text-white px-4 py-2 rounded hover:bg-red-700"
-            >
-              Return to Blog
-            </button>
-          </div>
-        </div>
-      </div>
-    );
-  }
+        );
+    }
 
-  // If authenticated and admin, render the protected component
-  return children;
+    // If authenticated and admin, render the protected component
+    return children;
 }
 
-export default ProtectedRoute;
+export default ProtectedRoute;

+ 553 - 424
src/components/ThemeEditor.jsx

@@ -1,441 +1,570 @@
-import React, { useState, useEffect } from 'react';
-import { Link, useParams, useNavigate } from 'react-router-dom';
-import { useTheme } from '../contexts/ThemeContext';
+import React, { useState, useEffect } from "react";
+import { Link, useParams, useNavigate } from "react-router-dom";
+import { useTheme } from "../contexts/ThemeContext";
 
 function ThemeEditor() {
-  const { themeId } = useParams();
-  const navigate = useNavigate();
-  const isEditing = !!themeId;
-  
-  const { 
-    currentTheme, 
-    allThemes, 
-    createTheme, 
-    updateTheme, 
-    setActiveTheme,
-    exportTheme,
-    importTheme 
-  } = useTheme();
-
-  const [formData, setFormData] = useState({
-    name: '',
-    id: '',
-    colors: {
-      primary: '#3b82f6',
-      primaryHover: '#2563eb',
-      secondary: '#6b7280',
-      background: '#ffffff',
-      surface: '#f9fafb',
-      text: '#1f2937',
-      textSecondary: '#6b7280',
-      border: '#e5e7eb',
-      accent: '#10b981',
-      error: '#ef4444',
-      warning: '#f59e0b',
-      success: '#10b981'
-    },
-    typography: {
-      fontFamily: 'Inter, system-ui, sans-serif',
-      headingFontFamily: 'Inter, system-ui, sans-serif'
-    },
-    customCSS: ''
-  });
-
-  const [saving, setSaving] = useState(false);
-  const [error, setError] = useState(null);
-  const [previewMode, setPreviewMode] = useState(false);
-
-  useEffect(() => {
-    if (isEditing && themeId) {
-      const theme = allThemes.find(t => t.id === themeId);
-      if (theme) {
-        setFormData({
-          name: theme.name,
-          id: theme.id,
-          colors: theme.colors,
-          typography: theme.typography,
-          customCSS: theme.customCSS || ''
-        });
-      }
-    }
-  }, [isEditing, themeId, allThemes]);
-
-  const handleInputChange = (field, value, section = null) => {
-    if (section) {
-      setFormData(prev => ({
-        ...prev,
-        [section]: {
-          ...prev[section],
-          [field]: value
+    const { themeId } = useParams();
+    const navigate = useNavigate();
+    const isEditing = !!themeId;
+
+    const {
+        currentTheme,
+        allThemes,
+        createTheme,
+        updateTheme,
+        setActiveTheme,
+        exportTheme,
+        importTheme,
+    } = useTheme();
+
+    const [formData, setFormData] = useState({
+        name: "",
+        id: "",
+        colors: {
+            primary: "#3b82f6",
+            primaryHover: "#2563eb",
+            secondary: "#6b7280",
+            background: "#ffffff",
+            surface: "#f9fafb",
+            text: "#1f2937",
+            textSecondary: "#6b7280",
+            border: "#e5e7eb",
+            accent: "#10b981",
+            error: "#ef4444",
+            warning: "#f59e0b",
+            success: "#10b981",
+        },
+        typography: {
+            fontFamily: "Inter, system-ui, sans-serif",
+            headingFontFamily: "Inter, system-ui, sans-serif",
+        },
+        customCSS: "",
+    });
+
+    const [saving, setSaving] = useState(false);
+    const [error, setError] = useState(null);
+    const [previewMode, setPreviewMode] = useState(false);
+
+    useEffect(() => {
+        if (isEditing && themeId) {
+            const theme = allThemes.find((t) => t.id === themeId);
+            if (theme) {
+                setFormData({
+                    name: theme.name,
+                    id: theme.id,
+                    colors: theme.colors,
+                    typography: theme.typography,
+                    customCSS: theme.customCSS || "",
+                });
+            }
         }
-      }));
-    } else {
-      setFormData(prev => ({ ...prev, [field]: value }));
-    }
-  };
-
-  const handleSubmit = async (e) => {
-    e.preventDefault();
-    
-    if (!formData.name.trim()) {
-      setError('Theme name is required');
-      return;
-    }
-
-    if (!isEditing && !formData.id.trim()) {
-      setError('Theme ID is required');
-      return;
-    }
-
-    setSaving(true);
-    setError(null);
-
-    try {
-      let result;
-      
-      if (isEditing) {
-        result = await updateTheme(themeId, formData);
-      } else {
-        result = await createTheme(formData);
-      }
-
-      if (result.success) {
-        navigate('/admin/themes');
-      } else {
-        setError(result.error);
-      }
-    } catch (err) {
-      setError('Failed to save theme');
-    } finally {
-      setSaving(false);
-    }
-  };
-
-  const handlePreview = () => {
-    setPreviewMode(!previewMode);
-    if (!previewMode) {
-      // Apply preview theme
-      const root = document.documentElement;
-      Object.entries(formData.colors).forEach(([key, value]) => {
-        const cssVar = `--color-${key.replace(/([A-Z])/g, '-$1').toLowerCase()}`;
-        root.style.setProperty(cssVar, value);
-      });
-    } else {
-      // Restore current theme
-      if (currentTheme) {
-        Object.entries(currentTheme.colors).forEach(([key, value]) => {
-          const cssVar = `--color-${key.replace(/([A-Z])/g, '-$1').toLowerCase()}`;
-          root.style.setProperty(cssVar, value);
-        });
-      }
-    }
-  };
-
-  const handleImport = () => {
-    const input = document.createElement('input');
-    input.type = 'file';
-    input.accept = '.json';
-    input.onchange = async (e) => {
-      const file = e.target.files[0];
-      if (!file) return;
-
-      try {
-        const text = await file.text();
-        const themeData = JSON.parse(text);
-        
-        const result = await importTheme(themeData);
-        if (result.success) {
-          navigate('/admin/themes');
+    }, [isEditing, themeId, allThemes]);
+
+    const handleInputChange = (field, value, section = null) => {
+        if (section) {
+            setFormData((prev) => ({
+                ...prev,
+                [section]: {
+                    ...prev[section],
+                    [field]: value,
+                },
+            }));
         } else {
-          setError(result.error);
+            setFormData((prev) => ({ ...prev, [field]: value }));
         }
-      } catch (err) {
-        setError('Invalid theme file');
-      }
     };
-    input.click();
-  };
-
-  const ColorInput = ({ label, value, onChange, description }) => (
-    <div className="space-y-2">
-      <label className="block text-sm font-medium text-gray-700">
-        {label}
-        {description && <span className="text-xs text-gray-500 block">{description}</span>}
-      </label>
-      <div className="flex items-center space-x-3">
-        <input
-          type="color"
-          value={value}
-          onChange={(e) => onChange(e.target.value)}
-          className="h-10 w-16 rounded border border-gray-300 cursor-pointer"
-        />
-        <input
-          type="text"
-          value={value}
-          onChange={(e) => onChange(e.target.value)}
-          className="flex-1 px-3 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-sm font-mono"
-          placeholder="#000000"
-        />
-      </div>
-    </div>
-  );
-
-  return (
-    <div className="min-h-screen bg-gray-50">
-      <div className="max-w-4xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
-        {/* Header */}
-        <div className="bg-white shadow rounded-lg mb-6">
-          <div className="px-6 py-4 border-b border-gray-200 flex justify-between items-center">
-            <div>
-              <h1 className="text-2xl font-bold text-gray-900">
-                {isEditing ? 'Edit Theme' : 'Create New Theme'}
-              </h1>
-              <p className="text-gray-600">
-                {isEditing ? 'Modify your existing theme' : 'Design a custom theme for your blog'}
-              </p>
-            </div>
-            <div className="flex space-x-3">
-              <button
-                type="button"
-                onClick={handlePreview}
-                className={`px-4 py-2 rounded-lg font-medium ${
-                  previewMode 
-                    ? 'bg-orange-100 text-orange-700 hover:bg-orange-200' 
-                    : 'bg-purple-100 text-purple-700 hover:bg-purple-200'
-                }`}
-              >
-                {previewMode ? 'Stop Preview' : 'Preview'}
-              </button>
-              <button
-                type="button"
-                onClick={handleImport}
-                className="bg-green-600 text-white px-4 py-2 rounded-lg hover:bg-green-700 transition-colors"
-              >
-                Import Theme
-              </button>
-              <Link
-                to="/admin/themes"
-                className="bg-gray-600 text-white px-4 py-2 rounded-lg hover:bg-gray-700 transition-colors"
-              >
-                Back to Themes
-              </Link>
-            </div>
-          </div>
-        </div>
 
-        {previewMode && (
-          <div className="bg-purple-50 border border-purple-200 rounded-lg p-4 mb-6">
-            <div className="flex items-center">
-              <svg className="h-5 w-5 text-purple-400" fill="currentColor" viewBox="0 0 20 20">
-                <path fillRule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clipRule="evenodd" />
-              </svg>
-              <p className="ml-3 text-sm text-purple-700">
-                <strong>Preview Mode Active:</strong> You're seeing a live preview of your theme changes.
-              </p>
-            </div>
-          </div>
-        )}
-
-        {error && (
-          <div className="mb-6 bg-red-50 border border-red-200 rounded-lg p-4">
-            <div className="flex">
-              <svg className="h-5 w-5 text-red-400" viewBox="0 0 20 20" fill="currentColor">
-                <path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clipRule="evenodd" />
-              </svg>
-              <p className="ml-3 text-sm text-red-700">{error}</p>
-            </div>
-          </div>
-        )}
-
-        <form onSubmit={handleSubmit} className="space-y-6">
-          {/* Basic Info */}
-          <div className="bg-white shadow rounded-lg">
-            <div className="px-6 py-4 border-b border-gray-200">
-              <h2 className="text-lg font-semibold text-gray-900">Basic Information</h2>
-            </div>
-            <div className="px-6 py-4 space-y-6">
-              <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
-                <div>
-                  <label htmlFor="name" className="block text-sm font-medium text-gray-700 mb-1">
-                    Theme Name *
-                  </label>
-                  <input
-                    type="text"
-                    id="name"
-                    required
-                    value={formData.name}
-                    onChange={(e) => handleInputChange('name', e.target.value)}
-                    className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
-                    placeholder="My Custom Theme"
-                  />
-                </div>
-                
-                <div>
-                  <label htmlFor="id" className="block text-sm font-medium text-gray-700 mb-1">
-                    Theme ID {!isEditing && '*'}
-                  </label>
-                  <input
-                    type="text"
-                    id="id"
-                    required={!isEditing}
-                    disabled={isEditing}
-                    value={formData.id}
-                    onChange={(e) => handleInputChange('id', e.target.value)}
-                    className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 disabled:bg-gray-100"
-                    placeholder="my-custom-theme"
-                  />
-                  <p className="mt-1 text-xs text-gray-500">
-                    {isEditing ? 'ID cannot be changed after creation' : 'Unique identifier (lowercase, hyphens allowed)'}
-                  </p>
-                </div>
-              </div>
-            </div>
-          </div>
+    const handleSubmit = async (e) => {
+        e.preventDefault();
 
-          {/* Colors */}
-          <div className="bg-white shadow rounded-lg">
-            <div className="px-6 py-4 border-b border-gray-200">
-              <h2 className="text-lg font-semibold text-gray-900">Colors</h2>
-              <p className="text-sm text-gray-600">Customize the color palette for your theme</p>
-            </div>
-            <div className="px-6 py-4">
-              <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
-                <ColorInput
-                  label="Primary Color"
-                  description="Main brand color for buttons and links"
-                  value={formData.colors.primary}
-                  onChange={(value) => handleInputChange('primary', value, 'colors')}
-                />
-                
-                <ColorInput
-                  label="Primary Hover"
-                  description="Hover state for primary elements"
-                  value={formData.colors.primaryHover}
-                  onChange={(value) => handleInputChange('primaryHover', value, 'colors')}
-                />
-                
-                <ColorInput
-                  label="Background"
-                  description="Main page background color"
-                  value={formData.colors.background}
-                  onChange={(value) => handleInputChange('background', value, 'colors')}
-                />
-                
-                <ColorInput
-                  label="Surface"
-                  description="Cards and panel backgrounds"
-                  value={formData.colors.surface}
-                  onChange={(value) => handleInputChange('surface', value, 'colors')}
-                />
-                
-                <ColorInput
-                  label="Text Color"
-                  description="Primary text color"
-                  value={formData.colors.text}
-                  onChange={(value) => handleInputChange('text', value, 'colors')}
-                />
-                
-                <ColorInput
-                  label="Secondary Text"
-                  description="Muted text and descriptions"
-                  value={formData.colors.textSecondary}
-                  onChange={(value) => handleInputChange('textSecondary', value, 'colors')}
-                />
-                
-                <ColorInput
-                  label="Border Color"
-                  description="Borders and dividers"
-                  value={formData.colors.border}
-                  onChange={(value) => handleInputChange('border', value, 'colors')}
+        if (!formData.name.trim()) {
+            setError("Theme name is required");
+            return;
+        }
+
+        if (!isEditing && !formData.id.trim()) {
+            setError("Theme ID is required");
+            return;
+        }
+
+        setSaving(true);
+        setError(null);
+
+        try {
+            let result;
+
+            if (isEditing) {
+                result = await updateTheme(themeId, formData);
+            } else {
+                result = await createTheme(formData);
+            }
+
+            if (result.success) {
+                navigate("/admin/themes");
+            } else {
+                setError(result.error);
+            }
+        } catch (err) {
+            setError("Failed to save theme");
+        } finally {
+            setSaving(false);
+        }
+    };
+
+    const handlePreview = () => {
+        setPreviewMode(!previewMode);
+        if (!previewMode) {
+            // Apply preview theme
+            const root = document.documentElement;
+            Object.entries(formData.colors).forEach(([key, value]) => {
+                const cssVar = `--color-${key.replace(/([A-Z])/g, "-$1").toLowerCase()}`;
+                root.style.setProperty(cssVar, value);
+            });
+        } else {
+            // Restore current theme
+            if (currentTheme) {
+                Object.entries(currentTheme.colors).forEach(([key, value]) => {
+                    const cssVar = `--color-${key.replace(/([A-Z])/g, "-$1").toLowerCase()}`;
+                    root.style.setProperty(cssVar, value);
+                });
+            }
+        }
+    };
+
+    const handleImport = () => {
+        const input = document.createElement("input");
+        input.type = "file";
+        input.accept = ".json";
+        input.onchange = async (e) => {
+            const file = e.target.files[0];
+            if (!file) return;
+
+            try {
+                const text = await file.text();
+                const themeData = JSON.parse(text);
+
+                const result = await importTheme(themeData);
+                if (result.success) {
+                    navigate("/admin/themes");
+                } else {
+                    setError(result.error);
+                }
+            } catch (err) {
+                setError("Invalid theme file");
+            }
+        };
+        input.click();
+    };
+
+    const ColorInput = ({ label, value, onChange, description }) => (
+        <div className="space-y-2">
+            <label className="block text-sm font-medium theme-text">
+                {label}
+                {description && (
+                    <span className="text-xs theme-text-secondary block">
+                        {description}
+                    </span>
+                )}
+            </label>
+            <div className="flex items-center space-x-3">
+                <input
+                    type="color"
+                    value={value}
+                    onChange={(e) => onChange(e.target.value)}
+                    className="h-10 w-16 rounded border theme-border cursor-pointer"
                 />
-                
-                <ColorInput
-                  label="Accent Color"
-                  description="Accent elements and highlights"
-                  value={formData.colors.accent}
-                  onChange={(value) => handleInputChange('accent', value, 'colors')}
+                <input
+                    type="text"
+                    value={value}
+                    onChange={(e) => onChange(e.target.value)}
+                    className="flex-1 px-3 py-2 border theme-border rounded-md focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-sm font-mono"
+                    placeholder="#000000"
                 />
-              </div>
             </div>
-          </div>
+        </div>
+    );
 
-          {/* Typography */}
-          <div className="bg-white shadow rounded-lg">
-            <div className="px-6 py-4 border-b border-gray-200">
-              <h2 className="text-lg font-semibold text-gray-900">Typography</h2>
-              <p className="text-sm text-gray-600">Font settings for your theme</p>
-            </div>
-            <div className="px-6 py-4 space-y-6">
-              <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
-                <div>
-                  <label className="block text-sm font-medium text-gray-700 mb-1">
-                    Body Font Family
-                  </label>
-                  <input
-                    type="text"
-                    value={formData.typography.fontFamily}
-                    onChange={(e) => handleInputChange('fontFamily', e.target.value, 'typography')}
-                    className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 font-mono text-sm"
-                    placeholder="Inter, system-ui, sans-serif"
-                  />
+    return (
+        <div className="min-h-screen theme-bg">
+            <div className="max-w-4xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
+                {/* Header */}
+                <div className="theme-surface shadow rounded-lg mb-6">
+                    <div className="px-6 py-4 border-b theme-border flex justify-between items-center">
+                        <div>
+                            <h1 className="text-2xl font-bold theme-text">
+                                {isEditing ? "Edit Theme" : "Create New Theme"}
+                            </h1>
+                            <p className="theme-text-secondary">
+                                {isEditing
+                                    ? "Modify your existing theme"
+                                    : "Design a custom theme for your blog"}
+                            </p>
+                        </div>
+                        <div className="flex space-x-3">
+                            <button
+                                type="button"
+                                onClick={handlePreview}
+                                className={`px-4 py-2 rounded-lg font-medium ${
+                                    previewMode
+                                        ? "btn-theme-secondary"
+                                        : "btn-theme-primary"
+                                }`}
+                            >
+                                {previewMode ? "Stop Preview" : "Preview"}
+                            </button>
+                            <button
+                                type="button"
+                                onClick={handleImport}
+                                className="btn-theme-primary text-white px-4 py-2 rounded-lg transition-colors"
+                            >
+                                Import Theme
+                            </button>
+                            <Link
+                                to="/admin/themes"
+                                className="btn-theme-secondary text-white px-4 py-2 rounded-lg transition-colors"
+                            >
+                                Back to Themes
+                            </Link>
+                        </div>
+                    </div>
                 </div>
-                
-                <div>
-                  <label className="block text-sm font-medium text-gray-700 mb-1">
-                    Heading Font Family
-                  </label>
-                  <input
-                    type="text"
-                    value={formData.typography.headingFontFamily}
-                    onChange={(e) => handleInputChange('headingFontFamily', e.target.value, 'typography')}
-                    className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 font-mono text-sm"
-                    placeholder="Inter, system-ui, sans-serif"
-                  />
-                </div>
-              </div>
-            </div>
-          </div>
 
-          {/* Custom CSS */}
-          <div className="bg-white shadow rounded-lg">
-            <div className="px-6 py-4 border-b border-gray-200">
-              <h2 className="text-lg font-semibold text-gray-900">Custom CSS</h2>
-              <p className="text-sm text-gray-600">Add custom CSS for advanced styling</p>
-            </div>
-            <div className="px-6 py-4">
-              <textarea
-                rows={10}
-                value={formData.customCSS}
-                onChange={(e) => handleInputChange('customCSS', e.target.value)}
-                className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 font-mono text-sm"
-                placeholder="/* Custom CSS rules */&#10;.my-custom-class {&#10;  /* your styles */&#10;}"
-              />
-              <p className="mt-2 text-xs text-gray-500">
-                Use CSS custom properties like <code>var(--color-primary)</code> to reference theme colors
-              </p>
+                {previewMode && (
+                    <div className="theme-surface border theme-border rounded-lg p-4 mb-6">
+                        <div className="flex items-center">
+                            <svg
+                                className="h-5 w-5 theme-primary"
+                                fill="currentColor"
+                                viewBox="0 0 20 20"
+                            >
+                                <path
+                                    fillRule="evenodd"
+                                    d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z"
+                                    clipRule="evenodd"
+                                />
+                            </svg>
+                            <p className="ml-3 text-sm theme-text">
+                                <strong>Preview Mode Active:</strong> You're
+                                seeing a live preview of your theme changes.
+                            </p>
+                        </div>
+                    </div>
+                )}
+
+                {error && (
+                    <div className="mb-6 bg-red-50 border border-red-200 rounded-lg p-4">
+                        <div className="flex">
+                            <svg
+                                className="h-5 w-5 text-red-400"
+                                viewBox="0 0 20 20"
+                                fill="currentColor"
+                            >
+                                <path
+                                    fillRule="evenodd"
+                                    d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z"
+                                    clipRule="evenodd"
+                                />
+                            </svg>
+                            <p className="ml-3 text-sm text-red-700">{error}</p>
+                        </div>
+                    </div>
+                )}
+
+                <form onSubmit={handleSubmit} className="space-y-6">
+                    {/* Basic Info */}
+                    <div className="theme-surface shadow rounded-lg">
+                        <div className="px-6 py-4 border-b theme-border">
+                            <h2 className="text-lg font-semibold theme-text">
+                                Basic Information
+                            </h2>
+                        </div>
+                        <div className="px-6 py-4 space-y-6">
+                            <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
+                                <div>
+                                    <label
+                                        htmlFor="name"
+                                        className="block text-sm font-medium theme-text mb-1"
+                                    >
+                                        Theme Name *
+                                    </label>
+                                    <input
+                                        type="text"
+                                        id="name"
+                                        required
+                                        value={formData.name}
+                                        onChange={(e) =>
+                                            handleInputChange(
+                                                "name",
+                                                e.target.value,
+                                            )
+                                        }
+                                        className="w-full px-3 py-2 border theme-border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
+                                        placeholder="My Custom Theme"
+                                    />
+                                </div>
+
+                                <div>
+                                    <label
+                                        htmlFor="id"
+                                        className="block text-sm font-medium theme-text mb-1"
+                                    >
+                                        Theme ID {!isEditing && "*"}
+                                    </label>
+                                    <input
+                                        type="text"
+                                        id="id"
+                                        required={!isEditing}
+                                        disabled={isEditing}
+                                        value={formData.id}
+                                        onChange={(e) =>
+                                            handleInputChange(
+                                                "id",
+                                                e.target.value,
+                                            )
+                                        }
+                                        className="w-full px-3 py-2 border theme-border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 disabled:bg-gray-100"
+                                        placeholder="my-custom-theme"
+                                    />
+                                    <p className="mt-1 text-xs theme-text-secondary">
+                                        {isEditing
+                                            ? "ID cannot be changed after creation"
+                                            : "Unique identifier (lowercase, hyphens allowed)"}
+                                    </p>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+
+                    {/* Colors */}
+                    <div className="theme-surface shadow rounded-lg">
+                        <div className="px-6 py-4 border-b theme-border">
+                            <h2 className="text-lg font-semibold theme-text">
+                                Colors
+                            </h2>
+                            <p className="theme-text-secondary">
+                                Customize the color palette for your theme
+                            </p>
+                        </div>
+                        <div className="px-6 py-4">
+                            <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
+                                <ColorInput
+                                    label="Primary Color"
+                                    description="Main brand color for buttons and links"
+                                    value={formData.colors.primary}
+                                    onChange={(value) =>
+                                        handleInputChange(
+                                            "primary",
+                                            value,
+                                            "colors",
+                                        )
+                                    }
+                                />
+
+                                <ColorInput
+                                    label="Primary Hover"
+                                    description="Hover state for primary elements"
+                                    value={formData.colors.primaryHover}
+                                    onChange={(value) =>
+                                        handleInputChange(
+                                            "primaryHover",
+                                            value,
+                                            "colors",
+                                        )
+                                    }
+                                />
+
+                                <ColorInput
+                                    label="Background"
+                                    description="Main page background color"
+                                    value={formData.colors.background}
+                                    onChange={(value) =>
+                                        handleInputChange(
+                                            "background",
+                                            value,
+                                            "colors",
+                                        )
+                                    }
+                                />
+
+                                <ColorInput
+                                    label="Surface"
+                                    description="Cards and panel backgrounds"
+                                    value={formData.colors.surface}
+                                    onChange={(value) =>
+                                        handleInputChange(
+                                            "surface",
+                                            value,
+                                            "colors",
+                                        )
+                                    }
+                                />
+
+                                <ColorInput
+                                    label="Text Color"
+                                    description="Primary text color"
+                                    value={formData.colors.text}
+                                    onChange={(value) =>
+                                        handleInputChange(
+                                            "text",
+                                            value,
+                                            "colors",
+                                        )
+                                    }
+                                />
+
+                                <ColorInput
+                                    label="Secondary Text"
+                                    description="Muted text and descriptions"
+                                    value={formData.colors.textSecondary}
+                                    onChange={(value) =>
+                                        handleInputChange(
+                                            "textSecondary",
+                                            value,
+                                            "colors",
+                                        )
+                                    }
+                                />
+
+                                <ColorInput
+                                    label="Border Color"
+                                    description="Borders and dividers"
+                                    value={formData.colors.border}
+                                    onChange={(value) =>
+                                        handleInputChange(
+                                            "border",
+                                            value,
+                                            "colors",
+                                        )
+                                    }
+                                />
+
+                                <ColorInput
+                                    label="Accent Color"
+                                    description="Accent elements and highlights"
+                                    value={formData.colors.accent}
+                                    onChange={(value) =>
+                                        handleInputChange(
+                                            "accent",
+                                            value,
+                                            "colors",
+                                        )
+                                    }
+                                />
+                            </div>
+                        </div>
+                    </div>
+
+                    {/* Typography */}
+                    <div className="theme-surface shadow rounded-lg">
+                        <div className="px-6 py-4 border-b theme-border">
+                            <h2 className="text-lg font-semibold theme-text">
+                                Typography
+                            </h2>
+                            <p className="theme-text-secondary">
+                                Font settings for your theme
+                            </p>
+                        </div>
+                        <div className="px-6 py-4 space-y-6">
+                            <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
+                                <div>
+                                    <label className="block text-sm font-medium theme-text mb-1">
+                                        Body Font Family
+                                    </label>
+                                    <input
+                                        type="text"
+                                        value={formData.typography.fontFamily}
+                                        onChange={(e) =>
+                                            handleInputChange(
+                                                "fontFamily",
+                                                e.target.value,
+                                                "typography",
+                                            )
+                                        }
+                                        className="w-full px-3 py-2 border theme-border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 font-mono text-sm"
+                                        placeholder="Inter, system-ui, sans-serif"
+                                    />
+                                </div>
+
+                                <div>
+                                    <label className="block text-sm font-medium theme-text mb-1">
+                                        Heading Font Family
+                                    </label>
+                                    <input
+                                        type="text"
+                                        value={
+                                            formData.typography
+                                                .headingFontFamily
+                                        }
+                                        onChange={(e) =>
+                                            handleInputChange(
+                                                "headingFontFamily",
+                                                e.target.value,
+                                                "typography",
+                                            )
+                                        }
+                                        className="w-full px-3 py-2 border theme-border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 font-mono text-sm"
+                                        placeholder="Inter, system-ui, sans-serif"
+                                    />
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+
+                    {/* Custom CSS */}
+                    <div className="theme-surface shadow rounded-lg">
+                        <div className="px-6 py-4 border-b theme-border">
+                            <h2 className="text-lg font-semibold theme-text">
+                                Custom CSS
+                            </h2>
+                            <p className="theme-text-secondary">
+                                Add custom CSS for advanced styling
+                            </p>
+                        </div>
+                        <div className="px-6 py-4">
+                            <textarea
+                                rows={10}
+                                value={formData.customCSS}
+                                onChange={(e) =>
+                                    handleInputChange(
+                                        "customCSS",
+                                        e.target.value,
+                                    )
+                                }
+                                className="w-full px-3 py-2 border theme-border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 font-mono text-sm"
+                                placeholder="/* Custom CSS rules */&#10;.my-custom-class {&#10;  /* your styles */&#10;}"
+                            />
+                            <p className="mt-2 text-xs theme-text-secondary">
+                                Use CSS custom properties like{" "}
+                                <em>var(--color-primary)</em> to reference theme
+                                colors
+                            </p>
+                        </div>
+                    </div>
+
+                    {/* Actions */}
+                    <div className="flex justify-end space-x-4">
+                        <Link
+                            to="/admin/themes"
+                            className="px-6 py-2 border theme-border rounded-lg theme-text hover:theme-bg"
+                        >
+                            Cancel
+                        </Link>
+                        <button
+                            type="submit"
+                            disabled={saving}
+                            className="px-6 py-2 btn-theme-primary text-white rounded-lg disabled:opacity-50 disabled:cursor-not-allowed"
+                        >
+                            {saving
+                                ? "Saving..."
+                                : isEditing
+                                  ? "Update Theme"
+                                  : "Create Theme"}
+                        </button>
+                    </div>
+                </form>
             </div>
-          </div>
-
-          {/* Actions */}
-          <div className="flex justify-end space-x-4">
-            <Link
-              to="/admin/themes"
-              className="px-6 py-2 border border-gray-300 rounded-lg text-gray-700 hover:bg-gray-50"
-            >
-              Cancel
-            </Link>
-            <button
-              type="submit"
-              disabled={saving}
-              className="px-6 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed"
-            >
-              {saving ? 'Saving...' : (isEditing ? 'Update Theme' : 'Create Theme')}
-            </button>
-          </div>
-        </form>
-      </div>
-    </div>
-  );
+        </div>
+    );
 }
 
-export default ThemeEditor;
+export default ThemeEditor;

+ 376 - 256
src/components/ThemesManager.jsx

@@ -1,281 +1,401 @@
-import React, { useState } from 'react';
-import { Link } from 'react-router-dom';
-import { useTheme } from '../contexts/ThemeContext';
+import React, { useState } from "react";
+import { Link } from "react-router-dom";
+import { useTheme } from "../contexts/ThemeContext";
 
 function ThemesManager() {
-  const { 
-    currentTheme, 
-    allThemes, 
-    activeThemeId, 
-    loading, 
-    error,
-    setActiveTheme, 
-    deleteTheme, 
-    exportTheme 
-  } = useTheme();
+    const {
+        currentTheme,
+        allThemes,
+        activeThemeId,
+        loading,
+        error,
+        setActiveTheme,
+        deleteTheme,
+        exportTheme,
+    } = useTheme();
 
-  const [changingTheme, setChangingTheme] = useState(null);
-  const [deletingTheme, setDeletingTheme] = useState(null);
+    const [changingTheme, setChangingTheme] = useState(null);
+    const [deletingTheme, setDeletingTheme] = useState(null);
 
-  const handleSetActive = async (themeId) => {
-    setChangingTheme(themeId);
-    try {
-      const result = await setActiveTheme(themeId);
-      if (!result.success) {
-        // Check if it's an authentication error
-        if (result.error.includes('401') || result.error.includes('Authentication required')) {
-          alert('You need to be logged in as an admin to change themes. Please log in and try again.');
-        } else {
-          alert(`Failed to set theme: ${result.error}`);
+    const handleSetActive = async (themeId) => {
+        setChangingTheme(themeId);
+        try {
+            const result = await setActiveTheme(themeId);
+            if (!result.success) {
+                // Check if it's an authentication error
+                if (
+                    result.error.includes("401") ||
+                    result.error.includes("Authentication required")
+                ) {
+                    alert(
+                        "You need to be logged in as an admin to change themes. Please log in and try again.",
+                    );
+                } else {
+                    alert(`Failed to set theme: ${result.error}`);
+                }
+            }
+        } finally {
+            setChangingTheme(null);
         }
-      }
-    } finally {
-      setChangingTheme(null);
-    }
-  };
+    };
 
-  const handleDelete = async (themeId) => {
-    if (!confirm('Are you sure you want to delete this theme? This action cannot be undone.')) {
-      return;
-    }
+    const handleDelete = async (themeId) => {
+        if (
+            !confirm(
+                "Are you sure you want to delete this theme? This action cannot be undone.",
+            )
+        ) {
+            return;
+        }
 
-    setDeletingTheme(themeId);
-    try {
-      await deleteTheme(themeId);
-    } finally {
-      setDeletingTheme(null);
-    }
-  };
+        setDeletingTheme(themeId);
+        try {
+            await deleteTheme(themeId);
+        } finally {
+            setDeletingTheme(null);
+        }
+    };
 
-  const handleExport = async (themeId) => {
-    await exportTheme(themeId);
-  };
+    const handleExport = async (themeId) => {
+        await exportTheme(themeId);
+    };
 
-  const ThemeCard = ({ theme }) => {
-    const isActive = theme.id === activeThemeId;
-    const isChanging = changingTheme === theme.id;
-    const isDeleting = deletingTheme === theme.id;
+    const ThemeCard = ({ theme }) => {
+        const isActive = theme.id === activeThemeId;
+        const isChanging = changingTheme === theme.id;
+        const isDeleting = deletingTheme === theme.id;
 
-    return (
-      <div className={`relative bg-white rounded-lg border-2 overflow-hidden transition-all duration-200 ${
-        isActive ? 'border-blue-500 shadow-lg' : 'border-gray-200 hover:border-gray-300 shadow'
-      }`}>
-        {/* Theme Preview */}
-        <div className="h-32 relative" style={{ backgroundColor: theme.colors.background }}>
-          <div className="absolute inset-0 p-4">
-            {/* Mini header */}
-            <div className="flex items-center justify-between mb-2">
-              <div className="h-2 w-16 rounded" style={{ backgroundColor: theme.colors.primary }}></div>
-              <div className="flex space-x-1">
-                <div className="h-2 w-2 rounded-full" style={{ backgroundColor: theme.colors.accent }}></div>
-                <div className="h-2 w-2 rounded-full" style={{ backgroundColor: theme.colors.secondary }}></div>
-              </div>
-            </div>
-            
-            {/* Mini content */}
-            <div className="space-y-2">
-              <div className="h-3 w-full rounded" style={{ backgroundColor: theme.colors.surface }}></div>
-              <div className="h-2 w-3/4 rounded" style={{ backgroundColor: theme.colors.textSecondary }}></div>
-              <div className="h-2 w-1/2 rounded" style={{ backgroundColor: theme.colors.textSecondary }}></div>
-            </div>
-            
-            {/* Mini button */}
-            <div className="absolute bottom-3 right-3">
-              <div className="h-6 w-12 rounded text-xs flex items-center justify-center text-white font-medium"
-                   style={{ backgroundColor: theme.colors.primary }}>
-                BTN
-              </div>
-            </div>
-          </div>
-        </div>
+        return (
+            <div
+                className={`relative theme-surface rounded-lg border-2 overflow-hidden transition-all duration-200 ${
+                    isActive
+                        ? "border-blue-500 shadow-lg"
+                        : "theme-border hover:border-gray-300 shadow"
+                }`}
+            >
+                {/* Theme Preview */}
+                <div
+                    className="h-32 relative"
+                    style={{ backgroundColor: theme.colors.background }}
+                >
+                    <div className="absolute inset-0 p-4">
+                        {/* Mini header */}
+                        <div className="flex items-center justify-between mb-2">
+                            <div
+                                className="h-2 w-16 rounded"
+                                style={{
+                                    backgroundColor: theme.colors.primary,
+                                }}
+                            ></div>
+                            <div className="flex space-x-1">
+                                <div
+                                    className="h-2 w-2 rounded-full"
+                                    style={{
+                                        backgroundColor: theme.colors.accent,
+                                    }}
+                                ></div>
+                                <div
+                                    className="h-2 w-2 rounded-full"
+                                    style={{
+                                        backgroundColor: theme.colors.secondary,
+                                    }}
+                                ></div>
+                            </div>
+                        </div>
 
-        {/* Theme Info */}
-        <div className="p-4">
-          <div className="flex items-center justify-between mb-2">
-            <h3 className="font-semibold text-gray-900">{theme.name}</h3>
-            {isActive && (
-              <span className="bg-green-100 text-green-800 text-xs px-2 py-1 rounded-full font-medium">
-                Active
-              </span>
-            )}
-            {theme.isBuiltIn && (
-              <span className="bg-blue-100 text-blue-800 text-xs px-2 py-1 rounded-full font-medium">
-                Built-in
-              </span>
-            )}
-          </div>
-          
-          <p className="text-sm text-gray-500 mb-3">
-            {theme.id} • {theme.isBuiltIn ? 'System theme' : 'Custom theme'}
-          </p>
+                        {/* Mini content */}
+                        <div className="space-y-2">
+                            <div
+                                className="h-3 w-full rounded"
+                                style={{
+                                    backgroundColor: theme.colors.surface,
+                                }}
+                            ></div>
+                            <div
+                                className="h-2 w-3/4 rounded"
+                                style={{
+                                    backgroundColor: theme.colors.textSecondary,
+                                }}
+                            ></div>
+                            <div
+                                className="h-2 w-1/2 rounded"
+                                style={{
+                                    backgroundColor: theme.colors.textSecondary,
+                                }}
+                            ></div>
+                        </div>
 
-          {/* Color Palette Preview */}
-          <div className="flex space-x-1 mb-4">
-            <div className="h-4 w-4 rounded border border-gray-200" 
-                 style={{ backgroundColor: theme.colors.primary }}></div>
-            <div className="h-4 w-4 rounded border border-gray-200" 
-                 style={{ backgroundColor: theme.colors.background }}></div>
-            <div className="h-4 w-4 rounded border border-gray-200" 
-                 style={{ backgroundColor: theme.colors.surface }}></div>
-            <div className="h-4 w-4 rounded border border-gray-200" 
-                 style={{ backgroundColor: theme.colors.text }}></div>
-            <div className="h-4 w-4 rounded border border-gray-200" 
-                 style={{ backgroundColor: theme.colors.accent }}></div>
-          </div>
+                        {/* Mini button */}
+                        <div className="absolute bottom-3 right-3">
+                            <div
+                                className="h-6 w-12 rounded text-xs flex items-center justify-center text-white font-medium"
+                                style={{
+                                    backgroundColor: theme.colors.primary,
+                                }}
+                            >
+                                BTN
+                            </div>
+                        </div>
+                    </div>
+                </div>
 
-          {/* Actions */}
-          <div className="flex space-x-2">
-            {!isActive && (
-              <button
-                onClick={() => handleSetActive(theme.id)}
-                disabled={isChanging}
-                className="flex-1 bg-blue-600 text-white py-2 px-3 rounded text-sm font-medium hover:bg-blue-700 disabled:opacity-50"
-              >
-                {isChanging ? 'Applying...' : 'Apply'}
-              </button>
-            )}
-            
-            <Link
-              to={`/admin/themes/${theme.id}/edit`}
-              className="flex-1 bg-gray-100 text-gray-700 py-2 px-3 rounded text-sm font-medium hover:bg-gray-200 text-center"
-            >
-              {theme.isBuiltIn ? 'Clone' : 'Edit'}
-            </Link>
-            
-            <button
-              onClick={() => handleExport(theme.id)}
-              className="bg-green-100 text-green-700 py-2 px-3 rounded text-sm font-medium hover:bg-green-200"
-              title="Export Theme"
-            >
-              <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
-                <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
-              </svg>
-            </button>
-            
-            {!theme.isBuiltIn && (
-              <button
-                onClick={() => handleDelete(theme.id)}
-                disabled={isDeleting}
-                className="bg-red-100 text-red-700 py-2 px-3 rounded text-sm font-medium hover:bg-red-200 disabled:opacity-50"
-                title="Delete Theme"
-              >
-                {isDeleting ? (
-                  <div className="w-4 h-4 border-2 border-red-700 border-t-transparent rounded-full animate-spin"></div>
-                ) : (
-                  <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
-                    <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
-                  </svg>
-                )}
-              </button>
-            )}
-          </div>
-        </div>
-      </div>
-    );
-  };
+                {/* Theme Info */}
+                <div className="p-4">
+                    <div className="flex items-center justify-between mb-2">
+                        <h3 className="font-semibold theme-text">
+                            {theme.name}
+                        </h3>
+                        {isActive && (
+                            <span className="bg-green-100 text-green-800 text-xs px-2 py-1 rounded-full font-medium">
+                                Active
+                            </span>
+                        )}
+                        {theme.isBuiltIn && (
+                            <span className="bg-blue-100 text-blue-800 text-xs px-2 py-1 rounded-full font-medium">
+                                Built-in
+                            </span>
+                        )}
+                    </div>
 
-  if (loading) {
-    return (
-      <div className="min-h-screen bg-gray-50 flex items-center justify-center">
-        <div className="text-center">
-          <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto"></div>
-          <p className="mt-4 text-gray-600">Loading themes...</p>
-        </div>
-      </div>
-    );
-  }
+                    <p className="text-sm theme-text-secondary mb-3">
+                        {theme.id} •{" "}
+                        {theme.isBuiltIn ? "System theme" : "Custom theme"}
+                    </p>
 
-  return (
-    <div className="min-h-screen bg-gray-50">
-      <div className="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
-        {/* Header */}
-        <div className="bg-white shadow rounded-lg mb-6">
-          <div className="px-6 py-4 border-b border-gray-200 flex justify-between items-center">
-            <div>
-              <h1 className="text-2xl font-bold text-gray-900">Theme Manager</h1>
-              <p className="text-gray-600">Customize your blog's appearance</p>
-            </div>
-            <div className="flex space-x-3">
-              <Link
-                to="/admin"
-                className="bg-gray-600 text-white px-4 py-2 rounded-lg hover:bg-gray-700 transition-colors"
-              >
-                Back to Admin
-              </Link>
-              <Link
-                to="/admin/themes/new"
-                className="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition-colors"
-              >
-                Create Theme
-              </Link>
-            </div>
-          </div>
-        </div>
+                    {/* Color Palette Preview */}
+                    <div className="flex space-x-1 mb-4">
+                        <div
+                            className="h-4 w-4 rounded border theme-border"
+                            style={{ backgroundColor: theme.colors.primary }}
+                        ></div>
+                        <div
+                            className="h-4 w-4 rounded border theme-border"
+                            style={{ backgroundColor: theme.colors.background }}
+                        ></div>
+                        <div
+                            className="h-4 w-4 rounded border theme-border"
+                            style={{ backgroundColor: theme.colors.surface }}
+                        ></div>
+                        <div
+                            className="h-4 w-4 rounded border theme-border"
+                            style={{ backgroundColor: theme.colors.text }}
+                        ></div>
+                        <div
+                            className="h-4 w-4 rounded border theme-border"
+                            style={{ backgroundColor: theme.colors.accent }}
+                        ></div>
+                    </div>
+
+                    {/* Actions */}
+                    <div className="flex space-x-2">
+                        {!isActive && (
+                            <button
+                                onClick={() => handleSetActive(theme.id)}
+                                disabled={isChanging}
+                                className="flex-1 btn-theme-primary text-white py-2 px-3 rounded text-sm font-medium disabled:opacity-50"
+                            >
+                                {isChanging ? "Applying..." : "Apply"}
+                            </button>
+                        )}
+
+                        <Link
+                            to={`/admin/themes/${theme.id}/edit`}
+                            className="flex-1 btn-theme-secondary text-white py-2 px-3 rounded text-sm font-medium text-center"
+                        >
+                            {theme.isBuiltIn ? "Clone" : "Edit"}
+                        </Link>
 
-        {error && (
-          <div className="mb-6 bg-red-50 border border-red-200 rounded-lg p-4">
-            <div className="flex">
-              <svg className="h-5 w-5 text-red-400" viewBox="0 0 20 20" fill="currentColor">
-                <path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clipRule="evenodd" />
-              </svg>
-              <p className="ml-3 text-sm text-red-700">{error}</p>
+                        <button
+                            onClick={() => handleExport(theme.id)}
+                            className="bg-green-100 text-green-700 py-2 px-3 rounded text-sm font-medium hover:bg-green-200"
+                            title="Export Theme"
+                        >
+                            <svg
+                                className="w-4 h-4"
+                                fill="none"
+                                stroke="currentColor"
+                                viewBox="0 0 24 24"
+                            >
+                                <path
+                                    strokeLinecap="round"
+                                    strokeLinejoin="round"
+                                    strokeWidth={2}
+                                    d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
+                                />
+                            </svg>
+                        </button>
+
+                        {!theme.isBuiltIn && (
+                            <button
+                                onClick={() => handleDelete(theme.id)}
+                                disabled={isDeleting}
+                                className="bg-red-100 text-red-700 py-2 px-3 rounded text-sm font-medium hover:bg-red-200 disabled:opacity-50"
+                                title="Delete Theme"
+                            >
+                                {isDeleting ? (
+                                    <div className="w-4 h-4 border-2 border-red-700 border-t-transparent rounded-full animate-spin"></div>
+                                ) : (
+                                    <svg
+                                        className="w-4 h-4"
+                                        fill="none"
+                                        stroke="currentColor"
+                                        viewBox="0 0 24 24"
+                                    >
+                                        <path
+                                            strokeLinecap="round"
+                                            strokeLinejoin="round"
+                                            strokeWidth={2}
+                                            d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
+                                        />
+                                    </svg>
+                                )}
+                            </button>
+                        )}
+                    </div>
+                </div>
             </div>
-          </div>
-        )}
+        );
+    };
 
-        {/* Current Theme Info */}
-        {currentTheme && (
-          <div className="bg-gradient-to-r from-blue-50 to-indigo-50 border border-blue-200 rounded-lg p-6 mb-6">
-            <div className="flex items-center justify-between">
-              <div>
-                <h2 className="text-lg font-semibold text-blue-900">Currently Active Theme</h2>
-                <p className="text-blue-700">
-                  <span className="font-medium">{currentTheme.name}</span> is currently applied to your blog
-                </p>
-              </div>
-              <div className="flex items-center space-x-2">
-                <div className="flex space-x-1">
-                  {Object.entries(currentTheme.colors).slice(0, 5).map(([key, value]) => (
-                    <div 
-                      key={key}
-                      className="w-6 h-6 rounded border-2 border-white shadow-sm"
-                      style={{ backgroundColor: value }}
-                      title={`${key}: ${value}`}
-                    ></div>
-                  ))}
+    if (loading) {
+        return (
+            <div className="min-h-screen theme-bg flex items-center justify-center">
+                <div className="text-center">
+                    <div className="animate-spin rounded-full h-12 w-12 border-b-2 theme-primary mx-auto"></div>
+                    <p className="mt-4 theme-text-secondary">
+                        Loading themes...
+                    </p>
                 </div>
-              </div>
             </div>
-          </div>
-        )}
+        );
+    }
 
-        {/* Themes Grid */}
-        <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
-          {allThemes.map((theme) => (
-            <ThemeCard key={theme.id} theme={theme} />
-          ))}
-        </div>
+    return (
+        <div className="min-h-screen theme-bg">
+            <div className="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
+                {/* Header */}
+                <div className="theme-surface shadow rounded-lg mb-6">
+                    <div className="px-6 py-4 border-b theme-border flex justify-between items-center">
+                        <div>
+                            <h1 className="text-2xl font-bold theme-text">
+                                Theme Manager
+                            </h1>
+                            <p className="theme-text-secondary">
+                                Customize your blog's appearance
+                            </p>
+                        </div>
+                        <div className="flex space-x-3">
+                            <Link
+                                to="/admin"
+                                className="btn-theme-secondary text-white px-4 py-2 rounded-lg transition-colors"
+                            >
+                                Back to Admin
+                            </Link>
+                            <Link
+                                to="/admin/themes/new"
+                                className="btn-theme-primary text-white px-4 py-2 rounded-lg transition-colors"
+                            >
+                                Create Theme
+                            </Link>
+                        </div>
+                    </div>
+                </div>
+
+                {error && (
+                    <div className="mb-6 bg-red-50 border border-red-200 rounded-lg p-4">
+                        <div className="flex">
+                            <svg
+                                className="h-5 w-5 text-red-400"
+                                viewBox="0 0 20 20"
+                                fill="currentColor"
+                            >
+                                <path
+                                    fillRule="evenodd"
+                                    d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z"
+                                    clipRule="evenodd"
+                                />
+                            </svg>
+                            <p className="ml-3 text-sm text-red-700">{error}</p>
+                        </div>
+                    </div>
+                )}
+
+                {/* Current Theme Info */}
+                {currentTheme && (
+                    <div className="theme-surface border theme-border rounded-lg p-6 mb-6">
+                        <div className="flex items-center justify-between">
+                            <div>
+                                <h2 className="text-lg font-semibold theme-text">
+                                    Currently Active Theme
+                                </h2>
+                                <p className="theme-text-secondary">
+                                    <span className="font-medium">
+                                        {currentTheme.name}
+                                    </span>{" "}
+                                    is currently applied to your blog
+                                </p>
+                            </div>
+                            <div className="flex items-center space-x-2">
+                                <div className="flex space-x-1">
+                                    {Object.entries(currentTheme.colors)
+                                        .slice(0, 5)
+                                        .map(([key, value]) => (
+                                            <div
+                                                key={key}
+                                                className="w-6 h-6 rounded border-2 border-white shadow-sm"
+                                                style={{
+                                                    backgroundColor: value,
+                                                }}
+                                                title={`${key}: ${value}`}
+                                            ></div>
+                                        ))}
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                )}
 
-        {allThemes.length === 0 && (
-          <div className="text-center py-12">
-            <svg className="mx-auto h-12 w-12 text-gray-400" stroke="currentColor" fill="none" viewBox="0 0 48 48">
-              <path d="M34 40h10v-4a6 6 0 00-10.712-3.714M34 40H14m20 0v-4a9.971 9.971 0 00-.712-3.714M14 40H4v-4a6 6 0 0110.713-3.714M14 40v-4c0-1.313.253-2.566.713-3.714m0 0A9.971 9.971 0 0118 28a9.971 9.971 0 014 4.286" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round" />
-            </svg>
-            <h3 className="mt-2 text-sm font-medium text-gray-900">No themes found</h3>
-            <p className="mt-1 text-sm text-gray-500">Get started by creating your first custom theme.</p>
-            <div className="mt-6">
-              <Link
-                to="/admin/themes/new"
-                className="inline-flex items-center px-4 py-2 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700"
-              >
-                Create Theme
-              </Link>
+                {/* Themes Grid */}
+                <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
+                    {allThemes.map((theme) => (
+                        <ThemeCard key={theme.id} theme={theme} />
+                    ))}
+                </div>
+
+                {allThemes.length === 0 && (
+                    <div className="text-center py-12">
+                        <svg
+                            className="mx-auto h-12 w-12 theme-text-secondary"
+                            stroke="currentColor"
+                            fill="none"
+                            viewBox="0 0 48 48"
+                        >
+                            <path
+                                d="M34 40h10v-4a6 6 0 00-10.712-3.714M34 40H14m20 0v-4a9.971 9.971 0 00-.712-3.714M14 40H4v-4a6 6 0 0110.713-3.714M14 40v-4c0-1.313.253-2.566.713-3.714m0 0A9.971 9.971 0 0118 28a9.971 9.971 0 014 4.286"
+                                strokeWidth={2}
+                                strokeLinecap="round"
+                                strokeLinejoin="round"
+                            />
+                        </svg>
+                        <h3 className="mt-2 text-sm font-medium theme-text">
+                            No themes found
+                        </h3>
+                        <p className="mt-1 text-sm theme-text-secondary">
+                            Get started by creating your first custom theme.
+                        </p>
+                        <div className="mt-6">
+                            <Link
+                                to="/admin/themes/new"
+                                className="inline-flex items-center px-4 py-2 border border-transparent shadow-sm text-sm font-medium rounded-md text-white btn-theme-primary"
+                            >
+                                Create Theme
+                            </Link>
+                        </div>
+                    </div>
+                )}
             </div>
-          </div>
-        )}
-      </div>
-    </div>
-  );
+        </div>
+    );
 }
 
-export default ThemesManager;
+export default ThemesManager;