feat:add kostum form contest, fix status table contest,add content rewrite 3 id,generate article

This commit is contained in:
Anang Yusman 2025-01-06 22:06:02 +08:00
parent 0fdfa4b8ba
commit 65ec901d9a
15 changed files with 554 additions and 233 deletions

View File

@ -108,12 +108,8 @@ const columns: ColumnDef<any>[] = [
diterima: "bg-green-100 text-green-600", diterima: "bg-green-100 text-green-600",
"menunggu review": "bg-orange-100 text-orange-600", "menunggu review": "bg-orange-100 text-orange-600",
}; };
// Mengambil `statusName` dari data API
const status = row.getValue("statusName") as string; const status = row.getValue("statusName") as string;
const statusName = status?.toLocaleLowerCase(); // Ubah ke huruf kecil const statusName = status?.toLocaleLowerCase();
// Gunakan `statusName` untuk pencocokan
const statusStyles = const statusStyles =
statusColors[statusName] || "bg-gray-100 text-gray-600"; statusColors[statusName] || "bg-gray-100 text-gray-600";
@ -124,7 +120,7 @@ const columns: ColumnDef<any>[] = [
statusStyles statusStyles
)} )}
> >
{status} {/* Tetap tampilkan nilai asli */} {status}
</Badge> </Badge>
); );
}, },

View File

@ -66,7 +66,7 @@ const columns: ColumnDef<any>[] = [
return ( return (
<div> <div>
{isActive ? ( {isActive ? (
<b className="text-info">Terkirim</b> <b className="text-blue-500">Terkirim</b>
) : ( ) : (
<b className="text-danger">Belum Terkirim</b> <b className="text-danger">Belum Terkirim</b>
)} )}

View File

@ -66,7 +66,7 @@ const columns: ColumnDef<any>[] = [
return ( return (
<div> <div>
{isActive ? ( {isActive ? (
<b className="text-info">Terkirim</b> <b className="text-blue-500">Terkirim</b>
) : ( ) : (
<b className="text-danger">Belum Terkirim</b> <b className="text-danger">Belum Terkirim</b>
)} )}

View File

@ -84,10 +84,17 @@ const columns: ColumnDef<any>[] = [
accessorKey: "isPublishForAll", accessorKey: "isPublishForAll",
header: "Status", header: "Status",
cell: ({ row }) => { cell: ({ row }) => {
const isPublishForAll = row.getValue("isPublishForAll");
return ( return (
<span className="whitespace-nowrap text-blue-600"> <Badge
{row.getValue("isPublishForAll")} className={`whitespace-nowrap px-2 py-1 rounded-full ${
</span> isPublishForAll
? "bg-green-100 text-green-600" // Warna hijau untuk status "Publish"
: "bg-orange-100 text-orange-600" // Warna kuning untuk status "Pending"
}`}
>
{isPublishForAll ? "Publish" : "Pending"}
</Badge>
); );
}, },
}, },

View File

@ -54,6 +54,7 @@ import { loading } from "@/config/swal";
import { getCookiesDecrypt } from "@/lib/utils"; import { getCookiesDecrypt } from "@/lib/utils";
import { Icon } from "@iconify/react/dist/iconify.js"; import { Icon } from "@iconify/react/dist/iconify.js";
import { error } from "@/lib/swal"; import { error } from "@/lib/swal";
import dynamic from "next/dynamic";
const imageSchema = z.object({ const imageSchema = z.object({
title: z.string().min(1, { message: "Judul diperlukan" }), title: z.string().min(1, { message: "Judul diperlukan" }),
@ -96,6 +97,13 @@ type Detail = {
uploadedById: number; uploadedById: number;
}; };
const ViewEditor = dynamic(
() => {
return import("@/components/editor/view-editor");
},
{ ssr: false }
);
export default function FormAudioDetail() { export default function FormAudioDetail() {
const MySwal = withReactContent(Swal); const MySwal = withReactContent(Swal);
const router = useRouter(); const router = useRouter();
@ -180,7 +188,7 @@ export default function FormAudioDetail() {
const getCategories = async () => { const getCategories = async () => {
try { try {
const category = await listEnableCategory(fileTypeId); const category = await listEnableCategory(fileTypeId);
const resCategory: Category[] = category.data.data.content; const resCategory: Category[] = category?.data.data.content;
setCategories(resCategory); setCategories(resCategory);
console.log("data category", resCategory); console.log("data category", resCategory);
@ -206,7 +214,7 @@ export default function FormAudioDetail() {
async function initState() { async function initState() {
if (id) { if (id) {
const response = await detailMedia(id); const response = await detailMedia(id);
const details = response.data?.data; const details = response?.data?.data;
console.log("detail", details); console.log("detail", details);
setFiles(details?.files); setFiles(details?.files);
setDetail(details); setDetail(details);
@ -400,12 +408,7 @@ export default function FormAudioDetail() {
control={control} control={control}
name="description" name="description"
render={({ field: { onChange, value } }) => ( render={({ field: { onChange, value } }) => (
<JoditEditor <ViewEditor initialData={detail?.htmlDescription} />
ref={editor}
value={detail?.description}
onChange={onChange}
className="dark:text-black"
/>
)} )}
/> />
{errors.description?.message && ( {errors.description?.message && (

View File

@ -51,6 +51,7 @@ import { CloudUpload } from "lucide-react";
import Image from "next/image"; import Image from "next/image";
import { error, loading } from "@/config/swal"; import { error, loading } from "@/config/swal";
import { Item } from "@radix-ui/react-dropdown-menu"; import { Item } from "@radix-ui/react-dropdown-menu";
import dynamic from "next/dynamic";
const imageSchema = z.object({ const imageSchema = z.object({
title: z.string().min(1, { message: "Judul diperlukan" }), title: z.string().min(1, { message: "Judul diperlukan" }),
@ -70,6 +71,13 @@ type Category = {
name: string; name: string;
}; };
const CustomEditor = dynamic(
() => {
return import("@/components/editor/custom-editor");
},
{ ssr: false }
);
export default function FormAudio() { export default function FormAudio() {
const MySwal = withReactContent(Swal); const MySwal = withReactContent(Swal);
const router = useRouter(); const router = useRouter();
@ -281,19 +289,47 @@ export default function FormAudio() {
}; };
const handleArticleIdClick = async (id: string) => { const handleArticleIdClick = async (id: string) => {
setIsLoading(true); // Tampilkan loading segera setelah ID diklik
try {
// Panggil API untuk mendapatkan data artikel
const res = await getDetailArticle(id); const res = await getDetailArticle(id);
const articleData = res?.data?.data; const articleData = res?.data?.data;
// Bersihkan konten articleBody dari elemen gambar
const cleanArticleBody = articleData?.articleBody?.replace( const cleanArticleBody = articleData?.articleBody?.replace(
/<img[^>]*>/g, /<img[^>]*>/g,
"" ""
); );
// Pisahkan URL gambar menjadi array
const articleImagesData = articleData?.imagesUrl?.split(","); const articleImagesData = articleData?.imagesUrl?.split(",");
// Tunggu hingga `articleBody` tidak null atau kosong
const waitForGeneratedBody = async () => {
return new Promise<void>((resolve) => {
const interval = setInterval(() => {
if (cleanArticleBody) {
clearInterval(interval); // Hentikan polling jika articleBody tersedia
resolve();
}
}, 500); // Periksa setiap 500ms
});
};
// Tunggu hingga articleBody selesai di-generate
await waitForGeneratedBody();
// Set data artikel ke state setelah validasi
setArticleBody(cleanArticleBody || ""); setArticleBody(cleanArticleBody || "");
setDetailData(articleData); setDetailData(articleData);
setSelectedArticleId(id); setSelectedArticleId(id);
setArticleImages(articleImagesData || []); setArticleImages(articleImagesData || []);
} catch (error) {
console.error("Error fetching article details:", error);
} finally {
// Hilangkan loading setelah semua data selesai di-generate
setIsLoading(false);
}
}; };
const handleAddTag = (e: React.KeyboardEvent<HTMLInputElement>) => { const handleAddTag = (e: React.KeyboardEvent<HTMLInputElement>) => {
@ -861,14 +897,18 @@ export default function FormAudio() {
<Controller <Controller
control={control} control={control}
name="description" name="description"
render={({ field: { onChange, value } }) => ( render={({ field: { onChange, value } }) =>
<JoditEditor isLoading ? (
ref={editor} <div className="flex justify-center items-center h-40">
value={articleBody || value} <p className="text-gray-500">Loading...</p>
</div>
) : (
<CustomEditor
onChange={onChange} onChange={onChange}
className="dark:text-black" initialData={articleBody || value}
/> />
)} )
}
/> />
{errors.description?.message && ( {errors.description?.message && (
<p className="text-red-400 text-sm"> <p className="text-red-400 text-sm">

View File

@ -295,7 +295,7 @@ export default function FormImage() {
setIsGeneratedArticle(true); setIsGeneratedArticle(true);
setArticleIds((prevIds: string[]) => { setArticleIds((prevIds: string[]) => {
if (prevIds.length < 5) { if (prevIds.length < 3) {
return [...prevIds, newArticleId]; return [...prevIds, newArticleId];
} else { } else {
const updatedIds = [...prevIds]; const updatedIds = [...prevIds];
@ -308,19 +308,47 @@ export default function FormImage() {
}; };
const handleArticleIdClick = async (id: string) => { const handleArticleIdClick = async (id: string) => {
setIsLoading(true); // Tampilkan loading segera setelah ID diklik
try {
// Panggil API untuk mendapatkan data artikel
const res = await getDetailArticle(id); const res = await getDetailArticle(id);
const articleData = res?.data?.data; const articleData = res?.data?.data;
// Bersihkan konten articleBody dari elemen gambar
const cleanArticleBody = articleData?.articleBody?.replace( const cleanArticleBody = articleData?.articleBody?.replace(
/<img[^>]*>/g, /<img[^>]*>/g,
"" ""
); );
// Pisahkan URL gambar menjadi array
const articleImagesData = articleData?.imagesUrl?.split(","); const articleImagesData = articleData?.imagesUrl?.split(",");
// Tunggu hingga `articleBody` tidak null atau kosong
const waitForGeneratedBody = async () => {
return new Promise<void>((resolve) => {
const interval = setInterval(() => {
if (cleanArticleBody) {
clearInterval(interval); // Hentikan polling jika articleBody tersedia
resolve();
}
}, 500); // Periksa setiap 500ms
});
};
// Tunggu hingga articleBody selesai di-generate
await waitForGeneratedBody();
// Set data artikel ke state setelah validasi
setArticleBody(cleanArticleBody || ""); setArticleBody(cleanArticleBody || "");
setDetailData(articleData); setDetailData(articleData);
setSelectedArticleId(id); setSelectedArticleId(id);
setArticleImages(articleImagesData || []); setArticleImages(articleImagesData || []);
} catch (error) {
console.error("Error fetching article details:", error);
} finally {
// Hilangkan loading setelah semua data selesai di-generate
setIsLoading(false);
}
}; };
const handleAddTag = (e: React.KeyboardEvent<HTMLInputElement>) => { const handleAddTag = (e: React.KeyboardEvent<HTMLInputElement>) => {
@ -862,7 +890,7 @@ export default function FormImage() {
</div> </div>
{isGeneratedArticle && ( {isGeneratedArticle && (
<div className="mt-3 pb-0 flex flex-row "> <div className="mt-3 pb-0 flex flex-row">
{articleIds.map((id: string, index: number) => ( {articleIds.map((id: string, index: number) => (
<p <p
key={index} key={index}
@ -915,24 +943,18 @@ export default function FormImage() {
<Controller <Controller
control={control} control={control}
name="description" name="description"
render={({ field: { onChange, value } }) => ( render={({ field: { onChange, value } }) =>
// <JoditEditor isLoading ? (
// ref={editor} <div className="flex justify-center items-center h-40">
// value={articleBody || value} <p className="text-gray-500">Loading...</p>
// onChange={onChange} </div>
// className="dark:text-black" ) : (
// />
<<<<<<< HEAD
<CustomEditor
onChange={onChange}
// value={articleBody || value}
=======
<CustomEditor <CustomEditor
onChange={onChange} onChange={onChange}
initialData={articleBody || value} initialData={articleBody || value}
>>>>>>> e2193a8c9ac305726ea8f34d9b99e36b010f6841
/> />
)} )
}
/> />
{errors.description?.message && ( {errors.description?.message && (
<p className="text-red-400 text-sm"> <p className="text-red-400 text-sm">

View File

@ -330,11 +330,11 @@ export default function FormConvertSPIT() {
setIsGeneratedArticle(true); setIsGeneratedArticle(true);
setArticleIds((prevIds: string[]) => { setArticleIds((prevIds: string[]) => {
if (prevIds.length < 5) { if (prevIds.length < 3) {
return [...prevIds, newArticleId]; return [...prevIds, newArticleId];
} else { } else {
const updatedIds = [...prevIds]; const updatedIds = [...prevIds];
updatedIds[4] = newArticleId; updatedIds[2] = newArticleId;
return updatedIds; return updatedIds;
} }
}); });

View File

@ -54,6 +54,7 @@ import { loading } from "@/config/swal";
import { getCookiesDecrypt } from "@/lib/utils"; import { getCookiesDecrypt } from "@/lib/utils";
import { Icon } from "@iconify/react/dist/iconify.js"; import { Icon } from "@iconify/react/dist/iconify.js";
import { error } from "@/lib/swal"; import { error } from "@/lib/swal";
import dynamic from "next/dynamic";
const imageSchema = z.object({ const imageSchema = z.object({
title: z.string().min(1, { message: "Judul diperlukan" }), title: z.string().min(1, { message: "Judul diperlukan" }),
@ -96,6 +97,13 @@ type Detail = {
uploadedById: number; uploadedById: number;
}; };
const ViewEditor = dynamic(
() => {
return import("@/components/editor/view-editor");
},
{ ssr: false }
);
export default function FormTeksDetail() { export default function FormTeksDetail() {
const MySwal = withReactContent(Swal); const MySwal = withReactContent(Swal);
const router = useRouter(); const router = useRouter();
@ -402,12 +410,7 @@ export default function FormTeksDetail() {
control={control} control={control}
name="description" name="description"
render={({ field: { onChange, value } }) => ( render={({ field: { onChange, value } }) => (
<JoditEditor <ViewEditor initialData={detail?.htmlDescription} />
ref={editor}
value={detail?.description}
onChange={onChange}
className="dark:text-black"
/>
)} )}
/> />
{errors.description?.message && ( {errors.description?.message && (

View File

@ -51,6 +51,7 @@ import { CloudUpload } from "lucide-react";
import Image from "next/image"; import Image from "next/image";
import { error, loading } from "@/config/swal"; import { error, loading } from "@/config/swal";
import { Item } from "@radix-ui/react-dropdown-menu"; import { Item } from "@radix-ui/react-dropdown-menu";
import dynamic from "next/dynamic";
const imageSchema = z.object({ const imageSchema = z.object({
title: z.string().min(1, { message: "Judul diperlukan" }), title: z.string().min(1, { message: "Judul diperlukan" }),
@ -70,6 +71,13 @@ type Category = {
name: string; name: string;
}; };
const CustomEditor = dynamic(
() => {
return import("@/components/editor/custom-editor");
},
{ ssr: false }
);
export default function FormTeks() { export default function FormTeks() {
const MySwal = withReactContent(Swal); const MySwal = withReactContent(Swal);
const router = useRouter(); const router = useRouter();
@ -281,19 +289,47 @@ export default function FormTeks() {
}; };
const handleArticleIdClick = async (id: string) => { const handleArticleIdClick = async (id: string) => {
setIsLoading(true); // Tampilkan loading segera setelah ID diklik
try {
// Panggil API untuk mendapatkan data artikel
const res = await getDetailArticle(id); const res = await getDetailArticle(id);
const articleData = res?.data?.data; const articleData = res?.data?.data;
// Bersihkan konten articleBody dari elemen gambar
const cleanArticleBody = articleData?.articleBody?.replace( const cleanArticleBody = articleData?.articleBody?.replace(
/<img[^>]*>/g, /<img[^>]*>/g,
"" ""
); );
// Pisahkan URL gambar menjadi array
const articleImagesData = articleData?.imagesUrl?.split(","); const articleImagesData = articleData?.imagesUrl?.split(",");
// Tunggu hingga `articleBody` tidak null atau kosong
const waitForGeneratedBody = async () => {
return new Promise<void>((resolve) => {
const interval = setInterval(() => {
if (cleanArticleBody) {
clearInterval(interval); // Hentikan polling jika articleBody tersedia
resolve();
}
}, 500); // Periksa setiap 500ms
});
};
// Tunggu hingga articleBody selesai di-generate
await waitForGeneratedBody();
// Set data artikel ke state setelah validasi
setArticleBody(cleanArticleBody || ""); setArticleBody(cleanArticleBody || "");
setDetailData(articleData); setDetailData(articleData);
setSelectedArticleId(id); setSelectedArticleId(id);
setArticleImages(articleImagesData || []); setArticleImages(articleImagesData || []);
} catch (error) {
console.error("Error fetching article details:", error);
} finally {
// Hilangkan loading setelah semua data selesai di-generate
setIsLoading(false);
}
}; };
const handleAddTag = (e: React.KeyboardEvent<HTMLInputElement>) => { const handleAddTag = (e: React.KeyboardEvent<HTMLInputElement>) => {
@ -861,14 +897,18 @@ export default function FormTeks() {
<Controller <Controller
control={control} control={control}
name="description" name="description"
render={({ field: { onChange, value } }) => ( render={({ field: { onChange, value } }) =>
<JoditEditor isLoading ? (
ref={editor} <div className="flex justify-center items-center h-40">
value={articleBody || value} <p className="text-gray-500">Loading...</p>
</div>
) : (
<CustomEditor
onChange={onChange} onChange={onChange}
className="dark:text-black" initialData={articleBody || value}
/> />
)} )
}
/> />
{errors.description?.message && ( {errors.description?.message && (
<p className="text-red-400 text-sm"> <p className="text-red-400 text-sm">

View File

@ -51,7 +51,14 @@ import { CloudUpload } from "lucide-react";
import Image from "next/image"; import Image from "next/image";
import { error, loading } from "@/config/swal"; import { error, loading } from "@/config/swal";
import { Item } from "@radix-ui/react-dropdown-menu"; import { Item } from "@radix-ui/react-dropdown-menu";
import CustomEditor from "@/components/editor/custom-editor"; import dynamic from "next/dynamic";
const CustomEditor = dynamic(
() => {
return import("@/components/editor/custom-editor");
},
{ ssr: false }
);
const imageSchema = z.object({ const imageSchema = z.object({
title: z.string().min(1, { message: "Judul diperlukan" }), title: z.string().min(1, { message: "Judul diperlukan" }),
@ -282,19 +289,47 @@ export default function FormVideo() {
}; };
const handleArticleIdClick = async (id: string) => { const handleArticleIdClick = async (id: string) => {
setIsLoading(true); // Tampilkan loading segera setelah ID diklik
try {
// Panggil API untuk mendapatkan data artikel
const res = await getDetailArticle(id); const res = await getDetailArticle(id);
const articleData = res?.data?.data; const articleData = res?.data?.data;
// Bersihkan konten articleBody dari elemen gambar
const cleanArticleBody = articleData?.articleBody?.replace( const cleanArticleBody = articleData?.articleBody?.replace(
/<img[^>]*>/g, /<img[^>]*>/g,
"" ""
); );
// Pisahkan URL gambar menjadi array
const articleImagesData = articleData?.imagesUrl?.split(","); const articleImagesData = articleData?.imagesUrl?.split(",");
// Tunggu hingga `articleBody` tidak null atau kosong
const waitForGeneratedBody = async () => {
return new Promise<void>((resolve) => {
const interval = setInterval(() => {
if (cleanArticleBody) {
clearInterval(interval); // Hentikan polling jika articleBody tersedia
resolve();
}
}, 500); // Periksa setiap 500ms
});
};
// Tunggu hingga articleBody selesai di-generate
await waitForGeneratedBody();
// Set data artikel ke state setelah validasi
setArticleBody(cleanArticleBody || ""); setArticleBody(cleanArticleBody || "");
setDetailData(articleData); setDetailData(articleData);
setSelectedArticleId(id); setSelectedArticleId(id);
setArticleImages(articleImagesData || []); setArticleImages(articleImagesData || []);
} catch (error) {
console.error("Error fetching article details:", error);
} finally {
// Hilangkan loading setelah semua data selesai di-generate
setIsLoading(false);
}
}; };
const handleAddTag = (e: React.KeyboardEvent<HTMLInputElement>) => { const handleAddTag = (e: React.KeyboardEvent<HTMLInputElement>) => {

View File

@ -20,8 +20,12 @@ import {
import { Checkbox } from "@/components/ui/checkbox"; import { Checkbox } from "@/components/ui/checkbox";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import JoditEditor from "jodit-react"; import JoditEditor from "jodit-react";
import { createTask, getTask } from "@/service/task"; import {
import { getContestById } from "@/service/contest/contest"; createTask,
getTask,
getUserLevelForAssignments,
} from "@/service/task";
import { getContestById, postCreateContest } from "@/service/contest/contest";
import page from "@/app/[locale]/page"; import page from "@/app/[locale]/page";
import { import {
Popover, Popover,
@ -29,10 +33,19 @@ import {
PopoverTrigger, PopoverTrigger,
} from "@/components/ui/popover"; } from "@/components/ui/popover";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { CalendarIcon } from "lucide-react"; import { CalendarIcon, ChevronDown, ChevronUp } from "lucide-react";
import { format, parseISO } from "date-fns"; import { format, parseISO } from "date-fns";
import { Calendar } from "@/components/ui/calendar"; import { Calendar } from "@/components/ui/calendar";
import { DateRange } from "react-day-picker"; import { DateRange } from "react-day-picker";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import dynamic from "next/dynamic";
import Cookies from "js-cookie";
const contestSchema = z.object({ const contestSchema = z.object({
theme: z.string().min(1, { message: "Judul diperlukan" }), theme: z.string().min(1, { message: "Judul diperlukan" }),
@ -40,9 +53,12 @@ const contestSchema = z.object({
description: z.string().min(2, { description: z.string().min(2, {
message: "Narasi Penugasan harus lebih dari 2 karakter.", message: "Narasi Penugasan harus lebih dari 2 karakter.",
}), }),
scoringFormula: z.string().min(2, {
message: "Narasi Penugasan harus lebih dari 2 karakter.",
}),
}); });
export type taskDetail = { export type contestDetail = {
id: number; id: number;
theme: string; theme: string;
hastagCode: string; hastagCode: string;
@ -56,12 +72,22 @@ export type taskDetail = {
name: string; name: string;
}; };
createdAt: string; createdAt: string;
platformType: string | null;
assignmentTypeId: string;
targetOutput: string; targetOutput: string;
targetParticipantTopLevel: string; targetParticipantTopLevel: string;
description: string; description: string;
fileTypeOutput: any;
is_active: string; is_active: string;
}; };
const CustomEditor = dynamic(
() => {
return import("@/components/editor/custom-editor");
},
{ ssr: false }
);
export default function FormContestDetail() { export default function FormContestDetail() {
const MySwal = withReactContent(Swal); const MySwal = withReactContent(Swal);
const router = useRouter(); const router = useRouter();
@ -69,8 +95,19 @@ export default function FormContestDetail() {
type ContestSchema = z.infer<typeof contestSchema>; type ContestSchema = z.infer<typeof contestSchema>;
const { id } = useParams() as { id: string }; const { id } = useParams() as { id: string };
console.log(id); console.log(id);
const [mainType, setMainType] = useState<string>("1");
const [broadcastType, setBroadcastType] = useState<string>(""); // untuk Tipe Penugasan
const [selectedTarget, setSelectedTarget] = useState("all");
const [detail, setDetail] = useState<any>();
const [refresh] = useState(false);
const [date, setDate] = useState<DateRange | undefined>();
const [listDest, setListDest] = useState([]);
const [checkedLevels, setCheckedLevels] = useState(new Set());
const [expandedPolda, setExpandedPolda] = useState([{}]);
const [isLoading, setIsLoading] = useState(false);
const [platformTypeVisible, setPlatformTypeVisible] = useState(false);
// State for various form fields
const [taskOutput, setTaskOutput] = useState({ const [taskOutput, setTaskOutput] = useState({
all: false, all: false,
video: false, video: false,
@ -79,18 +116,6 @@ export default function FormContestDetail() {
text: false, text: false,
}); });
// const [assignmentType, setAssignmentType] = useState("mediahub");
// const [assignmentCategory, setAssignmentCategory] = useState("publication");
const [mainType, setMainType] = useState<string>("1");
const [taskType, setTaskType] = useState<string>("atensi-khusus");
const [broadcastType, setBroadcastType] = useState<string>(""); // untuk Tipe Penugasan
const [type, setType] = useState<string>("1");
const [selectedTarget, setSelectedTarget] = useState("all");
const [detail, setDetail] = useState<taskDetail>();
const [refresh] = useState(false);
const [date, setDate] = useState<DateRange | undefined>();
const [platformTypeVisible, setPlatformTypeVisible] = useState(false);
const [unitSelection, setUnitSelection] = useState({ const [unitSelection, setUnitSelection] = useState({
allUnit: false, allUnit: false,
mabes: false, mabes: false,
@ -113,6 +138,30 @@ export default function FormContestDetail() {
// setPlatformTypeVisible(selectedValue === 2); // setPlatformTypeVisible(selectedValue === 2);
// }; // };
useEffect(() => {
async function fetchPoldaPolres() {
setIsLoading(true);
try {
const response = await getUserLevelForAssignments();
setListDest(response?.data?.data.list);
const initialExpandedState = response?.data?.data.list.reduce(
(acc: any, polda: any) => {
acc[polda.id] = false;
return acc;
},
{}
);
setExpandedPolda(initialExpandedState);
console.log("polres", initialExpandedState);
} catch (error) {
console.error("Error fetching Polda/Polres data:", error);
} finally {
setIsLoading(false);
}
}
fetchPoldaPolres();
}, []);
useEffect(() => { useEffect(() => {
async function initState() { async function initState() {
if (id) { if (id) {
@ -142,19 +191,42 @@ export default function FormContestDetail() {
} }
}, [detail?.targetOutput]); }, [detail?.targetOutput]);
useEffect(() => { // useEffect(() => {
if (detail?.targetParticipantTopLevel) { // if (detail?.targetParticipantTopLevel) {
const outputSet = new Set( // const outputSet = new Set(
detail.targetParticipantTopLevel.split(",").map(Number) // detail.targetParticipantTopLevel.split(",").map(Number)
); // );
setUnitSelection({ // setUnitSelection({
allUnit: outputSet.has(0), // allUnit: outputSet.has(0),
mabes: outputSet.has(1), // mabes: outputSet.has(1),
polda: outputSet.has(2), // polda: outputSet.has(2),
polres: outputSet.has(3), // polres: outputSet.has(3),
}); // });
// }
// }, [detail?.targetParticipantTopLevel]);
const handleCheckboxChange = (levelId: number) => {
setCheckedLevels((prev) => {
const updatedLevels = new Set(prev);
if (updatedLevels.has(levelId)) {
updatedLevels.delete(levelId);
} else {
updatedLevels.add(levelId);
} }
}, [detail?.targetParticipantTopLevel]); return updatedLevels;
});
};
const handlePoldaPolresChange = () => {
return Array.from(checkedLevels).join(","); // Mengonversi Set ke string
};
const toggleExpand = (poldaId: any) => {
setExpandedPolda((prev: any) => ({
...prev,
[poldaId]: !prev[poldaId],
}));
};
const save = async (data: ContestSchema) => { const save = async (data: ContestSchema) => {
const fileTypeMapping = { const fileTypeMapping = {
@ -165,34 +237,53 @@ export default function FormContestDetail() {
text: "5", text: "5",
}; };
const unitMapping = {
allUnit: "0",
mabes: "1",
polda: "2",
polres: "3",
};
const assignmentPurposeString = Object.keys(unitSelection)
.filter((key) => unitSelection[key as keyof typeof unitSelection])
.map((key) => unitMapping[key as keyof typeof unitMapping])
.join(",");
const selectedOutputs = Object.keys(taskOutput) const selectedOutputs = Object.keys(taskOutput)
.filter((key) => taskOutput[key as keyof typeof taskOutput]) // Ambil hanya yang `true` .filter((key) => taskOutput[key as keyof typeof taskOutput]) // Ambil hanya yang `true`
.map((key) => fileTypeMapping[key as keyof typeof fileTypeMapping]) // Konversi ke nilai string .map((key) => fileTypeMapping[key as keyof typeof fileTypeMapping]) // Konversi ke nilai string
.join(","); .join(",");
const requestData = { const requestData: {
id?: any;
theme: string;
assignedToLevel: any;
assignmentPurpose: any;
hastagCode: string;
description: string;
assignmentMainTypeId: any;
scoringFormula: string;
fileTypeOutput: any;
} = {
...data, ...data,
// assignmentType, hastagCode: data.hastagCode,
// assignmentCategory,
target: selectedTarget,
unitSelection,
assignedToRole: "3",
taskType: taskType,
broadcastType: broadcastType,
assignmentMainTypeId: mainType,
assignmentPurpose: "1",
assignmentTypeId: type,
fileTypeOutput: selectedOutputs,
id: null,
description: data.description,
platformType: "",
theme: data.theme, theme: data.theme,
description: data.description,
scoringFormula: data.scoringFormula,
assignmentMainTypeId: mainType,
assignedToLevel: handlePoldaPolresChange(),
assignmentPurpose: assignmentPurposeString,
fileTypeOutput: selectedOutputs,
}; };
// const response = await createTask(requestData); if (id != undefined) {
requestData.id = id;
}
const response = await postCreateContest(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",
@ -225,7 +316,6 @@ export default function FormContestDetail() {
<Card> <Card>
<div className="px-6 py-6"> <div className="px-6 py-6">
<p className="text-lg font-semibold mb-3">Form Contest</p> <p className="text-lg font-semibold mb-3">Form Contest</p>
<form onSubmit={handleSubmit(onSubmit)}> <form onSubmit={handleSubmit(onSubmit)}>
<div className="gap-5 mb-5"> <div className="gap-5 mb-5">
<div className="space-y-2"> <div className="space-y-2">
@ -233,12 +323,12 @@ export default function FormContestDetail() {
<Controller <Controller
control={control} control={control}
name="hastagCode" name="hastagCode"
render={({ field }) => ( render={({ field: { onChange, value } }) => (
<Input <Input
size="md" size="md"
type="text" type="text"
value={field.value} value={detail?.hastagCode || value}
onChange={field.onChange} onChange={onChange}
placeholder="Enter hastagCode" placeholder="Enter hastagCode"
/> />
)} )}
@ -255,12 +345,12 @@ export default function FormContestDetail() {
<Controller <Controller
control={control} control={control}
name="theme" name="theme"
render={({ field }) => ( render={({ field: { onChange, value } }) => (
<Input <Input
size="md" size="md"
type="text" type="text"
value={field.value} value={detail?.theme || value}
onChange={field.onChange} onChange={onChange}
placeholder="Enter theme" placeholder="Enter theme"
/> />
)} )}
@ -345,19 +435,101 @@ export default function FormContestDetail() {
</Label> </Label>
</div> </div>
))} ))}
<div className=" pl-1">
<Dialog>
<DialogTrigger asChild>
<Button variant="soft" size="sm" color="primary">
[Kustom]
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px] md:max-w-[500px] lg:max-w-[1500px]">
<DialogHeader>
<DialogTitle>
Daftar Wilayah Polda dan Polres
</DialogTitle>
</DialogHeader>
<div className="grid grid-cols-2 gap-2 max-h-[400px] overflow-y-auto">
{listDest.map((polda: any) => (
<div key={polda.id} className="border p-2">
<Label className="flex items-center">
<Checkbox
checked={checkedLevels.has(polda.id)}
onCheckedChange={() =>
handleCheckboxChange(polda.id)
}
className="mr-3"
/>
{polda.name}
<button
onClick={() => toggleExpand(polda.id)}
className="ml-2 focus:outline-none"
>
{expandedPolda[polda.id] ? (
<ChevronUp size={16} />
) : (
<ChevronDown size={16} />
)}
</button>
</Label>
{expandedPolda[polda.id] && (
<div className="ml-6 mt-2">
<Label className="block">
<Checkbox
checked={polda?.subDestination?.every(
(polres: any) =>
checkedLevels.has(polres.id)
)}
onCheckedChange={(isChecked) => {
const updatedLevels = new Set(
checkedLevels
);
polda?.subDestination?.forEach(
(polres: any) => {
if (isChecked) {
updatedLevels.add(polres.id);
} else {
updatedLevels.delete(polres.id);
}
}
);
setCheckedLevels(updatedLevels);
}}
className="mr-2"
/>
Pilih Semua Polres
</Label>
{polda?.subDestination?.map((polres: any) => (
<Label key={polres.id} className="block mt-1">
<Checkbox
checked={checkedLevels.has(polres.id)}
onCheckedChange={() =>
handleCheckboxChange(polres.id)
}
className="mr-2"
/>
{polres.name}
</Label>
))}
</div>
)}
</div>
))}
</div>
</DialogContent>
</Dialog>
</div> </div>
</div> </div>
</div>
<div className="mt-7"> <div className="mt-7">
<Label>Narasi Penugasan</Label> <Label>Narasi Penugasan</Label>
<Controller <Controller
control={control} control={control}
name="description" name="description"
render={({ field: { onChange, value } }) => ( render={({ field: { onChange, value } }) => (
<JoditEditor <CustomEditor
ref={editor}
value={value}
onChange={onChange} onChange={onChange}
className="dark:text-black" initialData={detail?.description || value}
/> />
)} )}
/> />
@ -367,6 +539,24 @@ export default function FormContestDetail() {
</p> </p>
)} )}
</div> </div>
<div className="mt-7">
<Label>Rumus Penilaian</Label>
<Controller
control={control}
name="scoringFormula"
render={({ field: { onChange, value } }) => (
<CustomEditor
onChange={onChange}
initialData={detail?.scoringFormula || value}
/>
)}
/>
{errors.scoringFormula?.message && (
<p className="text-red-400 text-sm">
{errors.scoringFormula.message}
</p>
)}
</div>
</div> </div>
<div className="mt-4"> <div className="mt-4">

View File

@ -111,6 +111,7 @@ export default function FormTask() {
const [isVideoUploadFinish, setIsVideoUploadFinish] = useState(false); const [isVideoUploadFinish, setIsVideoUploadFinish] = useState(false);
const [isTextUploadFinish, setIsTextUploadFinish] = useState(false); const [isTextUploadFinish, setIsTextUploadFinish] = useState(false);
const [isAudioUploadFinish, setIsAudioUploadFinish] = useState(false); const [isAudioUploadFinish, setIsAudioUploadFinish] = useState(false);
const [voiceNoteLink, setVoiceNoteLink] = useState("");
const [platformTypeVisible, setPlatformTypeVisible] = useState(false); const [platformTypeVisible, setPlatformTypeVisible] = useState(false);
const [unitSelection, setUnitSelection] = useState({ const [unitSelection, setUnitSelection] = useState({
@ -244,52 +245,28 @@ export default function FormTask() {
setIsImageUploadFinish(true); setIsImageUploadFinish(true);
} }
imageFiles?.map(async (item: any, index: number) => { imageFiles?.map(async (item: any, index: number) => {
await uploadResumableFile( await uploadResumableFile(index, String(id), item, "1", "0");
index,
String(id),
item,
"1",
"0"
);
}); });
if (videoFiles?.length == 0) { if (videoFiles?.length == 0) {
setIsVideoUploadFinish(true); setIsVideoUploadFinish(true);
} }
videoFiles?.map(async (item: any, index: number) => { videoFiles?.map(async (item: any, index: number) => {
await uploadResumableFile( await uploadResumableFile(index, String(id), item, "2", "0");
index,
String(id),
item,
"2",
"0"
);
}); });
if (textFiles?.length == 0) { if (textFiles?.length == 0) {
setIsTextUploadFinish(true); setIsTextUploadFinish(true);
} }
textFiles?.map(async (item: any, index: number) => { textFiles?.map(async (item: any, index: number) => {
await uploadResumableFile( await uploadResumableFile(index, String(id), item, "3", "0");
index,
String(id),
item,
"3",
"0"
);
}); });
if (audioFiles?.length == 0) { if (audioFiles?.length == 0) {
setIsAudioUploadFinish(true); setIsAudioUploadFinish(true);
} }
audioFiles?.map(async (item: any, index: number) => { audioFiles?.map(async (item: any, index: number) => {
await uploadResumableFile( await uploadResumableFile(index, String(id), item, "4", "0");
index,
String(id),
item,
"4",
"0"
);
}); });
// MySwal.fire({ // MySwal.fire({
@ -417,13 +394,15 @@ export default function FormTask() {
// counterUpdateProgress++; // counterUpdateProgress++;
// setCounterProgress(counterUpdateProgress); // setCounterProgress(counterUpdateProgress);
successTodo(); successTodo();
if (fileTypeId == '1'){ if (fileTypeId == "1") {
setIsImageUploadFinish(true); setIsImageUploadFinish(true);
} else if (fileTypeId == '2'){ } else if (fileTypeId == "2") {
setIsVideoUploadFinish(true); setIsVideoUploadFinish(true);
} if (fileTypeId == '3'){ }
if (fileTypeId == "3") {
setIsTextUploadFinish(true); setIsTextUploadFinish(true);
} if (fileTypeId == '4'){ }
if (fileTypeId == "4") {
setIsAudioUploadFinish(true); setIsAudioUploadFinish(true);
} }
}, },
@ -434,10 +413,20 @@ export default function FormTask() {
useEffect(() => { useEffect(() => {
successTodo(); successTodo();
}, [isImageUploadFinish, isVideoUploadFinish, isAudioUploadFinish, isTextUploadFinish]) }, [
isImageUploadFinish,
isVideoUploadFinish,
isAudioUploadFinish,
isTextUploadFinish,
]);
function successTodo() { function successTodo() {
if (isImageUploadFinish && isVideoUploadFinish && isAudioUploadFinish && isTextUploadFinish) { if (
isImageUploadFinish &&
isVideoUploadFinish &&
isAudioUploadFinish &&
isTextUploadFinish
) {
successSubmit("/in/contributor/agenda-setting"); successSubmit("/in/contributor/agenda-setting");
} }
} }
@ -454,7 +443,6 @@ export default function FormTask() {
}); });
}; };
return ( return (
<Card> <Card>
<div className="px-6 py-6"> <div className="px-6 py-6">
@ -776,10 +764,18 @@ export default function FormTask() {
</Button> </Button>
</div> </div>
)} )}
{isRecording && ( {isRecording && <p>Recording... {timer} seconds remaining</p>}{" "}
<p>Recording... {timer} seconds remaining</p>
)}{" "}
{/* Display remaining time */} {/* Display remaining time */}
<div className="mt-4">
<Label htmlFor="voiceNoteLink">Link Berita</Label>
<Input
id="voiceNoteLink"
type="url"
placeholder="Masukkan link voice note"
value={voiceNoteLink}
onChange={(e) => setVoiceNoteLink(e.target.value)}
/>
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -44,7 +44,6 @@ export async function listDataAll(
); );
} }
<<<<<<< HEAD
export async function listDataImage( export async function listDataImage(
limit: any, limit: any,
page: any, page: any,
@ -59,15 +58,11 @@ export async function listDataImage(
endDate: any, endDate: any,
title: string = "" title: string = ""
) { ) {
=======
export async function listDataImage(limit: any, page: any, isForSelf: any, isApproval: any, categoryFilter: any, statusFilter: any, needApprovalFromLevel: any, creator: any, source: any, startDate: any, endDate: any, title: string = "") {
>>>>>>> e2193a8c9ac305726ea8f34d9b99e36b010f6841
return await httpGetInterceptor( return await httpGetInterceptor(
`media/list?enablePage=1&sortBy=createdAt&sort=desc&size=${limit}&page=${page}&typeId=1&isForSelf=${isForSelf}&isApproval=${isApproval}&categoryId=${categoryFilter}&statusId=${statusFilter}&needApprovalFromLevel=${needApprovalFromLevel}&creatorUserLevelName=${source}&creatorName=${creator}&startDate=${startDate}&endDate=${endDate}&title=${title}` `media/list?enablePage=1&sortBy=createdAt&sort=desc&size=${limit}&page=${page}&typeId=1&isForSelf=${isForSelf}&isApproval=${isApproval}&categoryId=${categoryFilter}&statusId=${statusFilter}&needApprovalFromLevel=${needApprovalFromLevel}&creatorUserLevelName=${source}&creatorName=${creator}&startDate=${startDate}&endDate=${endDate}&title=${title}`
); );
} }
<<<<<<< HEAD
export async function listDataVideo( export async function listDataVideo(
limit: any, limit: any,
page: any, page: any,
@ -82,15 +77,11 @@ export async function listDataVideo(
endDate: any, endDate: any,
title: string = "" title: string = ""
) { ) {
=======
export async function listDataVideo(limit: any, page: any, isForSelf: any, isApproval: any, categoryFilter: any, statusFilter: any, needApprovalFromLevel: any, creator: any, source: any, startDate: any, endDate: any, title: string = "") {
>>>>>>> e2193a8c9ac305726ea8f34d9b99e36b010f6841
return await httpGetInterceptor( return await httpGetInterceptor(
`media/list?enablePage=1&sortBy=createdAt&sort=desc&size=${limit}&page=${page}&typeId=2&isForSelf=${isForSelf}&isApproval=${isApproval}&categoryId=${categoryFilter}&statusId=${statusFilter}&needApprovalFromLevel=${needApprovalFromLevel}&creatorUserLevelName=${source}&creatorName=${creator}&startDate=${startDate}&endDate=${endDate}&title=${title}` `media/list?enablePage=1&sortBy=createdAt&sort=desc&size=${limit}&page=${page}&typeId=2&isForSelf=${isForSelf}&isApproval=${isApproval}&categoryId=${categoryFilter}&statusId=${statusFilter}&needApprovalFromLevel=${needApprovalFromLevel}&creatorUserLevelName=${source}&creatorName=${creator}&startDate=${startDate}&endDate=${endDate}&title=${title}`
); );
} }
<<<<<<< HEAD
export async function listDataTeks( export async function listDataTeks(
limit: any, limit: any,
page: any, page: any,
@ -105,18 +96,14 @@ export async function listDataTeks(
endDate: any, endDate: any,
title: string = "" title: string = ""
) { ) {
=======
export async function listDataTeks(limit: any, page: any, isForSelf: any, isApproval: any, categoryFilter: any, statusFilter: any, needApprovalFromLevel: any, creator: any, source: any, startDate: any, endDate: any, title: string = "") {
>>>>>>> e2193a8c9ac305726ea8f34d9b99e36b010f6841
return await httpGetInterceptor( return await httpGetInterceptor(
`media/list?enablePage=1&sortBy=createdAt&sort=desc&size=${limit}&page=${page}&typeId=3&isForSelf=${isForSelf}&isApproval=${isApproval}&categoryId=${categoryFilter}&statusId=${statusFilter}&needApprovalFromLevel=${needApprovalFromLevel}&creatorUserLevelName=${source}&creatorName=${creator}&startDate=${startDate}&endDate=${endDate}&title=${title}` `media/list?enablePage=1&sortBy=createdAt&sort=desc&size=${limit}&page=${page}&typeId=3&isForSelf=${isForSelf}&isApproval=${isApproval}&categoryId=${categoryFilter}&statusId=${statusFilter}&needApprovalFromLevel=${needApprovalFromLevel}&creatorUserLevelName=${source}&creatorName=${creator}&startDate=${startDate}&endDate=${endDate}&title=${title}`
); );
} }
<<<<<<< HEAD
export async function listDataAudio( export async function listDataAudio(
page: any,
limit: any, limit: any,
page: any,
isForSelf: any, isForSelf: any,
isApproval: any, isApproval: any,
categoryFilter: any, categoryFilter: any,
@ -128,9 +115,6 @@ export async function listDataAudio(
endDate: any, endDate: any,
title: string = "" title: string = ""
) { ) {
=======
export async function listDataAudio(page: any, limit: any, isForSelf: any, isApproval: any, categoryFilter: any, statusFilter: any, needApprovalFromLevel: any, creator: any, source: any, startDate: any, endDate: any, title: string = "") {
>>>>>>> e2193a8c9ac305726ea8f34d9b99e36b010f6841
return await httpGetInterceptor( return await httpGetInterceptor(
`media/list?enablePage=1&sortBy=createdAt&sort=desc&size=${limit}&page=${page}&typeId=4&isForSelf=${isForSelf}&isApproval=${isApproval}&categoryId=${categoryFilter}&statusId=${statusFilter}&needApprovalFromLevel=${needApprovalFromLevel}&creatorUserLevelName=${source}&creatorName=${creator}&startDate=${startDate}&endDate=${endDate}&title=${title}` `media/list?enablePage=1&sortBy=createdAt&sort=desc&size=${limit}&page=${page}&typeId=4&isForSelf=${isForSelf}&isApproval=${isApproval}&categoryId=${categoryFilter}&statusId=${statusFilter}&needApprovalFromLevel=${needApprovalFromLevel}&creatorUserLevelName=${source}&creatorName=${creator}&startDate=${startDate}&endDate=${endDate}&title=${title}`
); );

View File

@ -22,3 +22,8 @@ export async function getContestById(id: any, pages = 0) {
const url = `contest?id=${id}&page=${pages}`; const url = `contest?id=${id}&page=${pages}`;
return httpGetInterceptor(url); return httpGetInterceptor(url);
} }
export async function postCreateContest(data: any) {
const url = "contest";
return httpPostInterceptor(url, data);
}