Files
startover/apps/web/src/app/subsidies/page.tsx
T
Johngreen 16bd2cb92a feat: Re:Link MVP 초기 구현 - 도메인/서비스/프론트엔드 전체
- 모노레포 구조 (Turborepo + pnpm): @relink/domain, @relink/shared, @relink/infrastructure, @relink/database, @relink/web
- 도메인 레이어: 매장(store), 매칭(matching), 업체(vendor), 보조금(subsidy), 계약/에스크로(contract) TDD 완료 (158 단위 테스트)
- 서비스 레이어: 전 도메인 서비스 함수 + 통합 테스트 (58 테스트)
- 프론트엔드: Next.js 15 App Router, 13개 페이지 (사용자 6 + 관리자 7)
- 인프라: PostgreSQL 16 + PostGIS, Prisma ORM, Docker Compose, AuditLog + OutboxEvent 패턴
- .env 파일 포함 (로컬 개발 기본값만 포함, 실제 시크릿 없음)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 17:39:56 +09:00

101 lines
3.7 KiB
TypeScript

import Link from 'next/link';
const SAMPLE_CASES = [
{
id: 'sc-1',
storeTitle: '강남역 카페',
status: 'DOCUMENTS_PENDING',
checklist: { total: 5, checked: 2 },
createdAt: '2026-03-04',
},
{
id: 'sc-2',
storeTitle: '선릉역 한식당',
status: 'SUBMITTED',
checklist: { total: 5, checked: 5 },
createdAt: '2026-03-01',
},
{
id: 'sc-3',
storeTitle: '합정 베이커리',
status: 'APPROVED',
checklist: { total: 4, checked: 4 },
createdAt: '2026-02-28',
},
];
const STATUS_MAP: Record<string, { label: string; color: string }> = {
DOCUMENTS_PENDING: { label: '서류 준비 중', color: 'bg-yellow-100 text-yellow-700' },
READY_TO_SUBMIT: { label: '제출 준비 완료', color: 'bg-blue-100 text-blue-700' },
SUBMITTED: { label: '제출됨', color: 'bg-purple-100 text-purple-700' },
REVIEWING: { label: '검토 중', color: 'bg-orange-100 text-orange-700' },
APPROVED: { label: '승인', color: 'bg-green-100 text-green-700' },
REJECTED: { label: '반려', color: 'bg-red-100 text-red-700' },
};
export default function SubsidiesPage() {
return (
<main className="mx-auto max-w-4xl px-4 py-8">
<h1 className="text-2xl font-bold text-gray-900"> </h1>
<p className="mt-1 text-sm text-gray-500">
</p>
{/* 안내 배너 */}
<div className="mt-6 rounded-lg border border-blue-200 bg-blue-50 p-4">
<h3 className="text-sm font-semibold text-blue-800"> </h3>
<p className="mt-1 text-sm text-blue-700">
Re:Link은 , · · ·
.
</p>
</div>
{/* 케이스 목록 */}
<div className="mt-6 space-y-4">
{SAMPLE_CASES.map((c) => {
const statusInfo = STATUS_MAP[c.status] ?? { label: c.status, color: 'bg-gray-100 text-gray-700' };
return (
<div key={c.id} className="rounded-lg border border-gray-200 bg-white p-5">
<div className="flex items-start justify-between">
<div>
<h3 className="font-semibold text-gray-900">{c.storeTitle}</h3>
<p className="mt-1 text-xs text-gray-400">: {c.createdAt}</p>
</div>
<span className={`rounded-full px-2.5 py-0.5 text-xs font-medium ${statusInfo.color}`}>
{statusInfo.label}
</span>
</div>
{/* 체크리스트 진행률 */}
<div className="mt-4">
<div className="flex items-center justify-between text-sm">
<span className="text-gray-600"> </span>
<span className="text-gray-900">
{c.checklist.checked}/{c.checklist.total}
</span>
</div>
<div className="mt-1 h-2 rounded-full bg-gray-200">
<div
className="h-2 rounded-full bg-blue-500"
style={{ width: `${(c.checklist.checked / c.checklist.total) * 100}%` }}
/>
</div>
</div>
</div>
);
})}
</div>
<div className="mt-8 rounded-lg border border-dashed border-gray-300 p-6 text-center">
<p className="text-sm text-gray-500">
{' '}
<Link href="/stores" className="text-blue-600 hover:underline">
</Link>
</p>
</div>
</main>
);
}