fix: media tracking table, and update task TA

This commit is contained in:
Sabda Yagra 2025-12-03 13:11:27 +07:00
parent 33f88feb60
commit 6f2eff48a1
3 changed files with 199 additions and 60 deletions

View File

@ -40,7 +40,7 @@ export default function TrackingBeritaCard() {
const initFecth = async () => { const initFecth = async () => {
loading(); loading();
const response = await listDataTracking(showData, page - 1); const response = await listDataTracking(Number(showData), page - 1, search);
const data = response?.data?.data; const data = response?.data?.data;
const newData = data?.content; const newData = data?.content;
setTotalPage(data?.totalPages || 1); setTotalPage(data?.totalPages || 1);
@ -56,23 +56,82 @@ export default function TrackingBeritaCard() {
setContent(response?.data?.data?.content || []); setContent(response?.data?.data?.content || []);
}; };
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => { const handleInputChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value; const value = e.target.value;
setSearch(value); setSearch(value);
if (value.trim() === "") { const response = await listDataTracking(Number(showData), 0, value);
initFecth(); setContent(response?.data?.data?.content || []);
} else {
fecthAll(value);
}
}; };
// const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
// const value = e.target.value;
// setSearch(value);
// if (value.trim() === "") {
// initFecth();
// } else {
// fecthAll(value);
// }
// };
const handleSelect = (id: number) => { const handleSelect = (id: number) => {
setSelectedItems((prev) => setSelectedItems((prev) =>
prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id] prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id]
); );
}; };
const doSave = async () => {
if (selectedItems.length === 0) {
MySwal.fire(
"Peringatan",
"Pilih minimal 1 berita untuk disimpan.",
"warning"
);
return;
}
try {
loading();
const promises = selectedItems.map(async (id) => {
const res = await mediaTrackingSave({
mediaUploadId: id,
duration: 24,
scrapingPeriod: 3,
});
// cek pesan API
if (!res?.data?.success) {
throw new Error(res?.data?.message || "Limit media tracking per hari sudah tercapai. Maksimal 5 tracking per hari.");
}
return res;
});
await Promise.all(promises);
close();
await MySwal.fire({
icon: "success",
title: "Berhasil!",
text: "Tracking berita berhasil ditambahkan.",
confirmButtonColor: "#2563eb",
});
setSelectedItems([]);
initFecth();
} catch (err: any) {
close();
MySwal.fire({
icon: "error",
title: "Gagal!",
text: err?.message || "Terjadi kesalahan saat menyimpan data.",
confirmButtonColor: "#dc2626",
});
}
};
// const doSave = async () => { // const doSave = async () => {
// if (selectedItems.length === 0) { // if (selectedItems.length === 0) {
// toast("Pilih minimal 1 berita untuk disimpan."); // toast("Pilih minimal 1 berita untuk disimpan.");
@ -99,48 +158,63 @@ export default function TrackingBeritaCard() {
// } // }
// }; // };
const doSave = async () => { // const doSave = async () => {
if (selectedItems.length === 0) { // if (selectedItems.length === 0) {
MySwal.fire( // MySwal.fire(
"Peringatan", // "Peringatan",
"Pilih minimal 1 berita untuk disimpan.", // "Pilih minimal 1 berita untuk disimpan.",
"warning" // "warning"
); // );
return; // return;
} // }
try { // try {
loading(); // loading();
const promises = selectedItems.map((id) => // const promises = selectedItems.map((id) =>
mediaTrackingSave({ // mediaTrackingSave({
mediaUploadId: id, // mediaUploadId: id,
duration: 24, // duration: 24,
scrapingPeriod: 3, // scrapingPeriod: 3,
}) // })
); // );
await Promise.all(promises); // await Promise.all(promises);
close(); // close();
await MySwal.fire({ // await MySwal.fire({
icon: "success", // icon: "success",
title: "Berhasil!", // title: "Berhasil!",
text: "Tracking berita berhasil ditambahkan.", // text: "Tracking berita berhasil ditambahkan.",
confirmButtonColor: "#2563eb", // confirmButtonColor: "#2563eb",
}); // });
setSelectedItems([]); // setSelectedItems([]);
initFecth(); // initFecth();
} catch (err: any) { // } catch (err: any) {
close(); // close();
MySwal.fire({ // MySwal.fire({
icon: "error", // icon: "error",
title: "Gagal!", // title: "Gagal!",
text: err?.message || "Terjadi kesalahan saat menyimpan data.", // text: err?.message || "Terjadi kesalahan saat menyimpan data.",
confirmButtonColor: "#dc2626", // confirmButtonColor: "#dc2626",
}); // });
} // }
// };
const slugify = (text: string) => {
return text
.toLowerCase()
.replace(/[^a-z0-9]+/g, "-")
.replace(/(^-|-$)+/g, "");
};
const goToDetail = (item: any) => {
const type = item.type || "image";
const slug = slugify(item.title || "");
const url = `/in/${type}/detail/${item.id}-${slug}`;
window.location.href = url;
}; };
return ( return (
@ -188,7 +262,7 @@ export default function TrackingBeritaCard() {
<div className="text-sm text-blue-600 font-medium"> <div className="text-sm text-blue-600 font-medium">
{selectedItems.length} Item Terpilih{" "} {selectedItems.length} Item Terpilih{" "}
<span className="text-black"> <span className="text-black">
/ Tracking Berita tersisa {29 - selectedItems.length} / Tracking Berita tersisa {5 - selectedItems.length}
</span> </span>
</div> </div>
<Button className="bg-blue-600 text-white" onClick={doSave}> <Button className="bg-blue-600 text-white" onClick={doSave}>
@ -198,6 +272,48 @@ export default function TrackingBeritaCard() {
)} )}
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4"> <div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4">
{content?.length > 0 &&
content.map((item: any) => (
<Card
key={item.id}
className="relative overflow-hidden shadow-sm border rounded-lg"
>
{/* KLIK GAMBAR = CHECKLIST */}
<div
className="cursor-pointer"
onClick={() => handleSelect(item.id)}
>
<img
src={item.thumbnailLink}
alt={item.title}
className="w-full h-[300px] object-cover"
/>
{/* CHECKBOX */}
<div className="absolute top-2 left-2">
<div className="w-5 h-5 border-2 border-white bg-white rounded-sm flex items-center justify-center">
{selectedItems.includes(item.id) && (
<div className="w-3 h-3 bg-blue-600 rounded-sm" />
)}
</div>
</div>
</div>
{/* KLIK JUDUL = DETAIL */}
<p
className="p-2 text-sm font-medium hover:underline cursor-pointer"
onClick={(e) => {
e.stopPropagation();
goToDetail(item);
}}
>
{item.title}
</p>
</Card>
))}
</div>
{/* <div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4">
{content?.length > 1 && {content?.length > 1 &&
content.map((item: any) => ( content.map((item: any) => (
<Card <Card
@ -222,7 +338,7 @@ export default function TrackingBeritaCard() {
</p> </p>
</Card> </Card>
))} ))}
</div> </div> */}
<div className="mt-3"> <div className="mt-3">
{content && content?.length > 0 ? ( {content && content?.length > 0 ? (
<CustomPagination <CustomPagination

View File

@ -58,10 +58,8 @@ import { getListCompetencies } from "@/service/management-user/management-user";
const taskSchema = z.object({ const taskSchema = z.object({
// uniqueCode: z.string().min(1, { message: "Judul diperlukan" }), // uniqueCode: z.string().min(1, { message: "Judul diperlukan" }),
title: z.string().min(1, { message: "Judul diperlukan" }), title: z.string().optional(),
naration: z.string().min(2, { naration: z.string().optional(),
message: "Narasi Penugasan harus lebih dari 2 karakter.",
}),
}); });
export type taskDetail = { export type taskDetail = {
@ -585,9 +583,11 @@ export default function FormTaskTaEdit() {
assignedToRole: selectedTarget, assignedToRole: selectedTarget,
assignmentType: taskType, assignmentType: taskType,
assignmentTypeId: type, assignmentTypeId: type,
narration: data.naration, // narration: data.naration,
narration: data.naration ?? detail?.narration ?? "",
expertCompetencies: Array.from(selectedCompetencies).join(","), expertCompetencies: Array.from(selectedCompetencies).join(","),
title: data.title, // title: data.title,
title: data.title ?? detail?.title ?? "",
attachmentUrl: urlInputs attachmentUrl: urlInputs
.map((url: any) => url.attachmentUrl || "") .map((url: any) => url.attachmentUrl || "")
.filter((url: string) => url.trim() !== ""), .filter((url: string) => url.trim() !== ""),
@ -844,7 +844,7 @@ export default function FormTaskTaEdit() {
const handleLinkChange = (index: number, value: string) => { const handleLinkChange = (index: number, value: string) => {
setUrlInputs((prev: any) => setUrlInputs((prev: any) =>
prev.map((url: any, idx: any) => prev.map((url: any, idx: any) =>
idx === index ? { ...url, attachmentUrl: value } : url idx === index ? { ...url, attachmentUrl: value } : url
) )
); );
@ -886,7 +886,9 @@ export default function FormTaskTaEdit() {
)} )}
</div> </div>
<div className="mt-5 space-y-2"> <div className="mt-5 space-y-2">
<Label>{t("areas-expertise", { defaultValue: "Areas Expertise" })}</Label> <Label>
{t("areas-expertise", { defaultValue: "Areas Expertise" })}
</Label>
<div className="flex flex-wrap gap-4"> <div className="flex flex-wrap gap-4">
{userCompetencies?.map((item: any) => ( {userCompetencies?.map((item: any) => (
<div className="flex items-center gap-2" key={item.id}> <div className="flex items-center gap-2" key={item.id}>
@ -901,7 +903,9 @@ export default function FormTaskTaEdit() {
</div> </div>
</div> </div>
<div className="mt-5 space-y-2"> <div className="mt-5 space-y-2">
<Label>{t("choose-expert", { defaultValue: "Choose Expert" })}</Label> <Label>
{t("choose-expert", { defaultValue: "Choose Expert" })}
</Label>
<div className="flex flex-wrap gap-4"> <div className="flex flex-wrap gap-4">
<Dialog> <Dialog>
<DialogTrigger asChild> <DialogTrigger asChild>
@ -982,7 +986,9 @@ export default function FormTaskTaEdit() {
</div> </div>
<div className="mt-5 space-y-2"> <div className="mt-5 space-y-2">
<Label>{t("description", { defaultValue: "Description" })}</Label> <Label>
{t("description", { defaultValue: "Description" })}
</Label>
<Controller <Controller
control={control} control={control}
name="naration" name="naration"
@ -1000,10 +1006,14 @@ export default function FormTaskTaEdit() {
)} )}
</div> </div>
<div className="space-y-2.5 mt-5"> <div className="space-y-2.5 mt-5">
<Label htmlFor="attachments">{t("attachment", { defaultValue: "Attachment" })}</Label> <Label htmlFor="attachments">
{t("attachment", { defaultValue: "Attachment" })}
</Label>
<div className="space-y-3"> <div className="space-y-3">
<div className="space-y-2"> <div className="space-y-2">
<Label>{t("audio-visual", { defaultValue: "Audio Visual" })}</Label> <Label>
{t("audio-visual", { defaultValue: "Audio Visual" })}
</Label>
<FileUploader <FileUploader
accept={{ accept={{
"mp4/*": [], "mp4/*": [],

View File

@ -44,12 +44,25 @@ export async function getMediaTrackingResult(data: any) {
return httpGetInterceptor(url); return httpGetInterceptor(url);
} }
export async function listDataTracking(size: any, page: any) { // export async function listDataTracking(size: any, page: any) {
// return await httpGetInterceptor(
// `media/public/list?enablePage=1&sort=desc&size=${size}&page=${page}`
// );
// }
export async function listDataTracking(
size: number,
page: number,
keyword = "",
categoryFilter = "",
statusFilter = ""
) {
return await httpGetInterceptor( return await httpGetInterceptor(
`media/public/list?enablePage=1&sort=desc&size=${size}&page=${page}` `media/list?isForAdmin=true&title=${keyword}&enablePage=1&sortBy=createdAt&sort=desc&size=${size}&page=${page}&categoryId=${categoryFilter}&statusId=${statusFilter}`
); );
} }
export async function listDataAllNonPagination(search: string) { export async function listDataAllNonPagination(search: string) {
return await httpGetInterceptor( return await httpGetInterceptor(
`media/public/list?enablePage=0&sort=desc&title=${search || ""}` `media/public/list?enablePage=0&sort=desc&title=${search || ""}`