Redesign blog as desktop OS
All checks were successful
Deploy blog-frontend / build-and-deploy (push) Successful in 2m10s
All checks were successful
Deploy blog-frontend / build-and-deploy (push) Successful in 2m10s
This commit is contained in:
@@ -5,6 +5,7 @@ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import toast from 'react-hot-toast';
|
||||
import { Folder, FolderOpen, FolderPlus, FolderTree, Loader2, Save, Trash2 } from 'lucide-react';
|
||||
import { createCategory, deleteCategory, getCategories, updateCategory } from '@/api/category';
|
||||
import WindowSurface from '@/components/ui/WindowSurface';
|
||||
import { Category, CategoryUpdateRequest } from '@/types';
|
||||
import { clsx } from 'clsx';
|
||||
|
||||
@@ -342,7 +343,7 @@ export default function AdminCategoryPanel() {
|
||||
};
|
||||
|
||||
return (
|
||||
<section className="rounded-lg border border-gray-200 bg-white p-5">
|
||||
<WindowSurface title="Categories" subtitle="Finder tree" bodyClassName="p-5">
|
||||
<div className="mb-5 flex items-start justify-between gap-4">
|
||||
<div>
|
||||
<h2 className="flex items-center gap-2 text-lg font-bold text-gray-950">
|
||||
@@ -426,6 +427,6 @@ export default function AdminCategoryPanel() {
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</WindowSurface>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
X,
|
||||
} from 'lucide-react';
|
||||
import { deleteAdminComment, getAdminComments } from '@/api/comments';
|
||||
import WindowSurface from '@/components/ui/WindowSurface';
|
||||
import { AdminComment, AdminCommentListResponse, PageMeta } from '@/types';
|
||||
|
||||
const PAGE_SIZE = 10;
|
||||
@@ -168,7 +169,7 @@ export default function AdminCommentsPanel() {
|
||||
};
|
||||
|
||||
return (
|
||||
<section className="rounded-lg border border-gray-200 bg-white p-5">
|
||||
<WindowSurface title="Comments" subtitle="Moderation" bodyClassName="p-5">
|
||||
<div className="mb-5 flex items-start justify-between gap-4">
|
||||
<div>
|
||||
<h2 className="flex items-center gap-2 text-lg font-bold text-gray-950">
|
||||
@@ -283,6 +284,6 @@ export default function AdminCommentsPanel() {
|
||||
onConfirm={handleConfirmDelete}
|
||||
/>
|
||||
)}
|
||||
</section>
|
||||
</WindowSurface>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
} from 'lucide-react';
|
||||
import { getCategories } from '@/api/category';
|
||||
import { deletePost, getPosts } from '@/api/posts';
|
||||
import WindowSurface from '@/components/ui/WindowSurface';
|
||||
import { Category, PageMeta, Post, PostListResponse } from '@/types';
|
||||
|
||||
const PAGE_SIZE = 10;
|
||||
@@ -399,7 +400,7 @@ export default function AdminPostsPanel() {
|
||||
};
|
||||
|
||||
return (
|
||||
<section className="rounded-lg border border-gray-200 bg-white p-5">
|
||||
<WindowSurface title="Posts" subtitle="Management" bodyClassName="p-5">
|
||||
<div className="mb-5 flex flex-col justify-between gap-4 md:flex-row md:items-end">
|
||||
<div>
|
||||
<h2 className="flex items-center gap-2 text-lg font-bold text-gray-950">
|
||||
@@ -665,6 +666,6 @@ export default function AdminPostsPanel() {
|
||||
onConfirm={handleConfirmBulkDelete}
|
||||
/>
|
||||
)}
|
||||
</section>
|
||||
</WindowSurface>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import toast from 'react-hot-toast';
|
||||
import { Camera, Loader2, Save, UserRound } from 'lucide-react';
|
||||
import { getProfile, updateProfile } from '@/api/profile';
|
||||
import { uploadImage } from '@/api/image';
|
||||
import WindowSurface from '@/components/ui/WindowSurface';
|
||||
import { Profile, ProfileUpdateRequest } from '@/types';
|
||||
|
||||
const defaultProfile: Profile = {
|
||||
@@ -206,7 +207,7 @@ export default function AdminProfilePanel() {
|
||||
].join('|');
|
||||
|
||||
return (
|
||||
<section className="rounded-lg border border-gray-200 bg-white p-5">
|
||||
<WindowSurface title="Profile" subtitle="Public identity" bodyClassName="p-5">
|
||||
<div className="mb-5 flex items-start justify-between gap-4">
|
||||
<div>
|
||||
<h2 className="flex items-center gap-2 text-lg font-bold text-gray-950">
|
||||
@@ -224,6 +225,6 @@ export default function AdminProfilePanel() {
|
||||
) : (
|
||||
<ProfileForm key={formKey} profile={displayProfile} />
|
||||
)}
|
||||
</section>
|
||||
</WindowSurface>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { useEffect } from 'react';
|
||||
import toast from 'react-hot-toast';
|
||||
import { BarChart3, FileText, FolderTree, Loader2, MessageSquareText, UserRound } from 'lucide-react';
|
||||
import { clsx } from 'clsx';
|
||||
import WindowSurface from '@/components/ui/WindowSurface';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
|
||||
const navItems = [
|
||||
@@ -57,31 +58,33 @@ export default function AdminRouteShell({ children }: { children: React.ReactNod
|
||||
|
||||
return (
|
||||
<div className="mx-auto max-w-7xl space-y-6 px-1 py-4 md:px-4">
|
||||
<nav
|
||||
className="flex gap-2 overflow-x-auto rounded-full border border-[var(--color-line)] bg-[var(--color-control)] p-1 shadow-[var(--shadow-control)] backdrop-blur-2xl"
|
||||
aria-label="관리자 메뉴"
|
||||
>
|
||||
{navItems.map((item) => {
|
||||
const Icon = item.icon;
|
||||
const isActive = item.exact ? pathname === item.href : pathname === item.href || pathname.startsWith(`${item.href}/`);
|
||||
<WindowSurface title="Admin Console" subtitle="Operations toolbar" bodyClassName="p-2">
|
||||
<nav
|
||||
className="flex gap-2 overflow-x-auto rounded-lg bg-[var(--color-control)] p-1 backdrop-blur-xl"
|
||||
aria-label="관리자 메뉴"
|
||||
>
|
||||
{navItems.map((item) => {
|
||||
const Icon = item.icon;
|
||||
const isActive = item.exact ? pathname === item.href : pathname === item.href || pathname.startsWith(`${item.href}/`);
|
||||
|
||||
return (
|
||||
<Link
|
||||
key={item.href}
|
||||
href={item.href}
|
||||
className={clsx(
|
||||
'inline-flex h-9 shrink-0 items-center gap-2 rounded-full px-4 text-sm font-semibold transition',
|
||||
isActive
|
||||
? 'bg-[var(--color-surface-strong)] text-[var(--color-text)] shadow-sm'
|
||||
: 'text-[var(--color-text-muted)] hover:bg-black/[0.04] hover:text-[var(--color-text)] dark:hover:bg-white/10',
|
||||
)}
|
||||
>
|
||||
<Icon size={16} />
|
||||
{item.label}
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</nav>
|
||||
return (
|
||||
<Link
|
||||
key={item.href}
|
||||
href={item.href}
|
||||
className={clsx(
|
||||
'inline-flex h-9 shrink-0 items-center gap-2 rounded-lg px-4 text-sm font-semibold transition',
|
||||
isActive
|
||||
? 'bg-[var(--window-bg-strong)] text-[var(--color-text)] shadow-sm'
|
||||
: 'text-[var(--color-text-muted)] hover:bg-black/[0.04] hover:text-[var(--color-text)] dark:hover:bg-white/10',
|
||||
)}
|
||||
>
|
||||
<Icon size={16} />
|
||||
{item.label}
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</nav>
|
||||
</WindowSurface>
|
||||
|
||||
{children}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user