8.8 제휴업소 요청: 요청 생성/승인/상태 관리/이력 o

This commit is contained in:
koreacomp5
2025-10-09 17:45:34 +09:00
parent 66a68c2bfa
commit 8f4446a87a
7 changed files with 93 additions and 2 deletions

View File

@@ -0,0 +1,16 @@
-- CreateTable
CREATE TABLE "partner_requests" (
"id" TEXT NOT NULL PRIMARY KEY,
"name" TEXT NOT NULL,
"category" TEXT NOT NULL,
"latitude" REAL NOT NULL,
"longitude" REAL NOT NULL,
"address" TEXT,
"contact" TEXT,
"status" TEXT NOT NULL DEFAULT 'pending',
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"approvedAt" DATETIME
);
-- CreateIndex
CREATE INDEX "partner_requests_status_createdAt_idx" ON "partner_requests"("status", "createdAt");

View File

@@ -646,3 +646,20 @@ model PartnerInquiry {
@@index([status, createdAt]) @@index([status, createdAt])
@@map("partner_inquiries") @@map("partner_inquiries")
} }
// 제휴업소 등록 요청
model PartnerRequest {
id String @id @default(cuid())
name String
category String
latitude Float
longitude Float
address String?
contact String?
status String @default("pending") // pending/approved/rejected
createdAt DateTime @default(now())
approvedAt DateTime?
@@index([status, createdAt])
@@map("partner_requests")
}

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 727 KiB

After

Width:  |  Height:  |  Size: 754 KiB

View File

@@ -0,0 +1,12 @@
import { NextResponse } from "next/server";
import prisma from "@/lib/prisma";
export async function POST(_: Request, context: { params: Promise<{ id: string }> }) {
const { id } = await context.params;
const reqItem = await prisma.partnerRequest.update({ where: { id }, data: { status: "approved", approvedAt: new Date() } });
// 승인 시 실제 파트너 등록
await prisma.partner.create({ data: { name: reqItem.name, category: reqItem.category, latitude: reqItem.latitude, longitude: reqItem.longitude, address: reqItem.address ?? undefined } });
return NextResponse.json({ request: reqItem });
}

View File

@@ -0,0 +1,20 @@
import { NextResponse } from "next/server";
import prisma from "@/lib/prisma";
import { z } from "zod";
const schema = z.object({ name: z.string().min(1), category: z.string().min(1), latitude: z.coerce.number(), longitude: z.coerce.number(), address: z.string().optional(), contact: z.string().optional() });
export async function POST(req: Request) {
const body = await req.json().catch(() => ({}));
const parsed = schema.safeParse(body);
if (!parsed.success) return NextResponse.json({ error: parsed.error.flatten() }, { status: 400 });
const created = await prisma.partnerRequest.create({ data: parsed.data });
return NextResponse.json({ request: created }, { status: 201 });
}
export async function GET() {
const items = await prisma.partnerRequest.findMany({ orderBy: { createdAt: "desc" } });
return NextResponse.json({ requests: items });
}

View File

@@ -0,0 +1,26 @@
"use client";
import { useState } from "react";
export default function PartnerRequestPage() {
const [form, setForm] = useState({ name: "", category: "spa", latitude: "", longitude: "", address: "", contact: "" });
const [done, setDone] = useState(false);
async function submit() {
const r = await fetch("/api/partner-requests", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ ...form, latitude: Number(form.latitude), longitude: Number(form.longitude) }) });
setDone(r.ok);
}
if (done) return <div> . .</div>;
return (
<div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
<h1> </h1>
<input placeholder="업체명" value={form.name} onChange={(e) => setForm({ ...form, name: e.target.value })} />
<input placeholder="카테고리(spa/gym 등)" value={form.category} onChange={(e) => setForm({ ...form, category: e.target.value })} />
<input placeholder="위도" value={form.latitude} onChange={(e) => setForm({ ...form, latitude: e.target.value })} />
<input placeholder="경도" value={form.longitude} onChange={(e) => setForm({ ...form, longitude: e.target.value })} />
<input placeholder="주소(선택)" value={form.address} onChange={(e) => setForm({ ...form, address: e.target.value })} />
<input placeholder="연락처(선택)" value={form.contact} onChange={(e) => setForm({ ...form, contact: e.target.value })} />
<button onClick={submit}></button>
</div>
);
}

View File

@@ -59,7 +59,7 @@
8.5 월간집계: 월별 지표 산출 배치/차트/다운로드(CSV) o 8.5 월간집계: 월별 지표 산출 배치/차트/다운로드(CSV) o
8.6 주변 제휴업체: 위치 기반 목록/지도/필터(거리/카테고리) o 8.6 주변 제휴업체: 위치 기반 목록/지도/필터(거리/카테고리) o
8.7 제휴문의: 접수 폼/관리자 승인 워크플로우/알림 o 8.7 제휴문의: 접수 폼/관리자 승인 워크플로우/알림 o
8.8 제휴업소 요청: 요청 생성/승인/상태 관리/이력 8.8 제휴업소 요청: 요청 생성/승인/상태 관리/이력 o
[에디터/업로드] [에디터/업로드]
9.1 에디터(Tiptap/Quill 중 택1) 통합 9.1 에디터(Tiptap/Quill 중 택1) 통합