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({