This commit is contained in:
hanif salafi 2025-06-23 23:22:51 +07:00
commit 199aaf5808
9 changed files with 677 additions and 266 deletions

View File

@ -9,12 +9,7 @@ import { Button } from "@/components/ui/button";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { Calendar } from "@/components/ui/calendar"; import { Calendar } from "@/components/ui/calendar";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { import { Book, CheckCheck, Plus, Timer } from "lucide-react";
Book,
CheckCheck,
Plus,
Timer,
} from "lucide-react";
import { Checkbox } from "@/components/ui/checkbox"; import { Checkbox } from "@/components/ui/checkbox";
import { EventContentArg } from "@fullcalendar/core"; import { EventContentArg } from "@fullcalendar/core";
import EventModal from "./event-modal"; import EventModal from "./event-modal";
@ -171,7 +166,7 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
const monthData = res?.data?.data; const monthData = res?.data?.data;
if (monthData) { if (monthData) {
const allEvents: CalendarEvent[] = []; const allEvents: CalendarEvent[] = [];
// Map API data to the calendarEvents structure // Map API data to the calendarEvents structure
const events = monthData?.map((event: any) => ({ const events = monthData?.map((event: any) => ({
id: event.id.toString(), id: event.id.toString(),
title: event.title, title: event.title,
@ -237,10 +232,13 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
const allCategoryId = ["1", "2", "3", "4", "5"]; const allCategoryId = ["1", "2", "3", "4", "5"];
// Cek apakah SEMUA validTypeIds ada di typeIdsInData // Cek apakah SEMUA validTypeIds ada di typeIdsInData
const hasAllCategories = allCategoryId.every(categoryId => selectedCategory.includes(categoryId)); const hasAllCategories = allCategoryId.every((categoryId) =>
selectedCategory.includes(categoryId)
);
return eventCategories?.some((cat: string) => return eventCategories?.some(
selectedCategory.includes(cat) || (hasAllCategories && cat == "0") (cat: string) =>
selectedCategory.includes(cat) || (hasAllCategories && cat == "0")
); );
}); });
@ -266,7 +264,7 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
}; };
const handleCategorySelection = (category: string) => { const handleCategorySelection = (category: string) => {
setSelectedCategory(prev => { setSelectedCategory((prev) => {
if (prev.includes(category)) { if (prev.includes(category)) {
return prev.filter((c) => c !== category); return prev.filter((c) => c !== category);
} else { } else {
@ -346,15 +344,22 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
const renderEventContent = (eventInfo: any) => { const renderEventContent = (eventInfo: any) => {
const { title } = eventInfo.event; const { title } = eventInfo.event;
const { createdByName, isPublish, calendar } = eventInfo.event.extendedProps; const { createdByName, isPublish, calendar } =
eventInfo.event.extendedProps;
const bgColor = getEventColor(calendar); const bgColor = getEventColor(calendar);
const colorList = getEventColorList(calendar); const colorList = getEventColorList(calendar);
return ( return (
<div className={`w-full p-2 mb-2 rounded-md text-white text-sm flex justify-between items-stretch ${bgColor}`}> <div
className={`w-full p-2 mb-2 rounded-md text-white text-sm flex justify-between items-stretch ${bgColor}`}
>
<div className="flex-1"> <div className="flex-1">
<div className="flex flex-row items-center"> <div className="flex flex-row items-center">
{isPublish === true ? <CheckCheck size={15} /> : <Timer size={15} />} {isPublish === true ? (
<CheckCheck size={15} />
) : (
<Timer size={15} />
)}
<p className="ml-1">{title}</p> <p className="ml-1">{title}</p>
</div> </div>
<p className="ml-1 text-xs text-start mt-2"> <p className="ml-1 text-xs text-start mt-2">
@ -461,8 +466,12 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
text={event.title} text={event.title}
createdBy={event.createdByName} createdBy={event.createdByName}
isPublish={event.isPublish} isPublish={event.isPublish}
bgColor={getEventColor(event.agendaType as EventType)} bgColor={getEventColor(
colorList={getEventColorList(event.agendaType as EventType)} event.agendaType as EventType
)}
colorList={getEventColorList(
event.agendaType as EventType
)}
/> />
))} ))}
</div> </div>
@ -593,7 +602,9 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
className={category.className} className={category.className}
id={category.label} id={category.label}
checked={selectedCategory.includes(category.value)} checked={selectedCategory.includes(category.value)}
onCheckedChange={() => handleCategorySelection(category.value)} onCheckedChange={() =>
handleCategorySelection(category.value)
}
/> />
<Label htmlFor={category.label}>{category.label}</Label> <Label htmlFor={category.label}>{category.label}</Label>
</li> </li>
@ -605,7 +616,12 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
<Card className="col-span-12 lg:col-span-8 2xl:col-span-9 pt-5"> <Card className="col-span-12 lg:col-span-8 2xl:col-span-9 pt-5">
<CardContent className="dashcode-app-calendar"> <CardContent className="dashcode-app-calendar">
<FullCalendar <FullCalendar
plugins={[dayGridPlugin, timeGridPlugin, interactionPlugin, listPlugin]} plugins={[
dayGridPlugin,
timeGridPlugin,
interactionPlugin,
listPlugin,
]}
headerToolbar={{ headerToolbar={{
left: "prev,next today", left: "prev,next today",
center: "title", center: "title",
@ -636,7 +652,9 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
handleDateChange(info.view.currentStart, info.view.currentEnd); handleDateChange(info.view.currentStart, info.view.currentEnd);
handleViewChange(info.view.type); handleViewChange(info.view.type);
}} }}
viewClassNames={activeView === "listYear" ? "hide-calendar-grid" : ""} viewClassNames={
activeView === "listYear" ? "hide-calendar-grid" : ""
}
/> />
{activeView === "listYear" && ( {activeView === "listYear" && (

View File

@ -148,10 +148,12 @@ const EventModal = ({
satker: false, satker: false,
international: false, international: false,
}); });
const [agendaType, setAgendaType] = React.useState(""); // State untuk agendaType const [agendaType, setAgendaType] = React.useState(""); // State untuk agendaType
const [selectedPolda, setSelectedPolda] = React.useState([]); // Untuk data Polda const [selectedPolda, setSelectedPolda] = useState<string[]>([]);
const [selectedSatker, setSelectedSatker] = React.useState([]); const [selectedPolres, setSelectedPolres] = useState<string[]>([]);
const [selectedPolres, setSelectedPolres] = React.useState([]); const [selectedSatker, setSelectedSatker] = useState<string[]>([]);
const isDetailMode = true;
const [wavesurfer, setWavesurfer] = useState<WaveSurfer>(); const [wavesurfer, setWavesurfer] = useState<WaveSurfer>();
const [isPlaying, setIsPlaying] = useState(false); const [isPlaying, setIsPlaying] = useState(false);
const [isPublishing, setIsPublishing] = useState(false); const [isPublishing, setIsPublishing] = useState(false);
@ -174,10 +176,7 @@ const EventModal = ({
const detail = res?.data?.data; const detail = res?.data?.data;
setDetailData(detail); setDetailData(detail);
const description = res?.data?.data?.description; const description = detail?.description;
console.log("description", res?.data?.data?.description);
// Set nilai awal description ke form control
if (description) { if (description) {
setValue("description", description); setValue("description", description);
} }
@ -196,17 +195,56 @@ const EventModal = ({
attachments?.filter((file: any) => file.fileTypeId == 4) attachments?.filter((file: any) => file.fileTypeId == 4)
); );
const agendaType = detail?.agendaType; const rawAgendaTypes = detail?.agendaType?.split(",") || []; // ["0","1","2","3","4","5"]
setWilayahPublish({ const assignedToLevel = detail?.assignedToLevel?.split(",") || [];
semua: agendaType === "all",
nasional: agendaType === "mabes",
polda: agendaType === "polda",
polres: agendaType === "polres",
satker: agendaType === "satker",
international: agendaType === "international",
});
}
const wilayahState = {
semua: false,
nasional: false,
polda: false,
polres: false,
satker: false,
international: false,
};
rawAgendaTypes.forEach((type: any) => {
switch (type) {
case "0":
wilayahState.semua = true;
break;
case "1":
wilayahState.nasional = true;
break;
case "2":
wilayahState.polda = true;
break;
case "3":
wilayahState.polres = true;
break;
case "4":
wilayahState.satker = true;
break;
case "5":
wilayahState.international = true;
break;
default:
break;
}
});
setWilayahPublish(wilayahState);
// Atur unit berdasarkan agendaType
if (rawAgendaTypes.includes("2")) {
setSelectedPolda(assignedToLevel);
}
if (rawAgendaTypes.includes("3")) {
setSelectedPolres(assignedToLevel);
}
if (rawAgendaTypes.includes("4")) {
setSelectedSatker(assignedToLevel);
}
}
fetchDetailData(); fetchDetailData();
}, [event, setValue]); }, [event, setValue]);
@ -233,33 +271,39 @@ const EventModal = ({
const toggleWilayah = (key: string) => { const toggleWilayah = (key: string) => {
setWilayahPublish((prev: any) => { setWilayahPublish((prev: any) => {
const newState = { ...prev, [key]: !prev[key] }; let newState = { ...prev };
// Handle "semua" logic to check all options // Jika key === semua dan sebelumnya belum aktif, aktifkan semua
if (key === "semua" && newState.semua) { if (key === "semua") {
setAgendaType("all"); const newChecked = !prev.semua;
return { newState = {
semua: true, semua: newChecked,
nasional: true, nasional: newChecked,
polda: true, polda: newChecked,
polres: true, polres: newChecked,
satker: true, satker: newChecked,
international: true, international: newChecked,
}; };
if (newChecked) {
setAgendaType("0,1,2,3,4,5");
} else {
setAgendaType("");
}
return newState;
} }
// Uncheck "semua" if any other option is selected // Jika key bukan "semua"
if (key !== "semua") { newState[key] = !prev[key];
newState.semua = false; newState.semua = false; // Uncheck "semua" jika yang dipilih adalah individu
}
// Set agendaType based on the selected checkbox // Hitung ulang agendaType berdasarkan pilihan
if (newState.nasional) setAgendaType("mabes"); const selectedKeys = Object.entries(newState)
else if (newState.polda) setAgendaType("polda"); .filter(([k, v]) => v && k !== "semua")
else if (newState.polres) setAgendaType("polres"); .map(([k]) => wilayahValueMap[k]);
else if (newState.satker) setAgendaType("satker");
else if (newState.international) setAgendaType("international"); setAgendaType(selectedKeys.join(","));
else setAgendaType(""); // Reset if no checkbox is selected
return newState; return newState;
}); });
@ -269,12 +313,25 @@ const EventModal = ({
const agendaTypeList: string[] = []; const agendaTypeList: string[] = [];
const assignedToLevelList: string[] = []; const assignedToLevelList: string[] = [];
// Mapping dari checkbox wilayah ke agendaType // // Mapping dari checkbox wilayah ke agendaType
Object.keys(wilayahPublish).forEach((key) => { // Object.keys(wilayahPublish).forEach((key) => {
if (wilayahPublish[key as keyof typeof wilayahPublish]) { // if (wilayahPublish[key as keyof typeof wilayahPublish]) {
agendaTypeList.push(wilayahValueMap[key]); // agendaTypeList.push(wilayahValueMap[key]);
} // }
}); // });
if (wilayahPublish.semua) {
agendaTypeList.push("0", "1", "2", "3", "4", "5");
} else {
Object.keys(wilayahPublish).forEach((key) => {
if (
wilayahPublish[key as keyof typeof wilayahPublish] &&
key !== "semua"
) {
agendaTypeList.push(wilayahValueMap[key]);
}
});
}
// Unit-unit berdasarkan wilayah yang aktif // Unit-unit berdasarkan wilayah yang aktif
if (wilayahPublish.polda && selectedPolda.length > 0) { if (wilayahPublish.polda && selectedPolda.length > 0) {
@ -558,6 +615,7 @@ const EventModal = ({
confirmButtonText: "OK", confirmButtonText: "OK",
}).then(() => { }).then(() => {
router.push(redirect); router.push(redirect);
window.location.reload();
}); });
}; };
@ -784,7 +842,8 @@ const EventModal = ({
{wilayahPublish.polda && ( {wilayahPublish.polda && (
<UnitMapping <UnitMapping
unit="Polda" unit="Polda"
isDetail={false} isDetail={isDetailMode} // jika Anda punya kondisi detail
initData={selectedPolda}
sendDataToParent={(data: any) => sendDataToParent={(data: any) =>
setSelectedPolda(data) setSelectedPolda(data)
} }
@ -802,8 +861,9 @@ const EventModal = ({
</label> </label>
{wilayahPublish.polres && ( {wilayahPublish.polres && (
<UnitMapping <UnitMapping
isDetail={false}
unit="Polres" unit="Polres"
isDetail={isDetailMode}
initData={selectedPolres}
sendDataToParent={(data: any) => sendDataToParent={(data: any) =>
setSelectedPolres(data) setSelectedPolres(data)
} }
@ -821,8 +881,9 @@ const EventModal = ({
</label> </label>
{wilayahPublish.satker && ( {wilayahPublish.satker && (
<UnitMapping <UnitMapping
isDetail={false}
unit="Satker" unit="Satker"
isDetail={isDetailMode}
initData={selectedSatker}
sendDataToParent={(data: any) => sendDataToParent={(data: any) =>
setSelectedSatker(data) setSelectedSatker(data)
} }

View File

@ -202,6 +202,23 @@ export default function FormAskExpert() {
const details = response?.data?.data; const details = response?.data?.data;
setDetail(details); setDetail(details);
if (details?.assignedToLevel) {
const levels: Set<number> = new Set(
details.assignedToLevel.split(",").map((x: any) => Number(x))
);
setCheckedLevels(levels);
}
if (details?.assignedToUsers) {
const userIds = details.assignedToUsers.split(",").map(Number);
setCheckedLevels(new Set(userIds));
}
if (details?.expertCompetencies) {
const compIds = details.expertCompetencies.split(",").map(Number);
setSelectedCompetencies(new Set(compIds));
}
} }
} }
initState(); initState();
@ -515,7 +532,7 @@ export default function FormAskExpert() {
return ( return (
<Card> <Card>
<div className="px-6 py-6"> <div className="px-6 py-6">
<p className="text-lg font-semibold mb-3">{t("form-task")}</p> <p className="text-lg font-semibold mb-3">{t("form-task-ta")}</p>
{detail !== undefined ? ( {detail !== undefined ? (
<form onSubmit={handleSubmit(onSubmit)}> <form onSubmit={handleSubmit(onSubmit)}>
<div className="gap-5 mb-5"> <div className="gap-5 mb-5">
@ -617,7 +634,14 @@ export default function FormAskExpert() {
} }
className="mr-3" className="mr-3"
/> />
{expert.fullname} <div className="flex flex-col gap-2">
<div className="font-bold">
{expert.fullname}
</div>
<div className="italic">
({expert.username})
</div>
</div>
</Label> </Label>
</div> </div>
))} ))}
@ -625,6 +649,46 @@ export default function FormAskExpert() {
</DialogContent> </DialogContent>
</Dialog> </Dialog>
</div> </div>
{checkedLevels.size > 0 && (
<div className="mt-3">
<Label className="text-sm text-gray-600 mb-2 block">
Tenaga Ahli Terpilih ({checkedLevels.size})
</Label>
<div className="flex flex-wrap gap-2">
{Array.from(checkedLevels).map((expertId) => {
const expert = listExpert?.find(
(exp: any) => exp.id === expertId
);
return expert ? (
<div
key={expert.id}
className="inline-flex items-center gap-2 bg-blue-100 text-blue-800 text-sm font-medium px-3 py-1.5 rounded-full border border-blue-200"
>
<span>{expert.fullname}</span>
<button
type="button"
onClick={() => handleCheckboxChange(expert.id)}
className="ml-1 text-blue-600 hover:text-blue-800 hover:bg-blue-200 rounded-full p-0.5 transition-colors"
title="Remove expert"
>
<svg
className="w-3 h-3"
fill="currentColor"
viewBox="0 0 20 20"
>
<path
fillRule="evenodd"
d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
clipRule="evenodd"
/>
</svg>
</button>
</div>
) : null;
})}
</div>
</div>
)}
</div> </div>
<div className="mt-5 space-y-2"> <div className="mt-5 space-y-2">

View File

@ -636,7 +636,7 @@ export default function FormDoItYourself() {
return ( return (
<Card> <Card>
<div className="px-6 py-6"> <div className="px-6 py-6">
<p className="text-lg font-semibold mb-3">{t("form-task")}</p> <p className="text-lg font-semibold mb-3">{t("form-task-ta-do")}</p>
{detail !== undefined ? ( {detail !== undefined ? (
<form onSubmit={handleSubmit(onSubmit)}> <form onSubmit={handleSubmit(onSubmit)}>
<div className="gap-5 mb-5"> <div className="gap-5 mb-5">

View File

@ -10,42 +10,23 @@ import * as z from "zod";
import Swal from "sweetalert2"; import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content"; import withReactContent from "sweetalert2-react-content";
import { useParams, useRouter } from "next/navigation"; import { useParams, useRouter } from "next/navigation";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Checkbox } from "@/components/ui/checkbox"; import { Checkbox } from "@/components/ui/checkbox";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import JoditEditor from "jodit-react";
import { import {
acceptAssignment, acceptAssignmentTa,
createAssignmentResponse, createAssignmentResponse,
createTask,
deleteAssignmentResponse, deleteAssignmentResponse,
deleteTask, deleteTask,
finishTask, finishTaskTa,
getAcceptance, getAcceptance,
getAcceptanceAssignmentStatus, getAcceptanceAssignmentStatus,
getAssignmentResponseList, getAssignmentResponseList,
getMediaUpload, getMediaUpload,
getMediaUploadTa, getMediaUploadTa,
getTask,
getTaskTa, getTaskTa,
getUserLevelForAssignments, getUserLevelForAssignments,
getUserLevelForExpert,
} from "@/service/task"; } from "@/service/task";
import { import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import {
ChevronDown,
ChevronUp,
Dock, Dock,
DotSquare, DotSquare,
ImageIcon, ImageIcon,
@ -61,14 +42,20 @@ import { close, error, loading } from "@/lib/swal";
import { getCookiesDecrypt } from "@/lib/utils"; import { getCookiesDecrypt } from "@/lib/utils";
import { Avatar, AvatarImage } from "@/components/ui/avatar"; import { Avatar, AvatarImage } from "@/components/ui/avatar";
import { successCallback } from "@/config/swal"; import { successCallback } from "@/config/swal";
import FileUploader from "../shared/file-uploader";
import { AudioRecorder } from "react-audio-voice-recorder";
import Image from "next/image"; import Image from "next/image";
import { Icon } from "@iconify/react/dist/iconify.js"; import { Icon } from "@iconify/react/dist/iconify.js";
import WavesurferPlayer from "@wavesurfer/react"; import WavesurferPlayer from "@wavesurfer/react";
import WaveSurfer from "wavesurfer.js"; import WaveSurfer from "wavesurfer.js";
import { InputGroup, InputGroupText } from "@/components/ui/input-group"; import { InputGroup, InputGroupText } from "@/components/ui/input-group";
import { useTranslations } from "next-intl"; import { useTranslations } from "next-intl";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
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" }),
@ -252,7 +239,11 @@ export default function FormTaskTaDetail() {
const [acceptAcceptance, setAcceptAcceptance] = useState<AcceptanceData[]>( const [acceptAcceptance, setAcceptAcceptance] = useState<AcceptanceData[]>(
[] []
); );
const [listExpert, setListExpert] = useState<any[]>([]);
const [userCompetencies, setUserCompetencies] = useState<any[]>([]);
const [selectedCompetencies, setSelectedCompetencies] = useState<Set<number>>(
new Set()
);
const [totalPage, setTotalPage] = React.useState(1); const [totalPage, setTotalPage] = React.useState(1);
const [dataTable, setDataTable] = React.useState<any[]>([]); const [dataTable, setDataTable] = React.useState<any[]>([]);
const [totalData, setTotalData] = React.useState<number>(1); const [totalData, setTotalData] = React.useState<number>(1);
@ -327,37 +318,37 @@ export default function FormTaskTaDetail() {
// setPlatformTypeVisible(selectedValue === 2); // setPlatformTypeVisible(selectedValue === 2);
// }; // };
const handleExpertiseOutputChange = ( // const handleExpertiseOutputChange = (
key: keyof typeof expertise, // key: keyof typeof expertise,
value: boolean // value: boolean
) => { // ) => {
if (key === "semua") { // if (key === "semua") {
const newState = { // const newState = {
semua: value, // semua: value,
komunikasi: value, // komunikasi: value,
hukum: value, // hukum: value,
bahasa: value, // bahasa: value,
ekonomi: value, // ekonomi: value,
politik: value, // politik: value,
sosiologi: value, // sosiologi: value,
ilmuadministrasipemerintah: value, // ilmuadministrasipemerintah: value,
ti: value, // ti: value,
}; // };
setExpertiseOutput(newState); // setExpertiseOutput(newState);
} else { // } else {
const updated = { // const updated = {
...expertise, // ...expertise,
[key]: value, // [key]: value,
}; // };
const allChecked = ["video", "audio", "image", "text"].every( // const allChecked = ["video", "audio", "image", "text"].every(
(k) => updated[k as keyof typeof expertise] // (k) => updated[k as keyof typeof expertise]
); // );
updated.semua = allChecked; // updated.semua = allChecked;
setExpertiseOutput(updated); // setExpertiseOutput(updated);
} // }
}; // };
const handleExpertOutputChange = ( const handleExpertOutputChange = (
key: keyof typeof expert, key: keyof typeof expert,
@ -383,6 +374,68 @@ export default function FormTaskTaDetail() {
} }
}; };
useEffect(() => {
getDataAdditional();
}, []);
async function getDataAdditional() {
const resCompetencies = await getListCompetencies();
console.log("competency", resCompetencies);
setUserCompetencies(resCompetencies?.data?.data);
}
useEffect(() => {
async function fetchListExpert() {
setIsLoading(true);
try {
const response = await getUserLevelForExpert(id);
setListExpert(response?.data?.data);
console.log("tenaga ahli", response?.data?.data);
} catch (error) {
console.error("Error fetching Polda/Polres data:", error);
} finally {
setIsLoading(false);
}
}
fetchListExpert();
}, []);
useEffect(() => {
const fetchExpertsForCompetencies = async () => {
const allExperts: any[] = [];
for (const compId of Array.from(selectedCompetencies)) {
const response = await getUserLevelForExpert(compId);
const experts = response?.data?.data || [];
allExperts.push(...experts);
}
const uniqueExperts = Array.from(
new Map(allExperts.map((e) => [e.id, e])).values()
);
setListExpert(uniqueExperts);
};
if (selectedCompetencies.size > 0) {
fetchExpertsForCompetencies();
} else {
setListExpert([]);
}
}, [selectedCompetencies]);
const handleCompetencyChange = async (competencyId: number) => {
setSelectedCompetencies((prev) => {
const updated = new Set(prev);
if (updated.has(competencyId)) {
updated.delete(competencyId);
} else {
updated.add(competencyId);
}
return updated;
});
};
useEffect(() => { useEffect(() => {
async function fetchPoldaPolres() { async function fetchPoldaPolres() {
setIsLoading(true); setIsLoading(true);
@ -458,6 +511,16 @@ export default function FormTaskTaDetail() {
setCheckedLevels(levels); setCheckedLevels(levels);
} }
if (details?.assignedToUsers) {
const userIds = details.assignedToUsers.split(",").map(Number);
setCheckedLevels(new Set(userIds));
}
if (details?.expertCompetencies) {
const compIds = details.expertCompetencies.split(",").map(Number);
setSelectedCompetencies(new Set(compIds));
}
const attachment = details?.files; const attachment = details?.files;
setImageUploadedFiles( setImageUploadedFiles(
attachment?.filter((file: any) => file.fileTypeId == 1) attachment?.filter((file: any) => file.fileTypeId == 1)
@ -542,7 +605,7 @@ export default function FormTaskTaDetail() {
confirmButtonText: "OK", confirmButtonText: "OK",
}).then((result) => { }).then((result) => {
if (result.isConfirmed) { if (result.isConfirmed) {
router.push("/en/contributor/task"); router.push("/en/contributor/task-ta");
} }
}); });
}; };
@ -682,7 +745,7 @@ export default function FormTaskTaDetail() {
const handleAcceptAcceptance = async () => { const handleAcceptAcceptance = async () => {
loading(); loading();
console.log("Id user :", userId); console.log("Id user :", userId);
const response = await acceptAssignment(id); const response = await acceptAssignmentTa(id);
if (response?.error) { if (response?.error) {
error(response?.message); error(response?.message);
@ -773,7 +836,7 @@ export default function FormTaskTaDetail() {
); );
async function finishAssignment() { async function finishAssignment() {
const response = finishTask(id); const response = finishTaskTa(id);
// if (response.error) { // if (response.error) {
// error(response.message); // error(response.message);
@ -1011,48 +1074,98 @@ export default function FormTaskTaDetail() {
<div className="mt-5 space-y-2"> <div className="mt-5 space-y-2">
<Label>{t("areas-expertise")}</Label> <Label>{t("areas-expertise")}</Label>
<div className="flex flex-wrap gap-4"> <div className="flex flex-wrap gap-4">
{Object.keys(expertise).map((key) => ( {userCompetencies?.map((item: any) => (
<div className="flex items-center gap-2" key={key}> <div className="flex items-center gap-2" key={item.id}>
<Checkbox <Checkbox
id={key} id={`comp-${item.id}`}
checked={expertise[key as keyof typeof expertise]} checked={selectedCompetencies.has(item.id)}
onCheckedChange={(value) => onCheckedChange={() => handleCompetencyChange(item.id)}
handleExpertiseOutputChange(
key as keyof typeof expertise,
value as boolean
)
}
disabled disabled
/> />
<Label htmlFor={key}> <Label htmlFor={`comp-${item.id}`}>{item.name}</Label>
{key.charAt(0).toUpperCase() + key.slice(1)}
</Label>
</div> </div>
))} ))}
</div> </div>
</div> </div>
<div className="mt-5 space-y-2"> <div className="mt-5 space-y-2">
<Label>{t("choose-expert")}</Label> {/* <Label>{t("choose-expert")}</Label>
<div className="flex flex-wrap gap-4"> <div className="flex flex-wrap gap-4">
{Object.keys(expert).map((key) => ( <Dialog>
<div className="flex items-center gap-2" key={key}> <DialogTrigger asChild>
<Checkbox <Button variant="soft" size="sm" color="primary">
id={key} [{"Pilih Tenaga Ahli"}]
checked={expert[key as keyof typeof expert]} </Button>
onCheckedChange={(value) => </DialogTrigger>
handleExpertOutputChange( <DialogContent className="sm:max-w-[425px] md:max-w-[500px] lg:max-w-[1500px]">
key as keyof typeof expert, <DialogHeader>
value as boolean <DialogTitle>Daftar Tenaga Ahli</DialogTitle>
) </DialogHeader>
} <div className="grid grid-cols-2 gap-2 max-h-[400px] overflow-y-auto">
/> {listExpert?.map((expert: any) => (
<Label htmlFor={key}> <div key={expert.id} className="border p-2">
{key.charAt(0).toUpperCase() + key.slice(1)} <Label className="flex items-center">
</Label> <Checkbox
checked={checkedLevels.has(expert.id)}
onCheckedChange={() =>
handleCheckboxChange(expert.id)
}
className="mr-3"
/>
<div className="flex flex-col gap-2">
<div className="font-bold">
{expert.fullname}
</div>
<div className="italic">
({expert.username})
</div>
</div>
</Label>
</div>
))}
</div>
</DialogContent>
</Dialog>
</div> */}
{checkedLevels.size > 0 && (
<div className="mt-3">
<Label className="text-sm text-gray-600 mb-2 block">
Tenaga Ahli Terpilih ({checkedLevels.size})
</Label>
<div className="flex flex-wrap gap-2">
{Array.from(checkedLevels).map((expertId) => {
const expert = listExpert?.find(
(exp: any) => exp.id === expertId
);
return expert ? (
<div
key={expert.id}
className="inline-flex items-center gap-2 bg-blue-100 text-blue-800 text-sm font-medium px-3 py-1.5 rounded-full border border-blue-200"
>
<span>{expert.fullname}</span>
<button
type="button"
onClick={() => handleCheckboxChange(expert.id)}
className="ml-1 text-blue-600 hover:text-blue-800 hover:bg-blue-200 rounded-full p-0.5 transition-colors"
title="Remove expert"
>
<svg
className="w-3 h-3"
fill="currentColor"
viewBox="0 0 20 20"
>
<path
fillRule="evenodd"
d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
clipRule="evenodd"
/>
</svg>
</button>
</div>
) : null;
})}
</div> </div>
))} </div>
</div> )}
</div> </div>
<div></div> <div></div>

View File

@ -26,6 +26,7 @@ import {
getTask, getTask,
getTaskTa, getTaskTa,
getUserLevelForAssignments, getUserLevelForAssignments,
getUserLevelForExpert,
} from "@/service/task"; } from "@/service/task";
import { import {
Dialog, Dialog,
@ -40,6 +41,7 @@ import {
Dock, Dock,
ImageIcon, ImageIcon,
Music, Music,
Trash2,
VideoIcon, VideoIcon,
} from "lucide-react"; } from "lucide-react";
import FileUploader from "../shared/file-uploader"; import FileUploader from "../shared/file-uploader";
@ -52,6 +54,7 @@ import { Upload } from "tus-js-client";
import { error, loading } from "@/lib/swal"; import { error, loading } from "@/lib/swal";
import dynamic from "next/dynamic"; import dynamic from "next/dynamic";
import { useTranslations } from "next-intl"; import { useTranslations } from "next-intl";
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" }),
@ -148,11 +151,18 @@ export default function FormTaskTaEdit() {
const [broadcastType, setBroadcastType] = useState<string>(""); // untuk Tipe Penugasan const [broadcastType, setBroadcastType] = useState<string>(""); // untuk Tipe Penugasan
const [type, setType] = useState<string>("1"); const [type, setType] = useState<string>("1");
const [selectedTarget, setSelectedTarget] = useState("3,4"); const [selectedTarget, setSelectedTarget] = useState("3,4");
const [listExpert, setListExpert] = useState<any[]>([]);
const [userCompetencies, setUserCompetencies] = useState<any[]>([]);
const [selectedCompetencies, setSelectedCompetencies] = useState<Set<number>>(
new Set()
);
const [detail, setDetail] = useState<taskDetail>(); const [detail, setDetail] = useState<taskDetail>();
const [urlInputs, setUrlInputs] = useState<Url[]>([]); const [urlInputs, setUrlInputs] = useState<Url[]>([]);
const [refresh] = useState(false); const [refresh] = useState(false);
const [listDest, setListDest] = useState([]); // Data Polda dan Polres const [listDest, setListDest] = useState([]); // Data Polda dan Polres
const [checkedLevels, setCheckedLevels] = useState(new Set()); const [checkedLevels, setCheckedLevels] = useState<Set<number>>(new Set());
const [expandedPolda, setExpandedPolda] = useState([{}]); const [expandedPolda, setExpandedPolda] = useState([{}]);
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const [audioFile, setAudioFile] = useState<File | null>(null); const [audioFile, setAudioFile] = useState<File | null>(null);
@ -247,6 +257,68 @@ export default function FormTaskTaEdit() {
} }
}; };
useEffect(() => {
getDataAdditional();
}, []);
async function getDataAdditional() {
const resCompetencies = await getListCompetencies();
console.log("competency", resCompetencies);
setUserCompetencies(resCompetencies?.data?.data);
}
useEffect(() => {
async function fetchListExpert() {
setIsLoading(true);
try {
const response = await getUserLevelForExpert(id);
setListExpert(response?.data?.data);
console.log("tenaga ahli", response?.data?.data);
} catch (error) {
console.error("Error fetching Polda/Polres data:", error);
} finally {
setIsLoading(false);
}
}
fetchListExpert();
}, []);
useEffect(() => {
const fetchExpertsForCompetencies = async () => {
const allExperts: any[] = [];
for (const compId of Array.from(selectedCompetencies)) {
const response = await getUserLevelForExpert(compId);
const experts = response?.data?.data || [];
allExperts.push(...experts);
}
const uniqueExperts = Array.from(
new Map(allExperts.map((e) => [e.id, e])).values()
);
setListExpert(uniqueExperts);
};
if (selectedCompetencies.size > 0) {
fetchExpertsForCompetencies();
} else {
setListExpert([]);
}
}, [selectedCompetencies]);
const handleCompetencyChange = async (competencyId: number) => {
setSelectedCompetencies((prev) => {
const updated = new Set(prev);
if (updated.has(competencyId)) {
updated.delete(competencyId);
} else {
updated.add(competencyId);
}
return updated;
});
};
useEffect(() => { useEffect(() => {
async function fetchPoldaPolres() { async function fetchPoldaPolres() {
setIsLoading(true); setIsLoading(true);
@ -285,12 +357,22 @@ export default function FormTaskTaEdit() {
} }
if (details?.assignedToLevel) { if (details?.assignedToLevel) {
const levels = new Set( const levels: Set<number> = new Set(
details.assignedToLevel.split(",").map(Number) details.assignedToLevel.split(",").map((x: any) => Number(x))
); );
setCheckedLevels(levels); setCheckedLevels(levels);
} }
if (details?.assignedToUsers) {
const userIds = details.assignedToUsers.split(",").map(Number);
setCheckedLevels(new Set(userIds));
}
if (details?.expertCompetencies) {
const compIds = details.expertCompetencies.split(",").map(Number);
setSelectedCompetencies(new Set(compIds));
}
const attachment = details?.files; const attachment = details?.files;
setImageUploadedFiles( setImageUploadedFiles(
attachment?.filter((file: any) => file.fileTypeId == 1) attachment?.filter((file: any) => file.fileTypeId == 1)
@ -376,7 +458,7 @@ export default function FormTaskTaEdit() {
confirmButtonText: "OK", confirmButtonText: "OK",
}).then((result) => { }).then((result) => {
if (result.isConfirmed) { if (result.isConfirmed) {
router.push("/en/contributor/task"); router.push("/en/contributor/task-ta");
} }
}); });
}; };
@ -393,9 +475,9 @@ export default function FormTaskTaEdit() {
}); });
}; };
const handlePoldaPolresChange = () => { // const handlePoldaPolresChange = () => {
return Array.from(checkedLevels).join(","); // Mengonversi Set ke string // return Array.from(checkedLevels).join(","); // Mengonversi Set ke string
}; // };
const handleUnitChange = ( const handleUnitChange = (
key: keyof typeof unitSelection, key: keyof typeof unitSelection,
@ -454,6 +536,10 @@ export default function FormTaskTaEdit() {
} }
}; };
const handleExpertChange = () => {
return Array.from(checkedLevels).join(",");
};
const save = async (data: TaskSchema) => { const save = async (data: TaskSchema) => {
const fileTypeMapping = { const fileTypeMapping = {
all: "1", all: "1",
@ -483,16 +569,11 @@ export default function FormTaskTaEdit() {
const requestData: { const requestData: {
id?: any; id?: any;
title: string; title: string;
assignedToLevel: any;
assignedToUsers: any; assignedToUsers: any;
assignmentTypeId: string; assignmentTypeId: string;
fileTypeOutput: string;
narration: string; narration: string;
platformType: string | null;
assignmentMainTypeId: any;
assignmentType: string; assignmentType: string;
assignedToRole: string; assignedToRole: string;
broadcastType: string;
expertCompetencies: string; expertCompetencies: string;
attachmentUrl: string[]; attachmentUrl: string[];
} = { } = {
@ -500,17 +581,12 @@ export default function FormTaskTaEdit() {
// assignmentType, // assignmentType,
// assignmentCategory, // assignmentCategory,
id: detail?.id || null, id: detail?.id || null,
assignedToLevel: handlePoldaPolresChange(), assignedToUsers: handleExpertChange(),
assignedToUsers: assignmentPurposeString,
assignedToRole: selectedTarget, assignedToRole: selectedTarget,
assignmentType: taskType, assignmentType: taskType,
broadcastType: broadcastType,
assignmentMainTypeId: mainType,
assignmentTypeId: type, assignmentTypeId: type,
fileTypeOutput: selectedOutputs,
narration: data.naration, narration: data.naration,
platformType: "", expertCompetencies: Array.from(selectedCompetencies).join(","),
expertCompetencies: "1,2,3",
title: data.title, title: data.title,
attachmentUrl: links, attachmentUrl: links,
}; };
@ -808,47 +884,97 @@ export default function FormTaskTaEdit() {
<div className="mt-5 space-y-2"> <div className="mt-5 space-y-2">
<Label>{t("areas-expertise")}</Label> <Label>{t("areas-expertise")}</Label>
<div className="flex flex-wrap gap-4"> <div className="flex flex-wrap gap-4">
{Object.keys(expertise).map((key) => ( {userCompetencies?.map((item: any) => (
<div className="flex items-center gap-2" key={key}> <div className="flex items-center gap-2" key={item.id}>
<Checkbox <Checkbox
id={key} id={`comp-${item.id}`}
checked={expertise[key as keyof typeof expertise]} checked={selectedCompetencies.has(item.id)}
onCheckedChange={(value) => onCheckedChange={() => handleCompetencyChange(item.id)}
handleExpertiseOutputChange(
key as keyof typeof expertise,
value as boolean
)
}
/> />
<Label htmlFor={key}> <Label htmlFor={`comp-${item.id}`}>{item.name}</Label>
{key.charAt(0).toUpperCase() + key.slice(1)}
</Label>
</div> </div>
))} ))}
</div> </div>
</div> </div>
<div className="mt-5 space-y-2"> <div className="mt-5 space-y-2">
<Label>{t("choose-expert")}</Label> <Label>{t("choose-expert")}</Label>
<div className="flex flex-wrap gap-4"> <div className="flex flex-wrap gap-4">
{Object.keys(expert).map((key) => ( <Dialog>
<div className="flex items-center gap-2" key={key}> <DialogTrigger asChild>
<Checkbox <Button variant="soft" size="sm" color="primary">
id={key} [{"Pilih Tenaga Ahli"}]
checked={expert[key as keyof typeof expert]} </Button>
onCheckedChange={(value) => </DialogTrigger>
handleExpertOutputChange( <DialogContent className="sm:max-w-[425px] md:max-w-[500px] lg:max-w-[1500px]">
key as keyof typeof expert, <DialogHeader>
value as boolean <DialogTitle>Daftar Tenaga Ahli</DialogTitle>
) </DialogHeader>
} <div className="grid grid-cols-2 gap-2 max-h-[400px] overflow-y-auto">
/> {listExpert?.map((expert: any) => (
<Label htmlFor={key}> <div key={expert.id} className="border p-2">
{key.charAt(0).toUpperCase() + key.slice(1)} <Label className="flex items-center">
</Label> <Checkbox
</div> checked={checkedLevels.has(expert.id)}
))} onCheckedChange={() =>
handleCheckboxChange(expert.id)
}
className="mr-3"
/>
<div className="flex flex-col gap-2">
<div className="font-bold">
{expert.fullname}
</div>
<div className="italic">
({expert.username})
</div>
</div>
</Label>
</div>
))}
</div>
</DialogContent>
</Dialog>
</div> </div>
{checkedLevels.size > 0 && (
<div className="mt-3">
<Label className="text-sm text-gray-600 mb-2 block">
Tenaga Ahli Terpilih ({checkedLevels.size})
</Label>
<div className="flex flex-wrap gap-2">
{Array.from(checkedLevels).map((expertId) => {
const expert = listExpert?.find(
(exp: any) => exp.id === expertId
);
return expert ? (
<div
key={expert.id}
className="inline-flex items-center gap-2 bg-blue-100 text-blue-800 text-sm font-medium px-3 py-1.5 rounded-full border border-blue-200"
>
<span>{expert.fullname}</span>
<button
type="button"
onClick={() => handleCheckboxChange(expert.id)}
className="ml-1 text-blue-600 hover:text-blue-800 hover:bg-blue-200 rounded-full p-0.5 transition-colors"
title="Remove expert"
>
<svg
className="w-3 h-3"
fill="currentColor"
viewBox="0 0 20 20"
>
<path
fillRule="evenodd"
d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
clipRule="evenodd"
/>
</svg>
</button>
</div>
) : null;
})}
</div>
</div>
)}
</div> </div>
<div className="mt-5 space-y-2"> <div className="mt-5 space-y-2">
@ -1079,13 +1205,42 @@ export default function FormTaskTaEdit() {
/> />
</div> </div>
))} ))}
<button <div className="mt-4 space-y-2">
type="button" <Label className="">{t("news-links")}</Label>
className="mt-4 bg-green-500 text-white px-4 py-2 rounded" {links.map((link, index) => (
onClick={handleAddLink} <div
> key={index}
{t("add-links")} className="flex items-center gap-2 mt-2"
</button> >
<Input
type="url"
className="border rounded p-2 w-full"
placeholder={`Masukkan link berita ${index + 1}`}
value={link}
onChange={(e) =>
handleLinkChange(index, e.target.value)
}
/>
{links.length > 1 && (
<button
type="button"
className="bg-red-500 text-white px-3 py-1 rounded"
onClick={() => handleRemoveRow(index)}
>
<Trash2 className="h-4 w-4" />
</button>
)}
</div>
))}
<Button
type="button"
className="mt-2 bg-blue-500 text-white px-4 py-2 rounded"
onClick={handleAddRow}
size="sm"
>
{t("add-links")}
</Button>
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -784,6 +784,8 @@
"assignment-type": "Assignment Type", "assignment-type": "Assignment Type",
"description-task": "Description Task", "description-task": "Description Task",
"form-task": "Form Task", "form-task": "Form Task",
"form-task-ta": "Form Ask the Expert",
"form-task-ta-do": "Form Do it yourself",
"assignment-selection": "Assignment Recipient", "assignment-selection": "Assignment Recipient",
"custom": "Costum", "custom": "Costum",
"assigment-type": "Assigment Type", "assigment-type": "Assigment Type",

View File

@ -571,47 +571,33 @@
"divisionNews": "Berita Satker", "divisionNews": "Berita Satker",
"areaCoverage": "Liputan Wilayah & Satker", "areaCoverage": "Liputan Wilayah & Satker",
"calendar": "KALENDER ACARA", "calendar": "KALENDER ACARA",
"january": "Januari", "january": "Januari",
"february": "February", "february": "February",
"march": "March", "march": "March",
"may": "May", "may": "May",
"june": "Juni", "june": "Juni",
"july": "Juli", "july": "Juli",
"august": "Agustus", "august": "Agustus",
"october": "Oktober", "october": "Oktober",
"december": "Desember", "december": "Desember",
"eventList": "Daftar Acara", "eventList": "Daftar Acara",
"noEvent": "Tidak ada acara yang tersedia", "noEvent": "Tidak ada acara yang tersedia",
"eventDetails": "Detail Acara", "eventDetails": "Detail Acara",
"date": "Tanggal:", "date": "Tanggal:",
"selectEvent": "Pilih acara untuk melihat detail", "selectEvent": "Pilih acara untuk melihat detail",
"regionalPolice": "Polda Jajaran", "regionalPolice": "Polda Jajaran",
"policeDivision": "Satuan Kerja Polri", "policeDivision": "Satuan Kerja Polri",
"close": "Tutup", "close": "Tutup",
"survey1": "SURVEI KEPUASAN PENGGUNA MEDIAHUB POLRI", "survey1": "SURVEI KEPUASAN PENGGUNA MEDIAHUB POLRI",
"survey2": "Kami menghargai pendapat Anda! Survei ini bertujuan untuk meningkatkan kualitas layanan MediaHub Polri. Mohon luangkan waktu beberapa menit untuk mengisi survei ini.", "survey2": "Kami menghargai pendapat Anda! Survei ini bertujuan untuk meningkatkan kualitas layanan MediaHub Polri. Mohon luangkan waktu beberapa menit untuk mengisi survei ini.",
"survey3": "SURVEY SEKARANG", "survey3": "SURVEY SEKARANG",
"survey4": "SURVEI KEPUASAN PENGGUNA MEDIAHUB POLRI", "survey4": "SURVEI KEPUASAN PENGGUNA MEDIAHUB POLRI",
"survey5": "Kami menghargai pendapat Anda! Survei ini bertujuan untuk meningkatkan kualitas layanan MediaHub Polri.", "survey5": "Kami menghargai pendapat Anda! Survei ini bertujuan untuk meningkatkan kualitas layanan MediaHub Polri.",
"survey6": "1. Seberapa sering Anda mengakses MediaHub Polri?", "survey6": "1. Seberapa sering Anda mengakses MediaHub Polri?",
"survey7": "2. Bagaimana pengalaman Anda dalam mengakses website ini?", "survey7": "2. Bagaimana pengalaman Anda dalam mengakses website ini?",
"survey8": "3. Seberapa puas Anda dengan informasi yang tersedia di MediaHub Polri?", "survey8": "3. Seberapa puas Anda dengan informasi yang tersedia di MediaHub Polri?",
"survey9": "4. Apakah Anda merasa website ini membantu dalam mendapatkan informasi terkait Polri?", "survey9": "4. Apakah Anda merasa website ini membantu dalam mendapatkan informasi terkait Polri?",
"survey10": "5. Apa saran atau masukan Anda?" "survey10": "5. Apa saran atau masukan Anda?"
}, },
"FilterPage": { "FilterPage": {
"image": "Foto", "image": "Foto",
@ -798,6 +784,8 @@
"assignment-type": "Jenis Penugasan", "assignment-type": "Jenis Penugasan",
"description-task": "Narasi Penugasan", "description-task": "Narasi Penugasan",
"form-task": "Form Penugasan", "form-task": "Form Penugasan",
"form-task-ta": "Form Tanya Tenaga Ahli",
"form-task-ta-do": "Form Tenaga Ahli Kurasi",
"assignment-selection": "Penerima Tugas", "assignment-selection": "Penerima Tugas",
"custom": "Kostum", "custom": "Kostum",
"assigment-type": "Jenis Tugas", "assigment-type": "Jenis Tugas",

View File

@ -108,6 +108,11 @@ export async function acceptAssignment(id: any) {
return httpPostInterceptor(url, id); return httpPostInterceptor(url, id);
} }
export async function acceptAssignmentTa(id: any) {
const url = `assignment-expert/acceptance?id=${id}`;
return httpPostInterceptor(url, id);
}
export async function postFinishAcceptance(id: any) { export async function postFinishAcceptance(id: any) {
const url = `assignment/finish-acceptance?id=${id}`; const url = `assignment/finish-acceptance?id=${id}`;
return httpPostInterceptor(url, id); return httpPostInterceptor(url, id);
@ -128,6 +133,11 @@ export async function finishTask(id: any) {
return httpPostInterceptor(url); return httpPostInterceptor(url);
} }
export async function finishTaskTa(id: any) {
const url = `assignment-expert/finish?id=${id}`;
return httpPostInterceptor(url);
}
export async function listTaskTa( export async function listTaskTa(
page: any, page: any,
title: string = "", title: string = "",