From 854a3e2bfb5d4d874955b42e69add624ac9d7053 Mon Sep 17 00:00:00 2001 From: jisangs Date: Thu, 2 Jul 2026 22:13:38 +0900 Subject: [PATCH] =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EA=B2=80=ED=86=A0=20?= =?UTF-8?q?=EB=B0=8F=20=EB=AC=B8=EC=84=9C=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AGENTS.md | 12 +++- .../[maestroId]/ExtensionRequestsSection.tsx | 10 ++- .../maestros/[id]/extension-requests/route.ts | 28 +------- .../maestros/[id]/upgrade-requests/route.ts | 12 +--- docs/plan/project-plan.md | 47 +++++++++++-- lib/constants.ts | 8 +++ lib/extension-requests.ts | 13 ++-- lib/maestros.ts | 12 +--- lib/upgrade-requests.ts | 70 ++++++++++--------- 9 files changed, 115 insertions(+), 97 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index e225b38..adb39cc 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -36,7 +36,7 @@ Admin panel for the chocomae service (mouse-typing), extracted into a standalone | `lib/logger.ts` | Structured logger — `debug/info/warn/error`, level controlled by `LOG_LEVEL` / `APP_ENV` | | `lib/errors.ts` | `ApiError` — unified error class with HTTP status code | | `lib/api-handler.ts` | `withApiHandler` — wraps route handlers with auth, timing, and error logging | -| `lib/constants.ts` | Status code constants (`ACTIVATE_STATUS`, `ACCOUNT_TYPE`, `REQUEST_STATUS`) | +| `lib/constants.ts` | Status code constants (`ACTIVATE_STATUS`, `ACCOUNT_TYPE`, `REQUEST_STATUS`); `PAID_ACCOUNT_TYPE_VALUES` (유료 계정 유형 배열 — 중복 정의 방지용 공용 상수) | | `lib/maestros.ts` | Maestro DB queries | | `lib/extension-requests.ts` | Extension request queries and approval logic | | `lib/upgrade-requests.ts` | Upgrade request queries and approval logic | @@ -67,11 +67,17 @@ Always record a log entry after a successful DB change. Use these `Type` values: - Phase 0–8: login, maestro list/detail, extension requests, upgrade requests, Docker setup, transaction safety, auth guards. - Phase 9 (substantially complete): structured logging (`lib/logger.ts`), `withApiHandler` wrapper (`lib/api-handler.ts`), `ApiError` class (`lib/errors.ts`), Prisma SQL pretty-logging and result table logging (`lib/db.ts`). Remaining: correlation IDs, stage/production log verification. +- Phase 10: Maestro info edit form (`PATCH /api/maestros/[id]`) + student list with server-side pagination (`GET /api/maestros/[id]/students`). +- Phase 11: Email auto-send via Nodemailer after extension/upgrade approval (`lib/mail.ts`). Upgrade email edge-case verification (trial→paid, paid→paid) still pending. +- Phase 12: Admin-initiated extension/upgrade request registration from maestro detail page. + - `POST /api/maestros/[id]/extension-requests` — inserts into `maestro_extension` using maestro's current `AccountType` read server-side; rejects trial account types. + - `POST /api/maestros/[id]/upgrade-requests` — inserts into `maestro_upgrade` inside a transaction; validates `requestedAccountType > maestro.AccountType`. + - `ExtensionRequestsSection.tsx`, `UpgradeRequestsSection.tsx` — client components with inline action UI; call `router.refresh()` on success. + - `PAID_ACCOUNT_TYPE_VALUES` consolidated in `lib/constants.ts` — shared by `lib/maestros.ts`, `lib/extension-requests.ts`, `lib/upgrade-requests.ts`, and both new API routes. ## Planned phases (not yet implemented) -- **Phase 10** — Maestro info edit form + student list with pagination (`PATCH /api/maestros/[id]`, `GET /api/maestros/[id]/students`) -- **Phase 11** — Email auto-send via Nodemailer after extension/upgrade approval (`lib/mail.ts`) +이후 작업 후보는 `docs/plan/project-plan.md` Section 10 "MVP 이후 확장 후보" 참조. ## Packages to know diff --git a/app/(admin)/maestros/[maestroId]/ExtensionRequestsSection.tsx b/app/(admin)/maestros/[maestroId]/ExtensionRequestsSection.tsx index 4dc27d9..1ca4695 100644 --- a/app/(admin)/maestros/[maestroId]/ExtensionRequestsSection.tsx +++ b/app/(admin)/maestros/[maestroId]/ExtensionRequestsSection.tsx @@ -5,6 +5,7 @@ import { useRouter } from "next/navigation"; import { Plus } from "lucide-react"; import { Button } from "@/components/ui/button"; +import { PAID_ACCOUNT_TYPE_VALUES } from "@/lib/constants"; import { formatDateTime, getAccountTypeLabel, getRequestStatusLabel } from "@/lib/utils"; import type { MaestroDetail } from "@/lib/maestros"; @@ -26,6 +27,7 @@ export function ExtensionRequestsSection({ const [isCreating, setIsCreating] = useState(false); const [errorMessage, setErrorMessage] = useState(""); + const isPaidAccount = (PAID_ACCOUNT_TYPE_VALUES as readonly number[]).includes(accountType); const isDisabled = isCreating || isPending; async function handleCreate() { @@ -37,11 +39,7 @@ export function ExtensionRequestsSection({ try { const response = await fetch( `/api/maestros/${maestroID}/extension-requests`, - { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ accountType }), - } + { method: "POST" } ); const body = (await response.json().catch(() => null)) as { @@ -75,7 +73,7 @@ export function ExtensionRequestsSection({