From 5c7a5bef1e1590a7dd5378351ba3de181badcf85 Mon Sep 17 00:00:00 2001 From: vibsin9322 Date: Thu, 11 Jun 2026 20:49:45 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EC=B9=B4=ED=85=8C=EA=B3=A0=EB=A6=AC=20?= =?UTF-8?q?=ED=86=B5=EC=9D=BC,=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EA=B8=B0?= =?UTF-8?q?=EB=B3=B8=EB=B7=B0,=20=EA=B4=80=EB=A6=AC=EC=9E=90=20=EC=A0=84?= =?UTF-8?q?=EC=9A=A9=20=EB=A1=9C=EA=B7=B8=EC=9D=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 카테고리 DB를 type 기반(doc/img/vid/zip/code)으로 마이그레이션 - 사이드바·상단탭이 DB 카테고리 API를 공유해 항상 일치 - 기본 뷰를 그리드 → 리스트로 변경 - 로그인 버튼 제거, 관리자만 로그인 가능 - 관리자 패널에서 사용자 관리 메뉴 제거 Co-Authored-By: Claude Sonnet 4.6 --- admin.html | 2 - index.html | 484 +++++++++++++++++++++++++++++++++++++++++++---------- server.js | 60 ++++--- 3 files changed, 431 insertions(+), 115 deletions(-) diff --git a/admin.html b/admin.html index b865ff1..e84fc76 100644 --- a/admin.html +++ b/admin.html @@ -755,7 +755,6 @@ const UserSection = ({ showToast }) => { const NAV = [ { id: 'categories', label: '카테고리 관리', icon: 'folder' }, { id: 'files', label: '파일 관리', icon: 'file' }, - { id: 'users', label: '사용자 관리', icon: 'user' }, ]; function AdminContent({ onLogout }) { @@ -811,7 +810,6 @@ function AdminContent({ onLogout }) {
{active === 'categories' && } {active === 'files' && } - {active === 'users' && }
diff --git a/index.html b/index.html index 02d736a..13019a7 100644 --- a/index.html +++ b/index.html @@ -247,6 +247,49 @@ .hidden{display:none !important} .scrollbar::-webkit-scrollbar{width:8px} .scrollbar::-webkit-scrollbar-thumb{background:var(--border-strong);border-radius:99px} + + /* Hamburger */ + .hamburger{display:none;appearance:none;border:0;background:transparent;color:var(--ink);width:32px;height:32px;border-radius:6px;cursor:default;place-items:center;flex-shrink:0;margin-right:2px} + .hamburger:hover{background:rgba(20,20,20,.06)} + + /* Type nav tabs */ + .hdr-tabs{display:flex;align-items:flex-end;gap:0;padding:0 28px;border-bottom:1px solid var(--border);background:var(--bg);flex-shrink:0;overflow-x:auto} + .hdr-tabs::-webkit-scrollbar{display:none} + .hdr-tab{padding:9px 14px;font-size:13.5px;font-family:inherit;color:var(--muted);border:none;background:transparent;border-bottom:2px solid transparent;cursor:default;white-space:nowrap;font-weight:500;line-height:1;transition:color .12s ease;margin-bottom:-1px} + .hdr-tab:hover{color:var(--ink-2)} + .hdr-tab.active{color:var(--ink);font-weight:600;border-bottom-color:var(--accent)} + + /* Context menu */ + .ctx-menu{position:fixed;background:var(--surface);border:1px solid var(--border-strong);border-radius:9px;box-shadow:var(--shadow-md);padding:4px;min-width:160px;z-index:500} + .ctx-mi{padding:7px 10px;border-radius:5px;font-size:13px;color:var(--ink-2);display:flex;align-items:center;gap:8px;cursor:default;user-select:none} + .ctx-mi:hover{background:var(--surface-2)} + .ctx-mi.danger{color:var(--warn)} + .ctx-mi.danger:hover{background:rgba(217,119,87,.06)} + + /* Sidebar overlay */ + .sb-overlay{display:none;position:fixed;inset:0;background:rgba(0,0,0,.4);z-index:299} + .sb-overlay.on{display:block;animation:fadeIn .15s ease} + + /* Indeterminate checkbox */ + .check.indeterminate{background:var(--accent);border-color:var(--accent);color:#fff} + + /* Responsive */ + @media(max-width:768px){ + .app{grid-template-columns:1fr} + .sb{position:fixed;left:-270px;top:0;bottom:0;z-index:300;width:260px;border-right:1px solid var(--border);background:var(--surface);box-shadow:var(--shadow-lg);transition:left .25s cubic-bezier(.3,.7,.4,1)} + .sb.open{left:0} + .hamburger{display:grid} + .hdr{padding:10px 16px} + .hdr-tabs{padding:0 16px} + .toolbar{padding:10px 16px 6px} + .scroll{padding:6px 16px 28px} + .grid{grid-template-columns:repeat(auto-fill,minmax(150px,1fr))} + .list{overflow-x:auto} + .list-head,.list-row{min-width:580px} + } + @media(max-width:480px){ + .grid{grid-template-columns:repeat(auto-fill,minmax(130px,1fr))} + } @@ -574,6 +617,7 @@ const CATEGORIES_DEF = [ { id: "media", label: "사진·영상", icon: "video" }, ]; + const FOLDERS_DEF = [ { id: "all", label: "모든 자료", icon: "folder" }, { id: "starred", label: "즐겨찾기", icon: "star" }, @@ -584,8 +628,11 @@ const FOLDERS_DEF = [ // ── API 헬퍼 ────────────────────────────────────────────────── -async function apiFetch(path, options) { - const res = await fetch(path, options); +async function apiFetch(path, options = {}) { + const token = sessionStorage.getItem('admin_token'); + const headers = { ...(options.headers || {}) }; + if (token) headers['Authorization'] = `Bearer ${token}`; + const res = await fetch(path, { ...options, headers }); if (!res.ok) throw new Error(`API ${path} → ${res.status}`); return res.json(); } @@ -637,12 +684,12 @@ const SkeletonGrid = () => ( ); -const Sidebar = ({ activeFolder, setActiveFolder, activeCat, setActiveCat, accent, stats, categories }) => { +const Sidebar = ({ activeFolder, setActiveFolder, activeCat, setActiveCat, categories, accent, stats, open, onClose }) => { const { folders = {}, categories: catStats = {}, storage = { used: 0, total: 100, remaining: 100 } } = stats; const usedPct = Math.min(100, (storage.used / storage.total) * 100).toFixed(0); return ( -