- Docker Compose with Postgres, Redis, MinIO, backend, frontend (port 3200/3201) - Prisma schema: User, Project, ProjectFile, Product, Order, FlashToken, Review, AuditLog - Backend: JWT auth, project CRUD + file upload (MinIO + sharp WebP), admin approval flow - Frontend: React + Vite SPA with auth, project/shop browse, seller dashboard, admin panel - Admin: pending approval queue, user management, audit log viewer, stats dashboard - Audit logging middleware for legal compliance - Admin init script: createAdmin.js - Full design document in PLATFORM_DESIGN.md Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
76 lines
3.1 KiB
JavaScript
76 lines
3.1 KiB
JavaScript
import { useEffect, useState } from 'react';
|
|
import { Link } from 'react-router-dom';
|
|
import { useAuth } from '../../hooks/useAuth';
|
|
import api from '../../api/client';
|
|
|
|
const STATUS_BADGE = {
|
|
draft: <span className="badge badge-draft">임시저장</span>,
|
|
pending: <span className="badge badge-pending">검토 대기</span>,
|
|
approved: <span className="badge badge-approved">승인됨</span>,
|
|
rejected: <span className="badge badge-rejected">반려됨</span>,
|
|
suspended: <span className="badge badge-suspended">정지됨</span>,
|
|
};
|
|
|
|
export default function Dashboard() {
|
|
const { user } = useAuth();
|
|
const [projects, setProjects] = useState([]);
|
|
const [loading, setLoading] = useState(true);
|
|
|
|
useEffect(() => {
|
|
api.get('/projects/my/list')
|
|
.then(r => setProjects(r.data))
|
|
.catch(() => {})
|
|
.finally(() => setLoading(false));
|
|
}, []);
|
|
|
|
return (
|
|
<div className="container page">
|
|
<div className="flex items-center justify-between" style={{ marginBottom: 24 }}>
|
|
<div>
|
|
<h2 style={{ fontSize: 22 }}>내 대시보드</h2>
|
|
<p className="text-muted" style={{ marginTop: 4 }}>안녕하세요, {user?.nickname}님</p>
|
|
</div>
|
|
<Link to="/dashboard/projects/new" className="btn btn-primary">+ 새 프로젝트</Link>
|
|
</div>
|
|
|
|
<div style={{ display: 'flex', gap: 12, marginBottom: 16 }}>
|
|
<Link to="/dashboard/projects/new" className="btn btn-outline btn-sm">프로젝트 등록</Link>
|
|
<Link to="/dashboard/orders" className="btn btn-outline btn-sm">구매 내역</Link>
|
|
<Link to="/dashboard/sales" className="btn btn-outline btn-sm">판매 내역</Link>
|
|
</div>
|
|
|
|
<h3 style={{ marginBottom: 16 }}>내 프로젝트</h3>
|
|
{loading ? <div className="spinner" /> : (
|
|
projects.length === 0 ? (
|
|
<div className="card text-center" style={{ padding: 48 }}>
|
|
<p className="text-muted">등록한 프로젝트가 없습니다.</p>
|
|
<Link to="/dashboard/projects/new" className="btn btn-primary" style={{ marginTop: 16 }}>첫 프로젝트 등록</Link>
|
|
</div>
|
|
) : (
|
|
<div className="table-wrap">
|
|
<table>
|
|
<thead>
|
|
<tr><th>제목</th><th>상태</th><th>판매가</th><th>판매수</th><th>날짜</th><th></th></tr>
|
|
</thead>
|
|
<tbody>
|
|
{projects.map(p => (
|
|
<tr key={p.id}>
|
|
<td>{p.title}</td>
|
|
<td>{STATUS_BADGE[p.status] || p.status}</td>
|
|
<td>{p.product ? `₩${p.product.price.toLocaleString()}` : '-'}</td>
|
|
<td>{p.product?.totalSales ?? '-'}</td>
|
|
<td className="text-muted">{new Date(p.createdAt).toLocaleDateString('ko-KR')}</td>
|
|
<td>
|
|
<Link to={`/dashboard/projects/${p.id}`} className="btn btn-outline btn-sm">관리</Link>
|
|
</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
)
|
|
)}
|
|
</div>
|
|
);
|
|
}
|