feat(overview) : add YouTube video section
This commit is contained in:
@@ -7,23 +7,28 @@ import { saveGuideOverview } from "@/app/admin/actions";
|
||||
import { AutosaveStatus } from "@/components/admin/autosave-status";
|
||||
import { MediaUploader } from "@/components/admin/media-uploader";
|
||||
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Field, FieldDescription, FieldGroup, FieldLabel } from "@/components/ui/field";
|
||||
import { Field, FieldDescription, FieldError, FieldGroup, FieldLabel } from "@/components/ui/field";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { useAutosave } from "@/hooks/use-autosave";
|
||||
import { adminDraftKey } from "@/lib/editor/local-draft";
|
||||
import { parseYouTubeUrl } from "@/lib/guides/youtube";
|
||||
|
||||
export function GuideOverviewForm({ guide }: { guide: { id: string; version: number; overview: string; coverMediaId: string; coverUrl: string; name: string } }) {
|
||||
export function GuideOverviewForm({ guide }: { guide: { id: string; version: number; overview: string; youtubeUrl: string | null; coverMediaId: string; coverUrl: string; name: string } }) {
|
||||
const [overview, setOverview] = useState(guide.overview);
|
||||
const [youtubeUrl, setYoutubeUrl] = useState(guide.youtubeUrl ?? "");
|
||||
const [cover, setCover] = useState({ id: guide.coverMediaId, url: guide.coverUrl });
|
||||
const value = { coverMediaId: cover.id, overview };
|
||||
const invalidYoutubeUrl = Boolean(youtubeUrl.trim() && !parseYouTubeUrl(youtubeUrl.trim()));
|
||||
const value = { coverMediaId: cover.id, overview, youtubeUrl };
|
||||
const incompleteIssues = [
|
||||
!cover.id ? "กรุณาอัปโหลด Guide Image" : null,
|
||||
!overview.trim() ? "กรุณากรอก Overview" : null,
|
||||
invalidYoutubeUrl ? "กรุณาใส่ลิงก์วิดีโอ YouTube ที่ถูกต้อง" : null,
|
||||
].filter((issue): issue is string => Boolean(issue));
|
||||
const localDraft = { overview, cover };
|
||||
const localDraft = { overview, youtubeUrl, cover };
|
||||
const autosave = useAutosave({
|
||||
value,
|
||||
valid: Boolean(cover.id && overview.trim()),
|
||||
valid: Boolean(cover.id && overview.trim() && !invalidYoutubeUrl),
|
||||
initialVersion: guide.version,
|
||||
persist: (next, expectedVersion) => saveGuideOverview(guide.id, { ...next, expectedVersion }),
|
||||
storageKey: adminDraftKey(guide.id, "overview"),
|
||||
@@ -33,12 +38,19 @@ export function GuideOverviewForm({ guide }: { guide: { id: string; version: num
|
||||
const draft = candidate as Record<string, unknown>;
|
||||
if (typeof draft.overview !== "string" || !draft.cover || typeof draft.cover !== "object") return null;
|
||||
const draftCover = draft.cover as Record<string, unknown>;
|
||||
return typeof draftCover.id === "string" && typeof draftCover.url === "string" ? candidate as typeof localDraft : null;
|
||||
if (typeof draftCover.id !== "string" || typeof draftCover.url !== "string") return null;
|
||||
if (draft.youtubeUrl !== undefined && typeof draft.youtubeUrl !== "string") return null;
|
||||
return {
|
||||
overview: draft.overview,
|
||||
youtubeUrl: typeof draft.youtubeUrl === "string" ? draft.youtubeUrl : "",
|
||||
cover: { id: draftCover.id, url: draftCover.url },
|
||||
};
|
||||
},
|
||||
restoreLocalDraft: (draft) => {
|
||||
setOverview(draft.overview);
|
||||
setYoutubeUrl(draft.youtubeUrl);
|
||||
setCover(draft.cover);
|
||||
},
|
||||
});
|
||||
return <Card><CardHeader><CardTitle>Overview</CardTitle><CardDescription>ภาพและข้อความนี้แสดงบนหน้าแรกและส่วนแนะนำของ Guide</CardDescription></CardHeader><CardContent><FieldGroup><Field><FieldLabel>Guide Image</FieldLabel><div className="relative mx-auto aspect-square w-full max-w-md overflow-hidden rounded-lg border"><Image src={cover.url} alt={`Guide ${guide.name}`} fill className="object-cover" /></div><MediaUploader onReady={(asset) => setCover(asset)} /></Field><Field><FieldLabel htmlFor="guide-overview">Overview</FieldLabel><Textarea id="guide-overview" value={overview} maxLength={500} onChange={(event) => setOverview(event.target.value)} /><FieldDescription>{overview.length}/500 ตัวอักษร</FieldDescription></Field></FieldGroup></CardContent><CardFooter className="justify-end"><AutosaveStatus state={autosave.state} issues={incompleteIssues} onSave={autosave.saveNow} /></CardFooter></Card>;
|
||||
return <Card><CardHeader><CardTitle>Overview</CardTitle><CardDescription>ภาพ ข้อความ และวิดีโอนี้แสดงบนหน้าแรกของ Guide</CardDescription></CardHeader><CardContent><FieldGroup><Field><FieldLabel>Guide Image</FieldLabel><div className="relative mx-auto aspect-square w-full max-w-md overflow-hidden rounded-lg border"><Image src={cover.url} alt={`Guide ${guide.name}`} fill className="object-cover" /></div><MediaUploader onReady={(asset) => setCover(asset)} /></Field><Field><FieldLabel htmlFor="guide-overview">Overview</FieldLabel><Textarea id="guide-overview" value={overview} maxLength={500} onChange={(event) => setOverview(event.target.value)} /><FieldDescription>{overview.length}/500 ตัวอักษร</FieldDescription></Field><Field data-invalid={invalidYoutubeUrl}><FieldLabel htmlFor="guide-youtube-url">YouTube video link</FieldLabel><Input id="guide-youtube-url" type="url" inputMode="url" value={youtubeUrl} maxLength={2_048} placeholder="https://www.youtube.com/watch?v=..." aria-invalid={invalidYoutubeUrl} onChange={(event) => setYoutubeUrl(event.target.value)} /><FieldDescription>ไม่บังคับ รองรับลิงก์ YouTube, youtu.be, Shorts และ Live</FieldDescription>{invalidYoutubeUrl ? <FieldError>กรุณาใส่ลิงก์วิดีโอ YouTube ที่ถูกต้อง</FieldError> : null}</Field></FieldGroup></CardContent><CardFooter className="justify-end"><AutosaveStatus state={autosave.state} issues={incompleteIssues} onSave={autosave.saveNow} /></CardFooter></Card>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user