fix:handle empty file placement & target publish spit
This commit is contained in:
parent
933bbc402b
commit
504101b91c
|
|
@ -42,7 +42,7 @@ import {
|
|||
Eye,
|
||||
Settings,
|
||||
CheckCircle,
|
||||
XCircle
|
||||
XCircle,
|
||||
} from "lucide-react";
|
||||
|
||||
// Swiper
|
||||
|
|
@ -62,10 +62,7 @@ import {
|
|||
getTagsBySubCategoryId,
|
||||
listEnableCategory,
|
||||
} from "@/service/content/content";
|
||||
import {
|
||||
generateDataRewrite,
|
||||
getDetailArticle,
|
||||
} from "@/service/content/ai";
|
||||
import { generateDataRewrite, getDetailArticle } from "@/service/content/ai";
|
||||
|
||||
// Utils
|
||||
import { getCookiesDecrypt } from "@/lib/utils";
|
||||
|
|
@ -157,7 +154,7 @@ const CustomEditor = dynamic(
|
|||
<Loader2 className="h-6 w-6 animate-spin" />
|
||||
<span className="ml-2">Loading editor...</span>
|
||||
</div>
|
||||
)
|
||||
),
|
||||
}
|
||||
);
|
||||
|
||||
|
|
@ -190,9 +187,14 @@ export default function FormConvertSPIT() {
|
|||
const [isDeleting, setIsDeleting] = useState(false);
|
||||
const [detail, setDetail] = useState<Detail | null>(null);
|
||||
const [categories, setCategories] = useState<Category[]>([]);
|
||||
const [selectedCategoryId, setSelectedCategoryId] = useState<number | null>(null);
|
||||
const [selectedFileType, setSelectedFileType] = useState<"original" | "rewrite">("original");
|
||||
const [selectedWritingStyle, setSelectedWritingStyle] = useState("professional");
|
||||
const [selectedCategoryId, setSelectedCategoryId] = useState<number | null>(
|
||||
null
|
||||
);
|
||||
const [selectedFileType, setSelectedFileType] = useState<
|
||||
"original" | "rewrite"
|
||||
>("original");
|
||||
const [selectedWritingStyle, setSelectedWritingStyle] =
|
||||
useState("professional");
|
||||
const [showRewriteEditor, setShowRewriteEditor] = useState(false);
|
||||
const [isGeneratingRewrite, setIsGeneratingRewrite] = useState(false);
|
||||
const [isLoadingRewrite, setIsLoadingRewrite] = useState(false);
|
||||
|
|
@ -205,7 +207,9 @@ export default function FormConvertSPIT() {
|
|||
|
||||
// Content rewrite state
|
||||
const [articleIds, setArticleIds] = useState<string[]>([]);
|
||||
const [selectedArticleId, setSelectedArticleId] = useState<string | null>(null);
|
||||
const [selectedArticleId, setSelectedArticleId] = useState<string | null>(
|
||||
null
|
||||
);
|
||||
const [articleBody, setArticleBody] = useState<string>("");
|
||||
|
||||
// Form data state
|
||||
|
|
@ -281,7 +285,7 @@ export default function FormConvertSPIT() {
|
|||
try {
|
||||
const response = await getTagsBySubCategoryId(categoryId);
|
||||
if (response?.data?.data?.length > 0) {
|
||||
const tagsMerge = [...tags, response?.data?.data]
|
||||
const tagsMerge = [...tags, response?.data?.data];
|
||||
setTags(tagsMerge);
|
||||
}
|
||||
} catch (error) {
|
||||
|
|
@ -301,8 +305,8 @@ export default function FormConvertSPIT() {
|
|||
setDetail(details);
|
||||
setFiles(details.contentList || []);
|
||||
setDetailThumb(
|
||||
(details.contentList || []).map((file: FileType) =>
|
||||
file.contentFile || "default-image.jpg"
|
||||
(details.contentList || []).map(
|
||||
(file: FileType) => file.contentFile || "default-image.jpg"
|
||||
)
|
||||
);
|
||||
|
||||
|
|
@ -314,7 +318,10 @@ export default function FormConvertSPIT() {
|
|||
setValue("contentTitle", details.contentTitle || "");
|
||||
setValue("contentDescription", details.contentDescription || "");
|
||||
setValue("contentCreator", details.contentCreator || "");
|
||||
setValue("contentRewriteDescription", details.contentRewriteDescription || "");
|
||||
setValue(
|
||||
"contentRewriteDescription",
|
||||
details.contentRewriteDescription || ""
|
||||
);
|
||||
|
||||
// Set category
|
||||
if (details.categoryId) {
|
||||
|
|
@ -324,7 +331,9 @@ export default function FormConvertSPIT() {
|
|||
|
||||
// Set tags
|
||||
if (details.contentTag) {
|
||||
const initialTags = details.contentTag.split(",").map((tag: string) => tag.trim());
|
||||
const initialTags = details.contentTag
|
||||
.split(",")
|
||||
.map((tag: string) => tag.trim());
|
||||
setTags(initialTags);
|
||||
}
|
||||
} catch (error) {
|
||||
|
|
@ -371,7 +380,7 @@ export default function FormConvertSPIT() {
|
|||
}
|
||||
|
||||
const newArticleId = response?.data?.data?.id;
|
||||
setArticleIds(prev => {
|
||||
setArticleIds((prev) => {
|
||||
const updated = [...prev];
|
||||
if (updated.length < 3) {
|
||||
updated.push(newArticleId);
|
||||
|
|
@ -414,14 +423,17 @@ export default function FormConvertSPIT() {
|
|||
const articleData = response?.data?.data;
|
||||
|
||||
if (articleData?.status === 2) {
|
||||
const cleanArticleBody = articleData.articleBody?.replace(/<img[^>]*>/g, "");
|
||||
const cleanArticleBody = articleData.articleBody?.replace(
|
||||
/<img[^>]*>/g,
|
||||
""
|
||||
);
|
||||
setArticleBody(cleanArticleBody || "");
|
||||
setValue("contentRewriteDescription", cleanArticleBody || "");
|
||||
break;
|
||||
}
|
||||
|
||||
retryCount++;
|
||||
await new Promise(resolve => setTimeout(resolve, 5000));
|
||||
await new Promise((resolve) => setTimeout(resolve, 5000));
|
||||
}
|
||||
|
||||
if (retryCount >= maxRetries) {
|
||||
|
|
@ -446,7 +458,7 @@ export default function FormConvertSPIT() {
|
|||
const newTag = inputRef.current.value.trim();
|
||||
|
||||
if (!tags.includes(newTag)) {
|
||||
setTags(prev => [...prev, newTag]);
|
||||
setTags((prev) => [...prev, newTag]);
|
||||
}
|
||||
|
||||
inputRef.current.value = "";
|
||||
|
|
@ -454,27 +466,33 @@ export default function FormConvertSPIT() {
|
|||
};
|
||||
|
||||
const handleRemoveTag = (index: number) => {
|
||||
setTags(prev => prev.filter((_, i) => i !== index));
|
||||
setTags((prev) => prev.filter((_, i) => i !== index));
|
||||
};
|
||||
|
||||
const handlePublishTargetChange = (optionId: string) => {
|
||||
if (optionId === "all") {
|
||||
setPublishedFor(prev =>
|
||||
prev.length === PUBLISH_OPTIONS.filter(opt => opt.id !== "all").length
|
||||
setPublishedFor((prev) =>
|
||||
prev.length === PUBLISH_OPTIONS.filter((opt) => opt.id !== "all").length
|
||||
? []
|
||||
: PUBLISH_OPTIONS.filter(opt => opt.id !== "all").map(opt => opt.id)
|
||||
: PUBLISH_OPTIONS.filter((opt) => opt.id !== "all").map(
|
||||
(opt) => opt.id
|
||||
)
|
||||
);
|
||||
} else {
|
||||
setPublishedFor(prev =>
|
||||
setPublishedFor((prev) =>
|
||||
prev.includes(optionId)
|
||||
? prev.filter(id => id !== optionId && id !== "all")
|
||||
: [...prev.filter(id => id !== "all"), optionId]
|
||||
? prev.filter((id) => id !== optionId && id !== "all")
|
||||
: [...prev.filter((id) => id !== "all"), optionId]
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const handleFilePlacementChange = (fileIndex: number, placement: string, checked: boolean) => {
|
||||
setFilePlacements(prev => {
|
||||
const handleFilePlacementChange = (
|
||||
fileIndex: number,
|
||||
placement: string,
|
||||
checked: boolean
|
||||
) => {
|
||||
setFilePlacements((prev) => {
|
||||
const updated = [...prev];
|
||||
const currentPlacements = updated[fileIndex] || [];
|
||||
|
||||
|
|
@ -492,9 +510,11 @@ export default function FormConvertSPIT() {
|
|||
if (placement === "all") {
|
||||
updated[fileIndex] = [];
|
||||
} else {
|
||||
const newPlacements = currentPlacements.filter(p => p !== placement);
|
||||
const newPlacements = currentPlacements.filter(
|
||||
(p) => p !== placement
|
||||
);
|
||||
if (newPlacements.length === 3 && newPlacements.includes("all")) {
|
||||
updated[fileIndex] = newPlacements.filter(p => p !== "all");
|
||||
updated[fileIndex] = newPlacements.filter((p) => p !== "all");
|
||||
} else {
|
||||
updated[fileIndex] = newPlacements;
|
||||
}
|
||||
|
|
@ -506,11 +526,11 @@ export default function FormConvertSPIT() {
|
|||
};
|
||||
|
||||
const handleSelectAllPlacements = (placement: string, checked: boolean) => {
|
||||
setFilePlacements(prev =>
|
||||
prev.map(filePlacements =>
|
||||
setFilePlacements((prev) =>
|
||||
prev.map((filePlacements) =>
|
||||
checked
|
||||
? Array.from(new Set([...filePlacements, placement]))
|
||||
: filePlacements.filter(p => p !== placement)
|
||||
: filePlacements.filter((p) => p !== placement)
|
||||
)
|
||||
);
|
||||
};
|
||||
|
|
@ -530,12 +550,35 @@ export default function FormConvertSPIT() {
|
|||
return placementData;
|
||||
};
|
||||
|
||||
const checkPlacement = (data: any) => {
|
||||
let temp = true;
|
||||
for (const element of data) {
|
||||
if (element.length < 1) {
|
||||
temp = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return temp;
|
||||
};
|
||||
|
||||
// Form submission
|
||||
const onSubmit = async (data: FormData) => {
|
||||
if (!checkPlacement(filePlacements)) {
|
||||
error("Select File Placement");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (publishedFor.length < 1) {
|
||||
error("Select Publish Target");
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
setIsSaving(true);
|
||||
|
||||
const description = selectedFileType === "original"
|
||||
const description =
|
||||
selectedFileType === "original"
|
||||
? data.contentDescription
|
||||
: data.contentRewriteDescription;
|
||||
|
||||
|
|
@ -630,17 +673,15 @@ export default function FormConvertSPIT() {
|
|||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold tracking-tight">SPIT Convert Form</h1>
|
||||
<h1 className="text-3xl font-bold tracking-tight">
|
||||
SPIT Convert Form
|
||||
</h1>
|
||||
<p className="text-muted-foreground">
|
||||
Convert and manage your SPIT content
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => router.back()}
|
||||
>
|
||||
<Button variant="outline" size="sm" onClick={() => router.back()}>
|
||||
<XCircle className="h-4 w-4 mr-2" />
|
||||
Cancel
|
||||
</Button>
|
||||
|
|
@ -734,7 +775,9 @@ export default function FormConvertSPIT() {
|
|||
<CardContent className="space-y-4">
|
||||
<RadioGroup
|
||||
value={selectedFileType}
|
||||
onValueChange={(value: "original" | "rewrite") => setSelectedFileType(value)}
|
||||
onValueChange={(value: "original" | "rewrite") =>
|
||||
setSelectedFileType(value)
|
||||
}
|
||||
className="grid grid-cols-2 gap-4"
|
||||
>
|
||||
<div className="flex items-center space-x-2">
|
||||
|
|
@ -789,7 +832,9 @@ export default function FormConvertSPIT() {
|
|||
<Button
|
||||
type="button"
|
||||
onClick={handleRewriteClick}
|
||||
disabled={isGeneratingRewrite || !detail?.contentDescription}
|
||||
disabled={
|
||||
isGeneratingRewrite || !detail?.contentDescription
|
||||
}
|
||||
className="bg-blue-600 hover:bg-blue-700"
|
||||
>
|
||||
{isGeneratingRewrite ? (
|
||||
|
|
@ -809,12 +854,17 @@ export default function FormConvertSPIT() {
|
|||
<Button
|
||||
key={articleId}
|
||||
type="button"
|
||||
variant={selectedArticleId === articleId ? "default" : "outline"}
|
||||
variant={
|
||||
selectedArticleId === articleId
|
||||
? "default"
|
||||
: "outline"
|
||||
}
|
||||
size="sm"
|
||||
onClick={() => handleArticleSelect(articleId)}
|
||||
disabled={isLoadingRewrite}
|
||||
>
|
||||
{isLoadingRewrite && selectedArticleId === articleId && (
|
||||
{isLoadingRewrite &&
|
||||
selectedArticleId === articleId && (
|
||||
<Loader2 className="h-4 w-4 mr-2 animate-spin" />
|
||||
)}
|
||||
Narrative {index + 1}
|
||||
|
|
@ -906,14 +956,23 @@ export default function FormConvertSPIT() {
|
|||
{files.length > 1 && (
|
||||
<div className="flex flex-wrap gap-4 p-4 bg-muted/50 rounded-lg">
|
||||
{PLACEMENT_OPTIONS.map((option) => (
|
||||
<div key={option.value} className="flex items-center space-x-2">
|
||||
<div
|
||||
key={option.value}
|
||||
className="flex items-center space-x-2"
|
||||
>
|
||||
<Checkbox
|
||||
id={`select-all-${option.value}`}
|
||||
onCheckedChange={(checked) =>
|
||||
handleSelectAllPlacements(option.value, Boolean(checked))
|
||||
handleSelectAllPlacements(
|
||||
option.value,
|
||||
Boolean(checked)
|
||||
)
|
||||
}
|
||||
/>
|
||||
<Label htmlFor={`select-all-${option.value}`} className="text-sm">
|
||||
<Label
|
||||
htmlFor={`select-all-${option.value}`}
|
||||
className="text-sm"
|
||||
>
|
||||
All {option.label}
|
||||
</Label>
|
||||
</div>
|
||||
|
|
@ -936,12 +995,21 @@ export default function FormConvertSPIT() {
|
|||
<p className="font-medium text-sm">{file.fileName}</p>
|
||||
<div className="flex flex-wrap gap-3">
|
||||
{PLACEMENT_OPTIONS.map((option) => (
|
||||
<div key={option.value} className="flex items-center space-x-2">
|
||||
<div
|
||||
key={option.value}
|
||||
className="flex items-center space-x-2"
|
||||
>
|
||||
<Checkbox
|
||||
id={`${file.contentId}-${option.value}`}
|
||||
checked={filePlacements[index]?.includes(option.value)}
|
||||
checked={filePlacements[index]?.includes(
|
||||
option.value
|
||||
)}
|
||||
onCheckedChange={(checked) =>
|
||||
handleFilePlacementChange(index, option.value, Boolean(checked))
|
||||
handleFilePlacementChange(
|
||||
index,
|
||||
option.value,
|
||||
Boolean(checked)
|
||||
)
|
||||
}
|
||||
/>
|
||||
<Label
|
||||
|
|
@ -1067,10 +1135,14 @@ export default function FormConvertSPIT() {
|
|||
id={option.id}
|
||||
checked={
|
||||
option.id === "all"
|
||||
? publishedFor.length === PUBLISH_OPTIONS.filter(opt => opt.id !== "all").length
|
||||
? publishedFor.length ===
|
||||
PUBLISH_OPTIONS.filter((opt) => opt.id !== "all")
|
||||
.length
|
||||
: publishedFor.includes(option.id)
|
||||
}
|
||||
onCheckedChange={() => handlePublishTargetChange(option.id)}
|
||||
onCheckedChange={() =>
|
||||
handlePublishTargetChange(option.id)
|
||||
}
|
||||
/>
|
||||
<Label htmlFor={option.id} className="text-sm">
|
||||
{option.label}
|
||||
|
|
|
|||
Loading…
Reference in New Issue