Merge branch 'dev-anang' of https://gitlab.com/hanifsalafi/mediahub_redesign
This commit is contained in:
commit
608afb32c2
|
|
@ -12,32 +12,22 @@ import { Calendar } from "@/components/ui/calendar";
|
||||||
import { Card, CardContent, CardHeader } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader } from "@/components/ui/card";
|
||||||
import { Plus } from "lucide-react";
|
import { Plus } from "lucide-react";
|
||||||
import { Checkbox } from "@/components/ui/checkbox";
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
import { CalendarCategory } from "./data";
|
|
||||||
import { EventContentArg } from "@fullcalendar/core";
|
import { EventContentArg } from "@fullcalendar/core";
|
||||||
import EventModal from "./event-modal";
|
import EventModal from "./event-modal";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import { getAgendaSettingsList } from "@/service/agenda-setting/agenda-setting";
|
import { getAgendaSettingsList } from "@/service/agenda-setting/agenda-setting";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { getCookiesDecrypt } from "@/lib/utils";
|
import { getCookiesDecrypt } from "@/lib/utils";
|
||||||
|
import { CalendarCategory } from "./data";
|
||||||
|
|
||||||
const wait = () => new Promise((resolve) => setTimeout(resolve, 1000));
|
const wait = () => new Promise((resolve) => setTimeout(resolve, 1000));
|
||||||
interface CalendarViewProps {
|
interface CalendarViewProps {
|
||||||
events: CalendarEvent[];
|
// events: CalendarEvent[];
|
||||||
categories: CalendarCategory[];
|
categories: CalendarCategory[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const INITIAL_YEAR = dayjs().format("YYYY");
|
const INITIAL_YEAR = dayjs().format("YYYY");
|
||||||
const INITIAL_MONTH = dayjs().format("M");
|
const INITIAL_MONTH = dayjs().format("M");
|
||||||
export interface CalendarEvent {
|
|
||||||
id: string;
|
|
||||||
title: string;
|
|
||||||
start: Date;
|
|
||||||
end: Date;
|
|
||||||
allDay: boolean;
|
|
||||||
extendedProps: {
|
|
||||||
calendar: string;
|
|
||||||
description?: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AgendaSettingsAPIResponse {
|
export interface AgendaSettingsAPIResponse {
|
||||||
id: number;
|
id: number;
|
||||||
|
|
@ -59,14 +49,11 @@ interface APIResponse {
|
||||||
data: AgendaSettingsAPIResponse[] | null; // `data` bisa berupa array atau null
|
data: AgendaSettingsAPIResponse[] | null; // `data` bisa berupa array atau null
|
||||||
}
|
}
|
||||||
|
|
||||||
const CalendarView = ({ events, categories }: CalendarViewProps) => {
|
const CalendarView = ({ categories }: CalendarViewProps) => {
|
||||||
const [selectedCategory, setSelectedCategory] = useState<string[] | null>(
|
const [selectedCategory, setSelectedCategory] = useState<string[] | null>(
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
const [selectedEventDate, setSelectedEventDate] = useState<Date | null>(null);
|
const [selectedEventDate, setSelectedEventDate] = useState<Date | null>(null);
|
||||||
// const [selectedEvent, setSelectedEvent] = useState<CalendarEvent | null>(
|
|
||||||
// null
|
|
||||||
// );
|
|
||||||
const roleId = Number(getCookiesDecrypt("urie")) || 0;
|
const roleId = Number(getCookiesDecrypt("urie")) || 0;
|
||||||
const [apiEvents, setApiEvents] = useState<CalendarEvent[]>([]);
|
const [apiEvents, setApiEvents] = useState<CalendarEvent[]>([]);
|
||||||
const [loading, setLoading] = useState<boolean>(false);
|
const [loading, setLoading] = useState<boolean>(false);
|
||||||
|
|
@ -84,36 +71,47 @@ const CalendarView = ({ events, categories }: CalendarViewProps) => {
|
||||||
{ title: "Create New theme", id: "104", tag: "etc" },
|
{ title: "Create New theme", id: "104", tag: "etc" },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const [calendarEvents, setCalendarEvents] = useState<CalendarEvent[]>([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getCalendarEvents();
|
getCalendarEvents();
|
||||||
});
|
}, []);
|
||||||
|
|
||||||
const getCalendarEvents = async () => {
|
const getCalendarEvents = async () => {
|
||||||
const res = await getAgendaSettingsList(INITIAL_YEAR, INITIAL_MONTH, "");
|
const res = await getAgendaSettingsList(INITIAL_YEAR, INITIAL_MONTH, "");
|
||||||
console.log("ress", res);
|
console.log("API Response:", res);
|
||||||
|
|
||||||
if (res.error) {
|
if (res.error) {
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Map API data to the calendarEvents structure
|
||||||
|
const events = res?.data?.data.map((event: any) => ({
|
||||||
|
id: event.id.toString(),
|
||||||
|
title: event.title,
|
||||||
|
start: new Date(event.startDate),
|
||||||
|
end: new Date(event.endDate),
|
||||||
|
allDay: true, // Assuming all events are all-day by default
|
||||||
|
extendedProps: {
|
||||||
|
calendar: event.agendaType, // Map agendaType to the calendar category
|
||||||
|
description: event.description,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
setCalendarEvents(events);
|
||||||
|
console.log("event", events);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setSelectedCategory(categories?.map((c) => c.value));
|
setSelectedCategory(categories?.map((c) => c.value));
|
||||||
}, [events, categories]);
|
}, [categories]);
|
||||||
|
|
||||||
useEffect(() => {
|
const filteredEvents = calendarEvents?.filter((event) =>
|
||||||
console.log("Fetched events from API:", apiEvents);
|
|
||||||
}, [apiEvents]);
|
|
||||||
|
|
||||||
const filteredEvents = apiEvents?.filter((event) =>
|
|
||||||
selectedCategory?.includes(event.extendedProps.calendar)
|
selectedCategory?.includes(event.extendedProps.calendar)
|
||||||
);
|
);
|
||||||
|
|
||||||
const displayedEvents =
|
const displayedEvents =
|
||||||
filteredEvents?.length > 0 ? filteredEvents : apiEvents;
|
filteredEvents?.length > 1 ? filteredEvents : apiEvents;
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
console.log("Filtered events based on category:", displayedEvents);
|
|
||||||
}, [filteredEvents, apiEvents]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setSelectedCategory(categories?.map((c) => c.value));
|
setSelectedCategory(categories?.map((c) => c.value));
|
||||||
|
|
@ -151,6 +149,7 @@ const CalendarView = ({ events, categories }: CalendarViewProps) => {
|
||||||
description: item.description,
|
description: item.description,
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
setApiEvents(eventsFromAPI);
|
setApiEvents(eventsFromAPI);
|
||||||
} else {
|
} else {
|
||||||
console.warn("No events found in API response.");
|
console.warn("No events found in API response.");
|
||||||
|
|
@ -167,6 +166,14 @@ const CalendarView = ({ events, categories }: CalendarViewProps) => {
|
||||||
fetchAgendaEvents();
|
fetchAgendaEvents();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
console.log("Fetched events from API 1:", apiEvents);
|
||||||
|
}, [apiEvents]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
console.log("Filtered events based on category 1:", calendarEvents);
|
||||||
|
}, [filteredEvents, apiEvents]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const draggableEl = document.getElementById("external-events");
|
const draggableEl = document.getElementById("external-events");
|
||||||
|
|
||||||
|
|
@ -247,17 +254,16 @@ const CalendarView = ({ events, categories }: CalendarViewProps) => {
|
||||||
<div className="grid grid-cols-12 gap-6 divide-x divide-border">
|
<div className="grid grid-cols-12 gap-6 divide-x divide-border">
|
||||||
<Card className="col-span-12 lg:col-span-4 2xl:col-span-3 pb-5">
|
<Card className="col-span-12 lg:col-span-4 2xl:col-span-3 pb-5">
|
||||||
<CardContent className="p-0">
|
<CardContent className="p-0">
|
||||||
{roleId === 11 && (
|
<CardHeader className="border-none mb-2 pt-5">
|
||||||
<CardHeader className="border-none mb-2 pt-5">
|
<Button
|
||||||
<Button
|
onClick={handleDateClick}
|
||||||
onClick={handleDateClick}
|
className="dark:bg-background dark:text-foreground"
|
||||||
className="dark:bg-background dark:text-foreground"
|
>
|
||||||
>
|
<Plus className="w-4 h-4 me-1" />
|
||||||
<Plus className="w-4 h-4 me-1" />
|
{"Tambahkan Agenda baru"}
|
||||||
{"Tambahkan Agenda baru"}
|
</Button>
|
||||||
</Button>
|
</CardHeader>
|
||||||
</CardHeader>
|
|
||||||
)}
|
|
||||||
<div className="px-3">
|
<div className="px-3">
|
||||||
<Calendar
|
<Calendar
|
||||||
mode="single"
|
mode="single"
|
||||||
|
|
@ -321,7 +327,7 @@ const CalendarView = ({ events, categories }: CalendarViewProps) => {
|
||||||
headerToolbar={{
|
headerToolbar={{
|
||||||
left: "prev,next today",
|
left: "prev,next today",
|
||||||
center: "title",
|
center: "title",
|
||||||
right: "dayGridMonth,timeGridWeek,timeGridDay,listWeek",
|
right: "dayGridMonth,listWeek,",
|
||||||
}}
|
}}
|
||||||
events={displayedEvents}
|
events={displayedEvents}
|
||||||
editable={true}
|
editable={true}
|
||||||
|
|
@ -352,3 +358,15 @@ const CalendarView = ({ events, categories }: CalendarViewProps) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export default CalendarView;
|
export default CalendarView;
|
||||||
|
|
||||||
|
export type CalendarEvent = {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
start: Date;
|
||||||
|
end: Date;
|
||||||
|
allDay: boolean;
|
||||||
|
extendedProps: {
|
||||||
|
calendar: string;
|
||||||
|
description: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -13,15 +13,6 @@ const nextMonth = date.getMonth() === 11 ? new Date(date.getFullYear() + 1, 0, 1
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
const prevMonth = date.getMonth() === 11 ? new Date(date.getFullYear() - 1, 0, 1) : new Date(date.getFullYear(), date.getMonth() - 1, 1)
|
const prevMonth = date.getMonth() === 11 ? new Date(date.getFullYear() - 1, 0, 1) : new Date(date.getFullYear(), date.getMonth() - 1, 1)
|
||||||
|
|
||||||
export const getCalendarEvents = async () => {
|
|
||||||
const res = await getAgendaSettingsList(INITIAL_YEAR, INITIAL_MONTH, "");
|
|
||||||
if (res.error) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
console.log("ress", res.data.data);
|
|
||||||
return res?.data?.data;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const calendarEvents = [
|
export const calendarEvents = [
|
||||||
{
|
{
|
||||||
id: faker.string.uuid(),
|
id: faker.string.uuid(),
|
||||||
|
|
@ -94,7 +85,7 @@ export const calendarEvents = [
|
||||||
export const calendarCategories = [
|
export const calendarCategories = [
|
||||||
{
|
{
|
||||||
label: "Nasional",
|
label: "Nasional",
|
||||||
value: "national",
|
value: "mabes",
|
||||||
activeClass: "ring-primary-500 bg-primary-500",
|
activeClass: "ring-primary-500 bg-primary-500",
|
||||||
className: "group-hover:border-blue-500",
|
className: "group-hover:border-blue-500",
|
||||||
},
|
},
|
||||||
|
|
@ -127,7 +118,7 @@ export const calendarCategories = [
|
||||||
export const categories = [
|
export const categories = [
|
||||||
{
|
{
|
||||||
label: "Nasional",
|
label: "Nasional",
|
||||||
value: "national",
|
value: "mabes",
|
||||||
className:
|
className:
|
||||||
"data-[state=checked]:bg-primary data-[state=checked]:ring-primary",
|
"data-[state=checked]:bg-primary data-[state=checked]:ring-primary",
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -135,7 +135,7 @@ const EventModal = ({
|
||||||
|
|
||||||
const reqData = {
|
const reqData = {
|
||||||
title: data.title,
|
title: data.title,
|
||||||
description: data.description || "",
|
description: data.description,
|
||||||
agendaType: calendarProps,
|
agendaType: calendarProps,
|
||||||
startDate: format(startDate, "yyyy-MM-dd"),
|
startDate: format(startDate, "yyyy-MM-dd"),
|
||||||
endDate: format(endDate, "yyyy-MM-dd"),
|
endDate: format(endDate, "yyyy-MM-dd"),
|
||||||
|
|
@ -162,7 +162,7 @@ const EventModal = ({
|
||||||
confirmButtonColor: "#3085d6",
|
confirmButtonColor: "#3085d6",
|
||||||
confirmButtonText: "OK",
|
confirmButtonText: "OK",
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
router.push("/contributor/agenda-setting");
|
router.push("en/contributor/agenda-setting");
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -272,11 +272,18 @@ const EventModal = ({
|
||||||
<div className="space-y-4 pb-5 ">
|
<div className="space-y-4 pb-5 ">
|
||||||
<div className="space-y-1.5">
|
<div className="space-y-1.5">
|
||||||
<Label htmlFor="title">Judul Agenda</Label>
|
<Label htmlFor="title">Judul Agenda</Label>
|
||||||
<Input
|
<Controller
|
||||||
id="title"
|
control={control}
|
||||||
type="text"
|
name="title"
|
||||||
placeholder="Enter Event Name"
|
render={({ field }) => (
|
||||||
{...register("title")}
|
<Input
|
||||||
|
size="md"
|
||||||
|
type="text"
|
||||||
|
value={field.value}
|
||||||
|
onChange={field.onChange}
|
||||||
|
placeholder="Enter Title"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
{errors?.title?.message && (
|
{errors?.title?.message && (
|
||||||
<div className="text-destructive text-sm">
|
<div className="text-destructive text-sm">
|
||||||
|
|
@ -475,10 +482,16 @@ const EventModal = ({
|
||||||
)}
|
)}
|
||||||
<div className="space-y-1.5">
|
<div className="space-y-1.5">
|
||||||
<Label htmlFor="description">Isi Agenda Setting</Label>
|
<Label htmlFor="description">Isi Agenda Setting</Label>
|
||||||
<Textarea
|
<Controller
|
||||||
id="description"
|
control={control}
|
||||||
placeholder="Enter Event Name"
|
name="description"
|
||||||
{...register("description")}
|
render={({ field }) => (
|
||||||
|
<Textarea
|
||||||
|
defaultValue={field.value}
|
||||||
|
onChange={field.onChange}
|
||||||
|
placeholder="Enter Title"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
{errors?.description?.message && (
|
{errors?.description?.message && (
|
||||||
<div className="text-destructive text-sm">
|
<div className="text-destructive text-sm">
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { getEvents, getCategories } from "./utils";
|
import { getEvents, getCategories } from "./utils";
|
||||||
import { Category } from "./data";
|
import { calendarEvents, Category } from "./data";
|
||||||
import CalendarView from "./calender-view";
|
import CalendarView from "./calender-view";
|
||||||
|
|
||||||
const CalenderPage = async () => {
|
const CalenderPage = async () => {
|
||||||
|
|
@ -11,7 +11,7 @@ const CalenderPage = async () => {
|
||||||
}));
|
}));
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<CalendarView events={events} categories={formattedCategories} />
|
<CalendarView categories={formattedCategories} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -63,12 +63,12 @@ const ReactTableImagePage = () => {
|
||||||
Unggah Foto
|
Unggah Foto
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
<Link href={"/contributor/content/image/createAi"}>
|
{/* <Link href={"/contributor/content/image/createAi"}>
|
||||||
<Button color="primary" className="text-white ml-3">
|
<Button color="primary" className="text-white ml-3">
|
||||||
<UploadIcon />
|
<UploadIcon />
|
||||||
Unggah Foto Dengan AI
|
Unggah Foto Dengan AI
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link> */}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,14 @@ import {
|
||||||
uploadThumbnail,
|
uploadThumbnail,
|
||||||
} from "@/service/content/content";
|
} from "@/service/content/content";
|
||||||
import { uploadThumbnailBlog } from "@/service/blog/blog";
|
import { uploadThumbnailBlog } from "@/service/blog/blog";
|
||||||
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
|
import {
|
||||||
|
generateDataArticle,
|
||||||
|
getDetailArticle,
|
||||||
|
getGenerateKeywords,
|
||||||
|
getGenerateTitle,
|
||||||
|
} from "@/service/content/ai";
|
||||||
|
import { getCookiesDecrypt } from "@/lib/utils";
|
||||||
|
|
||||||
const imageSchema = z.object({
|
const imageSchema = z.object({
|
||||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||||
|
|
@ -56,12 +64,33 @@ export default function FormImage() {
|
||||||
const taskId = Cookies.get("taskId");
|
const taskId = Cookies.get("taskId");
|
||||||
const scheduleId = Cookies.get("scheduleId");
|
const scheduleId = Cookies.get("scheduleId");
|
||||||
const scheduleType = Cookies.get("scheduleType");
|
const scheduleType = Cookies.get("scheduleType");
|
||||||
|
const roleId = getCookiesDecrypt("urie");
|
||||||
|
|
||||||
const [categories, setCategories] = useState<Category[]>([]);
|
const [categories, setCategories] = useState<Category[]>([]);
|
||||||
const [selectedCategory, setSelectedCategory] = useState<any>();
|
const [selectedCategory, setSelectedCategory] = useState<any>();
|
||||||
const [tags, setTags] = useState<any[]>([]);
|
const [tags, setTags] = useState<any[]>([]);
|
||||||
const [thumbnail, setThumbnail] = useState<File | null>(null);
|
const [thumbnail, setThumbnail] = useState<File | null>(null);
|
||||||
const [preview, setPreview] = useState<string | null>(null);
|
const [preview, setPreview] = useState<string | null>(null);
|
||||||
|
const [selectedLanguage, setSelectedLanguage] = useState("");
|
||||||
|
|
||||||
|
const [selectedSEO, setSelectedSEO] = useState<string>("");
|
||||||
|
const [title, setTitle] = useState<string>("");
|
||||||
|
const [selectedAdvConfig, setSelectedAdvConfig] = useState<string>("");
|
||||||
|
const [editingArticleId, setEditingArticleId] = useState<string | null>(null);
|
||||||
|
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const [articleIds, setArticleIds] = useState<string[]>([]);
|
||||||
|
const [isGeneratedArticle, setIsGeneratedArticle] = useState(false);
|
||||||
|
const [articleBody, setArticleBody] = useState<string>("");
|
||||||
|
const [selectedArticleId, setSelectedArticleId] = useState<string | null>(
|
||||||
|
null
|
||||||
|
);
|
||||||
|
const [selectedMainKeyword, setSelectedMainKeyword] = useState("");
|
||||||
|
const [selectedWritingStyle, setSelectedWritingStyle] = useState("");
|
||||||
|
const [selectedSize, setSelectedSize] = useState("");
|
||||||
|
const [detailData, setDetailData] = useState<any>(null);
|
||||||
|
const [articleImages, setArticleImages] = useState<string[]>([]);
|
||||||
|
const [isSwitchOn, setIsSwitchOn] = useState<boolean>(false);
|
||||||
|
|
||||||
const [selectedTarget, setSelectedTarget] = useState("");
|
const [selectedTarget, setSelectedTarget] = useState("");
|
||||||
const [unitSelection, setUnitSelection] = useState({
|
const [unitSelection, setUnitSelection] = useState({
|
||||||
|
|
@ -82,6 +111,161 @@ export default function FormImage() {
|
||||||
resolver: zodResolver(imageSchema),
|
resolver: zodResolver(imageSchema),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const doGenerateMainKeyword = async () => {
|
||||||
|
console.log(selectedMainKeyword);
|
||||||
|
if (selectedMainKeyword?.length > 1) {
|
||||||
|
try {
|
||||||
|
setIsLoading(true);
|
||||||
|
const titleData = {
|
||||||
|
keyword: selectedMainKeyword,
|
||||||
|
style: selectedWritingStyle,
|
||||||
|
website: "0",
|
||||||
|
connectToWeb: true,
|
||||||
|
lang: selectedLanguage,
|
||||||
|
pointOfView: "None",
|
||||||
|
clientId: "",
|
||||||
|
};
|
||||||
|
console.log("Sending request for title with data:", titleData);
|
||||||
|
const titleRes = await getGenerateTitle(titleData);
|
||||||
|
setTitle(titleRes?.data?.data || "");
|
||||||
|
console.log("Generated title:", titleRes?.data?.data);
|
||||||
|
|
||||||
|
const keywordsData = {
|
||||||
|
keyword: selectedMainKeyword,
|
||||||
|
style: selectedWritingStyle,
|
||||||
|
website: "0",
|
||||||
|
connectToWeb: true,
|
||||||
|
lang: selectedLanguage,
|
||||||
|
pointOfView: "None",
|
||||||
|
clientId: "",
|
||||||
|
};
|
||||||
|
console.log("Sending request for keywords with data:", keywordsData);
|
||||||
|
const keywordsRes = await getGenerateKeywords(keywordsData);
|
||||||
|
setSelectedSEO(keywordsRes?.data?.data || []);
|
||||||
|
console.log("Generated keywords:", keywordsRes?.data?.data);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error during generation process:", error);
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.error("Please provide a valid main keyword.");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const doGenerateTitle = async () => {
|
||||||
|
if (selectedMainKeyword?.length > 1) {
|
||||||
|
try {
|
||||||
|
setIsLoading(true);
|
||||||
|
const titleData = {
|
||||||
|
keyword: selectedMainKeyword,
|
||||||
|
style: selectedWritingStyle,
|
||||||
|
website: "0",
|
||||||
|
connectToWeb: true,
|
||||||
|
lang: selectedLanguage,
|
||||||
|
pointOfView: "None",
|
||||||
|
clientId: "",
|
||||||
|
};
|
||||||
|
console.log("Sending request for title with data:", titleData);
|
||||||
|
const titleRes = await getGenerateTitle(titleData);
|
||||||
|
setTitle(titleRes?.data?.data || "");
|
||||||
|
console.log("Generated title:", titleRes?.data?.data);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error generating title:", error);
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.error("Please provide a valid main keyword.");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const doGenerateKeyword = async () => {
|
||||||
|
if (selectedMainKeyword?.length > 1) {
|
||||||
|
try {
|
||||||
|
setIsLoading(true);
|
||||||
|
const keywordsData = {
|
||||||
|
keyword: selectedMainKeyword,
|
||||||
|
style: selectedWritingStyle,
|
||||||
|
website: "0",
|
||||||
|
connectToWeb: true,
|
||||||
|
lang: selectedLanguage,
|
||||||
|
pointOfView: "None",
|
||||||
|
clientId: "",
|
||||||
|
};
|
||||||
|
console.log("Sending request for keywords with data:", keywordsData);
|
||||||
|
const keywordsRes = await getGenerateKeywords(keywordsData);
|
||||||
|
setSelectedSEO(keywordsRes?.data?.data || []);
|
||||||
|
console.log("Generated keywords:", keywordsRes?.data?.data);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error generating keywords:", error);
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.error("Please provide a valid main keyword.");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleGenerateArtikel = async () => {
|
||||||
|
const request = {
|
||||||
|
advConfig: selectedAdvConfig,
|
||||||
|
style: selectedWritingStyle,
|
||||||
|
website: "None",
|
||||||
|
connectToWeb: true,
|
||||||
|
lang: selectedLanguage,
|
||||||
|
pointOfView: "None",
|
||||||
|
title: title,
|
||||||
|
imageSource: "Web",
|
||||||
|
mainKeyword: selectedMainKeyword,
|
||||||
|
additionalKeywords: selectedSEO,
|
||||||
|
targetCountry: null,
|
||||||
|
articleSize: selectedSize,
|
||||||
|
projectId: 2,
|
||||||
|
createdBy: roleId,
|
||||||
|
clientId: "ngDLPPiorplznw2jTqVe3YFCz5xqKfUJ",
|
||||||
|
};
|
||||||
|
|
||||||
|
const res = await generateDataArticle(request);
|
||||||
|
close();
|
||||||
|
|
||||||
|
if (res.error) {
|
||||||
|
console.error(res.message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newArticleId = res?.data?.data?.id;
|
||||||
|
setIsGeneratedArticle(true);
|
||||||
|
|
||||||
|
setArticleIds((prevIds: string[]) => {
|
||||||
|
if (prevIds.length < 5) {
|
||||||
|
return [...prevIds, newArticleId];
|
||||||
|
} else {
|
||||||
|
const updatedIds = [...prevIds];
|
||||||
|
updatedIds[4] = newArticleId;
|
||||||
|
return updatedIds;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Cookies.set("nulisAIArticleIdTemp", JSON.stringify(articleIds));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleArticleIdClick = async (id: string) => {
|
||||||
|
const res = await getDetailArticle(id);
|
||||||
|
const articleData = res?.data?.data;
|
||||||
|
|
||||||
|
const cleanArticleBody = articleData?.articleBody?.replace(
|
||||||
|
/<img[^>]*>/g,
|
||||||
|
""
|
||||||
|
);
|
||||||
|
const articleImagesData = articleData?.imagesUrl?.split(",");
|
||||||
|
|
||||||
|
setArticleBody(cleanArticleBody || "");
|
||||||
|
setDetailData(articleData);
|
||||||
|
setSelectedArticleId(id);
|
||||||
|
setArticleImages(articleImagesData || []);
|
||||||
|
};
|
||||||
|
|
||||||
// const handleKeyDown = (e: any) => {
|
// const handleKeyDown = (e: any) => {
|
||||||
// const newTag = e.target.value.trim(); // Ambil nilai input
|
// const newTag = e.target.value.trim(); // Ambil nilai input
|
||||||
// if (e.key === "Enter" && newTag) {
|
// if (e.key === "Enter" && newTag) {
|
||||||
|
|
@ -242,6 +426,7 @@ export default function FormImage() {
|
||||||
<p className="text-red-400 text-sm">{errors.title.message}</p>
|
<p className="text-red-400 text-sm">{errors.title.message}</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<div className="py-3 w-full">
|
<div className="py-3 w-full">
|
||||||
<Label>Kategori</Label>
|
<Label>Kategori</Label>
|
||||||
|
|
@ -268,7 +453,228 @@ export default function FormImage() {
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="py-3">
|
<div className="flex flex-row items-center gap-3 py-2">
|
||||||
|
<Label>Bantuan AI</Label>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<Switch
|
||||||
|
defaultChecked={isSwitchOn}
|
||||||
|
color="primary"
|
||||||
|
id="c2"
|
||||||
|
onCheckedChange={(checked: boolean) =>
|
||||||
|
setIsSwitchOn(checked)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{isSwitchOn && (
|
||||||
|
<div>
|
||||||
|
<div className="flex flex-row gap-3">
|
||||||
|
<div className="space-y-2 py-3 w-4/12">
|
||||||
|
<Label>Bahasa</Label>
|
||||||
|
<Select onValueChange={setSelectedLanguage}>
|
||||||
|
<SelectTrigger size="md">
|
||||||
|
<SelectValue placeholder="Pilih" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="id">Indonesia</SelectItem>
|
||||||
|
<SelectItem value="en">English</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2 py-3 w-4/12">
|
||||||
|
<Label>Writing Style</Label>
|
||||||
|
<Select onValueChange={setSelectedWritingStyle}>
|
||||||
|
<SelectTrigger size="md">
|
||||||
|
<SelectValue placeholder="Pilih" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="friendly">Friendly</SelectItem>
|
||||||
|
<SelectItem value="profesional">
|
||||||
|
Profesional
|
||||||
|
</SelectItem>
|
||||||
|
<SelectItem value="informational">
|
||||||
|
Informational
|
||||||
|
</SelectItem>
|
||||||
|
<SelectItem value="neutral">Neutral</SelectItem>
|
||||||
|
<SelectItem value="witty">Witty</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2 py-3 w-4/12">
|
||||||
|
<Label>Article Size</Label>
|
||||||
|
<Select onValueChange={setSelectedSize}>
|
||||||
|
<SelectTrigger size="md">
|
||||||
|
<SelectValue placeholder="Pilih" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="news">
|
||||||
|
News (300 - 900 words)
|
||||||
|
</SelectItem>
|
||||||
|
<SelectItem value="info">
|
||||||
|
Info (900 - 2000 words)
|
||||||
|
</SelectItem>
|
||||||
|
<SelectItem value="detail">
|
||||||
|
Detail (2000 - 5000 words)
|
||||||
|
</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="mt-5">
|
||||||
|
<div className="flex flex-row items-center gap-3 mb-3">
|
||||||
|
<Label>Main Keyword</Label>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
color="primary"
|
||||||
|
onClick={doGenerateMainKeyword}
|
||||||
|
disabled={isLoading}
|
||||||
|
>
|
||||||
|
{isLoading ? "Processing..." : "Proses"}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Input
|
||||||
|
size="md"
|
||||||
|
type="text"
|
||||||
|
value={selectedMainKeyword}
|
||||||
|
onChange={(e) => setSelectedMainKeyword(e.target.value)}
|
||||||
|
placeholder="Enter Main Keyword"
|
||||||
|
/>
|
||||||
|
{/* )}
|
||||||
|
/> */}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="mt-5">
|
||||||
|
<div className="flex flex-row items-center gap-3 mb-3">
|
||||||
|
<Label>Judul</Label>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
color="primary"
|
||||||
|
onClick={doGenerateTitle}
|
||||||
|
disabled={isLoading}
|
||||||
|
>
|
||||||
|
{isLoading ? "Generating..." : "Generate"}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Input
|
||||||
|
size="md"
|
||||||
|
type="text"
|
||||||
|
value={title}
|
||||||
|
onChange={(e) => setTitle(e.target.value)}
|
||||||
|
placeholder="Generated Title"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="mt-5">
|
||||||
|
<div className="flex flex-row items-center gap-3 mb-3">
|
||||||
|
<Label>SEO</Label>
|
||||||
|
<Button
|
||||||
|
variant={"outline"}
|
||||||
|
color="primary"
|
||||||
|
onClick={doGenerateKeyword}
|
||||||
|
disabled={isLoading}
|
||||||
|
>
|
||||||
|
{isLoading ? "Generating..." : "Generate"}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<p className="font-semibold">
|
||||||
|
Kata kunci untuk disertakan dalam teks
|
||||||
|
</p>
|
||||||
|
<p className="text-sm">
|
||||||
|
JIka Anda tidak Memberikan kata kunci, kami akan secara
|
||||||
|
otomatis membuat kata kunci yang relevan dari kata kunci
|
||||||
|
utama untuk setiap bagian dan menggunakannya untuk membuat
|
||||||
|
artikel. Untuk menambahkan kata kunci baru, ketik ', +
|
||||||
|
kata kunci'.
|
||||||
|
</p>
|
||||||
|
<div className="mt-3">
|
||||||
|
<Textarea
|
||||||
|
value={selectedSEO}
|
||||||
|
onChange={(e) => setSelectedSEO(e.target.value)}
|
||||||
|
placeholder="Enter Title"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="mt-5">
|
||||||
|
<Label>Instruksi Khusus (Optional)</Label>
|
||||||
|
<div className="mt-3">
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="title"
|
||||||
|
render={({ field }) => (
|
||||||
|
<Textarea
|
||||||
|
value={field.value}
|
||||||
|
onChange={field.onChange}
|
||||||
|
placeholder="Enter Title"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div className="my-5">
|
||||||
|
<Button
|
||||||
|
// variant={"outline"}
|
||||||
|
color="primary"
|
||||||
|
onClick={handleGenerateArtikel}
|
||||||
|
>
|
||||||
|
Generate Article
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{isGeneratedArticle && (
|
||||||
|
<div className="mt-3 pb-0">
|
||||||
|
{articleIds.map((id: string, index: number) => (
|
||||||
|
<Button
|
||||||
|
key={index}
|
||||||
|
className={`btn m-1 ${
|
||||||
|
selectedArticleId === id
|
||||||
|
? "btn-warning"
|
||||||
|
: "btn-success"
|
||||||
|
}`}
|
||||||
|
onClick={() => handleArticleIdClick(id)}
|
||||||
|
variant={"outline"}
|
||||||
|
color="success"
|
||||||
|
>
|
||||||
|
{id}
|
||||||
|
</Button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="pt-3">
|
||||||
|
<div className="flex flex-row justify-between items-center">
|
||||||
|
{selectedArticleId && (
|
||||||
|
<a
|
||||||
|
href={`/admin/media/${
|
||||||
|
fileTypeId === "1"
|
||||||
|
? "image"
|
||||||
|
: fileTypeId === "2"
|
||||||
|
? "video"
|
||||||
|
: fileTypeId === "3"
|
||||||
|
? "text"
|
||||||
|
: "audio"
|
||||||
|
}/update-new/${selectedArticleId}`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
className="mb-2"
|
||||||
|
size="sm"
|
||||||
|
variant={"outline"}
|
||||||
|
color="primary"
|
||||||
|
>
|
||||||
|
Edit
|
||||||
|
</Button>
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="">
|
||||||
<Label>Deskripsi</Label>
|
<Label>Deskripsi</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
|
|
@ -276,7 +682,7 @@ export default function FormImage() {
|
||||||
render={({ field: { onChange, value } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
<JoditEditor
|
<JoditEditor
|
||||||
ref={editor}
|
ref={editor}
|
||||||
value={value}
|
value={articleBody || value}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
className="dark:text-black"
|
className="dark:text-black"
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -351,6 +351,14 @@ export default function FormConvertSPIT() {
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
<div className="my-5">
|
||||||
|
<Button
|
||||||
|
// variant={"outline"}
|
||||||
|
color="primary"
|
||||||
|
>
|
||||||
|
Content Rewrite
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<Label className="text-xl text-black">File Media</Label>
|
<Label className="text-xl text-black">File Media</Label>
|
||||||
<div className="w-full ">
|
<div className="w-full ">
|
||||||
|
|
|
||||||
|
|
@ -189,10 +189,10 @@ export default function FormContestDetail() {
|
||||||
theme: data.theme,
|
theme: data.theme,
|
||||||
};
|
};
|
||||||
|
|
||||||
const response = await createTask(requestData);
|
// const response = await createTask(requestData);
|
||||||
|
|
||||||
console.log("Form Data Submitted:", requestData);
|
console.log("Form Data Submitted:", requestData);
|
||||||
console.log("response", response);
|
// console.log("response", response);
|
||||||
|
|
||||||
MySwal.fire({
|
MySwal.fire({
|
||||||
title: "Sukses",
|
title: "Sukses",
|
||||||
|
|
@ -201,7 +201,7 @@ export default function FormContestDetail() {
|
||||||
confirmButtonColor: "#3085d6",
|
confirmButtonColor: "#3085d6",
|
||||||
confirmButtonText: "OK",
|
confirmButtonText: "OK",
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
router.push("/en/contributor/task");
|
router.push("/en/shared/contest");
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -369,11 +369,11 @@ export default function FormContestDetail() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* <div className="mt-4">
|
<div className="mt-4">
|
||||||
<Button type="submit" color="primary">
|
<Button type="submit" color="primary">
|
||||||
Submit
|
Submit
|
||||||
</Button>
|
</Button>
|
||||||
</div> */}
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
|
||||||
14
lib/menus.ts
14
lib/menus.ts
|
|
@ -97,13 +97,13 @@ export function getMenuList(pathname: string, t: any): Group[] {
|
||||||
icon: "heroicons:credit-card",
|
icon: "heroicons:credit-card",
|
||||||
children: [],
|
children: [],
|
||||||
},
|
},
|
||||||
{
|
// {
|
||||||
href: "/contributor/content/nulis-ai",
|
// href: "/contributor/content/nulis-ai",
|
||||||
label: "nulis ai",
|
// label: "nulis ai",
|
||||||
active: pathname.includes("/content/nulisai"),
|
// active: pathname.includes("/content/nulisai"),
|
||||||
icon: "heroicons:credit-card",
|
// icon: "heroicons:credit-card",
|
||||||
children: [],
|
// children: [],
|
||||||
},
|
// },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue