"use client"; import { ChangeEvent, FormEvent, useEffect, useMemo, useState } from "react"; import { apiFetch, apiUrl } from "@/app/lib/api-client"; import { mediaUrl } from "@/app/lib/media"; type SubmissionType = "ebook" | "video"; interface FormState { type: SubmissionType; title: string; subtitle: string; description: string; coverImage: string; mediaUrl: string; authorName: string; authorEmail: string; chaptersText: string; } interface ContentSubmissionModalProps { open: boolean; locale: string; onClose: () => void; submissionType?: SubmissionType; } const MB = 1024 * 1024; const EMPTY_FORM: FormState = { type: "ebook", title: "", subtitle: "", description: "", coverImage: "", mediaUrl: "", authorName: "", authorEmail: "", chaptersText: "", }; export default function ContentSubmissionModal({ open, locale, onClose, submissionType }: ContentSubmissionModalProps) { const isZh = locale === "zh"; const initialType = submissionType || "ebook"; const copy = useMemo(() => { let title = isZh ? "投稿电子书 / 访谈视频" : "Submit ebook / interview"; if (submissionType === "ebook") { title = isZh ? "投稿电子书" : "Submit ebook"; } if (submissionType === "video") { title = isZh ? "投稿访谈视频" : "Submit interview video"; } return { title, close: isZh ? "关闭" : "Close", ebook: isZh ? "电子书" : "Ebook", video: isZh ? "访谈视频" : "Interview video", name: isZh ? "作者昵称" : "Author name", email: isZh ? "联系邮箱" : "Contact email", contentTitle: isZh ? "标题" : "Title", subtitle: isZh ? "一句话简介" : "Short subtitle", description: isZh ? "内容简介" : "Description", cover: isZh ? "封面图" : "Cover image", coverUrl: isZh ? "封面图 URL" : "Cover image URL", file: isZh ? "内容文件" : "Content file", fileUrl: isZh ? "电子书 PDF/EPUB 或视频直链" : "PDF/EPUB or direct video URL", chapters: isZh ? "目录 / 章节" : "Chapters", chaptersHint: isZh ? "每行一个章节,可选" : "One chapter per line, optional", uploadCover: isZh ? "上传封面" : "Upload cover", uploadFile: isZh ? "上传文件" : "Upload file", uploading: isZh ? "上传中..." : "Uploading...", submit: isZh ? "提交审核" : "Submit for review", submitting: isZh ? "提交中..." : "Submitting...", success: isZh ? "已提交,审核通过后会展示在首页。" : "Submitted. It will appear after review.", required: isZh ? "请填写标题、简介、封面、内容文件和联系邮箱。" : "Please complete title, description, cover, file and email.", uploadFailed: isZh ? "上传失败" : "Upload failed", fileTooLarge: isZh ? "文件过大" : "File too large", review: isZh ? "状态:待审核" : "Status: pending review", }; }, [isZh, submissionType]); const [form, setForm] = useState({ ...EMPTY_FORM, type: initialType }); const [uploading, setUploading] = useState<"coverImage" | "mediaUrl" | null>(null); const [submitting, setSubmitting] = useState(false); const [message, setMessage] = useState(""); const [error, setError] = useState(""); useEffect(() => { if (open) { setMessage(""); setError(""); setForm((prev) => ({ ...prev, type: initialType })); } }, [open, initialType]); if (!open) return null; const set = (key: K, value: FormState[K]) => { setForm((prev) => ({ ...prev, [key]: value })); setError(""); }; const mediaMaxSize = form.type === "video" ? 200 * MB : 100 * MB; const contentAccept = form.type === "video" ? "video/mp4,video/webm,video/quicktime" : "application/pdf,application/epub+zip,.pdf,.epub,.mobi,.azw3"; async function uploadFile(event: ChangeEvent, target: "coverImage" | "mediaUrl") { const file = event.currentTarget.files?.[0]; event.currentTarget.value = ""; if (!file) return; const limit = target === "coverImage" ? 20 * MB : mediaMaxSize; if (file.size > limit) { setError(`${copy.fileTooLarge},最大 ${Math.round(limit / MB)}MB`); return; } setUploading(target); setError(""); try { const data = new FormData(); data.append("file", file); const res = await fetch(apiUrl("/api/upload-media?purpose=content"), { method: "POST", body: data, credentials: "include", }); const json = (await res.json().catch(() => ({}))) as { ok?: boolean; url?: string; detail?: string; error?: string }; if (!res.ok || !json.ok || !json.url) { throw new Error(json.detail || json.error || copy.uploadFailed); } setForm((prev) => ({ ...prev, [target]: json.url || "" })); } catch (err) { setError(err instanceof Error ? err.message : copy.uploadFailed); } finally { setUploading(null); } } async function submit(event: FormEvent) { event.preventDefault(); const title = form.title.trim(); const description = form.description.trim(); const coverImage = form.coverImage.trim(); const mediaUrl = form.mediaUrl.trim(); const authorEmail = form.authorEmail.trim(); if (!title || !description || !coverImage || !mediaUrl || !authorEmail) { setError(copy.required); return; } setSubmitting(true); setError(""); setMessage(""); try { await apiFetch("/api/content/submissions", { method: "POST", body: JSON.stringify({ type: form.type, title, subtitle: form.subtitle.trim(), description, coverImage, mediaUrl, authorName: form.authorName.trim(), authorEmail, chapters: form.chaptersText .split("\n") .map((line) => line.trim()) .filter(Boolean) .map((line) => ({ title: line })), }), }); setMessage(copy.success); setForm({ ...EMPTY_FORM, type: initialType }); } catch (err) { setError(err instanceof Error ? err.message : "提交失败"); } finally { setSubmitting(false); } } const busy = submitting || Boolean(uploading); return (

{copy.title}

{copy.review}

{!submissionType && (
{(["ebook", "video"] as SubmissionType[]).map((type) => ( ))}
)}