Merge branch 'dev-anang' of https://gitlab.com/hanifsalafi/mediahub_redesign
This commit is contained in:
commit
199aaf5808
|
|
@ -9,12 +9,7 @@ import { Button } from "@/components/ui/button";
|
|||
import { Label } from "@/components/ui/label";
|
||||
import { Calendar } from "@/components/ui/calendar";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import {
|
||||
Book,
|
||||
CheckCheck,
|
||||
Plus,
|
||||
Timer,
|
||||
} from "lucide-react";
|
||||
import { Book, CheckCheck, Plus, Timer } from "lucide-react";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { EventContentArg } from "@fullcalendar/core";
|
||||
import EventModal from "./event-modal";
|
||||
|
|
@ -124,7 +119,7 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
|||
const [calendarEvents, setCalendarEvents] = useState<CalendarEvent[]>([]);
|
||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||
const t = useTranslations("CalendarApp");
|
||||
|
||||
|
||||
// Modal states
|
||||
const [sheetOpen, setSheetOpen] = useState<boolean>(false);
|
||||
const [date, setDate] = useState<Date>(new Date());
|
||||
|
|
@ -171,7 +166,7 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
|||
const monthData = res?.data?.data;
|
||||
if (monthData) {
|
||||
const allEvents: CalendarEvent[] = [];
|
||||
// Map API data to the calendarEvents structure
|
||||
// Map API data to the calendarEvents structure
|
||||
const events = monthData?.map((event: any) => ({
|
||||
id: event.id.toString(),
|
||||
title: event.title,
|
||||
|
|
@ -207,7 +202,7 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
|||
"",
|
||||
""
|
||||
);
|
||||
|
||||
|
||||
if (res?.error) {
|
||||
error(res?.message || "Failed to fetch yearly events");
|
||||
return;
|
||||
|
|
@ -230,17 +225,20 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
|||
const filteredEvents = calendarEvents.filter((event) => {
|
||||
if (!selectedCategory.length) return false;
|
||||
console.log("Event category : ", selectedCategory);
|
||||
|
||||
|
||||
const eventCategories = event.extendedProps.calendar
|
||||
?.split(",")
|
||||
.map((val: string) => val.trim());
|
||||
|
||||
const allCategoryId = ["1", "2", "3", "4", "5"];
|
||||
// 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) =>
|
||||
selectedCategory.includes(cat) || (hasAllCategories && cat == "0")
|
||||
return eventCategories?.some(
|
||||
(cat: string) =>
|
||||
selectedCategory.includes(cat) || (hasAllCategories && cat == "0")
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -266,7 +264,7 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
|||
};
|
||||
|
||||
const handleCategorySelection = (category: string) => {
|
||||
setSelectedCategory(prev => {
|
||||
setSelectedCategory((prev) => {
|
||||
if (prev.includes(category)) {
|
||||
return prev.filter((c) => c !== category);
|
||||
} else {
|
||||
|
|
@ -313,7 +311,7 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
|||
const getEventColor = (type: EventType): string => {
|
||||
const typeSplit = type.split(",");
|
||||
const firstType = typeSplit[0] as EventType;
|
||||
|
||||
|
||||
const colors: Record<EventType, string> = {
|
||||
"0": "bg-black",
|
||||
"1": "bg-yellow-500",
|
||||
|
|
@ -322,7 +320,7 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
|||
"4": "bg-orange-500",
|
||||
"5": "bg-green-400",
|
||||
};
|
||||
|
||||
|
||||
return colors[firstType] || "bg-gray-400";
|
||||
};
|
||||
|
||||
|
|
@ -346,15 +344,22 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
|||
|
||||
const renderEventContent = (eventInfo: any) => {
|
||||
const { title } = eventInfo.event;
|
||||
const { createdByName, isPublish, calendar } = eventInfo.event.extendedProps;
|
||||
const { createdByName, isPublish, calendar } =
|
||||
eventInfo.event.extendedProps;
|
||||
const bgColor = getEventColor(calendar);
|
||||
const colorList = getEventColorList(calendar);
|
||||
|
||||
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 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>
|
||||
</div>
|
||||
<p className="ml-1 text-xs text-start mt-2">
|
||||
|
|
@ -386,7 +391,7 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
|||
bgColor,
|
||||
colorList,
|
||||
}) => (
|
||||
<div
|
||||
<div
|
||||
className={`w-full p-2 mb-2 rounded-md text-white text-sm flex justify-between items-stretch cursor-pointer hover:opacity-80 ${bgColor}`}
|
||||
onClick={() => handleClickListItem(item)}
|
||||
>
|
||||
|
|
@ -461,8 +466,12 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
|||
text={event.title}
|
||||
createdBy={event.createdByName}
|
||||
isPublish={event.isPublish}
|
||||
bgColor={getEventColor(event.agendaType as EventType)}
|
||||
colorList={getEventColorList(event.agendaType as EventType)}
|
||||
bgColor={getEventColor(
|
||||
event.agendaType as EventType
|
||||
)}
|
||||
colorList={getEventColorList(
|
||||
event.agendaType as EventType
|
||||
)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
|
@ -537,7 +546,7 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
|||
{t("addEvent")}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
|
||||
{roleId === 3 && userLevelId === 216 && (
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogTrigger asChild>
|
||||
|
|
@ -593,7 +602,9 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
|||
className={category.className}
|
||||
id={category.label}
|
||||
checked={selectedCategory.includes(category.value)}
|
||||
onCheckedChange={() => handleCategorySelection(category.value)}
|
||||
onCheckedChange={() =>
|
||||
handleCategorySelection(category.value)
|
||||
}
|
||||
/>
|
||||
<Label htmlFor={category.label}>{category.label}</Label>
|
||||
</li>
|
||||
|
|
@ -605,7 +616,12 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
|||
<Card className="col-span-12 lg:col-span-8 2xl:col-span-9 pt-5">
|
||||
<CardContent className="dashcode-app-calendar">
|
||||
<FullCalendar
|
||||
plugins={[dayGridPlugin, timeGridPlugin, interactionPlugin, listPlugin]}
|
||||
plugins={[
|
||||
dayGridPlugin,
|
||||
timeGridPlugin,
|
||||
interactionPlugin,
|
||||
listPlugin,
|
||||
]}
|
||||
headerToolbar={{
|
||||
left: "prev,next today",
|
||||
center: "title",
|
||||
|
|
@ -636,7 +652,9 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
|||
handleDateChange(info.view.currentStart, info.view.currentEnd);
|
||||
handleViewChange(info.view.type);
|
||||
}}
|
||||
viewClassNames={activeView === "listYear" ? "hide-calendar-grid" : ""}
|
||||
viewClassNames={
|
||||
activeView === "listYear" ? "hide-calendar-grid" : ""
|
||||
}
|
||||
/>
|
||||
|
||||
{activeView === "listYear" && (
|
||||
|
|
@ -657,7 +675,7 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
|||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
|
||||
<EventModal
|
||||
open={sheetOpen}
|
||||
onClose={handleCloseModal}
|
||||
|
|
@ -669,4 +687,4 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
|||
);
|
||||
};
|
||||
|
||||
export default CalendarView;
|
||||
export default CalendarView;
|
||||
|
|
|
|||
|
|
@ -148,10 +148,12 @@ const EventModal = ({
|
|||
satker: false,
|
||||
international: false,
|
||||
});
|
||||
|
||||
const [agendaType, setAgendaType] = React.useState(""); // State untuk agendaType
|
||||
const [selectedPolda, setSelectedPolda] = React.useState([]); // Untuk data Polda
|
||||
const [selectedSatker, setSelectedSatker] = React.useState([]);
|
||||
const [selectedPolres, setSelectedPolres] = React.useState([]);
|
||||
const [selectedPolda, setSelectedPolda] = useState<string[]>([]);
|
||||
const [selectedPolres, setSelectedPolres] = useState<string[]>([]);
|
||||
const [selectedSatker, setSelectedSatker] = useState<string[]>([]);
|
||||
const isDetailMode = true;
|
||||
const [wavesurfer, setWavesurfer] = useState<WaveSurfer>();
|
||||
const [isPlaying, setIsPlaying] = useState(false);
|
||||
const [isPublishing, setIsPublishing] = useState(false);
|
||||
|
|
@ -174,10 +176,7 @@ const EventModal = ({
|
|||
const detail = res?.data?.data;
|
||||
setDetailData(detail);
|
||||
|
||||
const description = res?.data?.data?.description;
|
||||
console.log("description", res?.data?.data?.description);
|
||||
|
||||
// Set nilai awal description ke form control
|
||||
const description = detail?.description;
|
||||
if (description) {
|
||||
setValue("description", description);
|
||||
}
|
||||
|
|
@ -196,17 +195,56 @@ const EventModal = ({
|
|||
attachments?.filter((file: any) => file.fileTypeId == 4)
|
||||
);
|
||||
|
||||
const agendaType = detail?.agendaType;
|
||||
setWilayahPublish({
|
||||
semua: agendaType === "all",
|
||||
nasional: agendaType === "mabes",
|
||||
polda: agendaType === "polda",
|
||||
polres: agendaType === "polres",
|
||||
satker: agendaType === "satker",
|
||||
international: agendaType === "international",
|
||||
});
|
||||
}
|
||||
const rawAgendaTypes = detail?.agendaType?.split(",") || []; // ["0","1","2","3","4","5"]
|
||||
const assignedToLevel = detail?.assignedToLevel?.split(",") || [];
|
||||
|
||||
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();
|
||||
}, [event, setValue]);
|
||||
|
||||
|
|
@ -233,33 +271,39 @@ const EventModal = ({
|
|||
|
||||
const toggleWilayah = (key: string) => {
|
||||
setWilayahPublish((prev: any) => {
|
||||
const newState = { ...prev, [key]: !prev[key] };
|
||||
let newState = { ...prev };
|
||||
|
||||
// Handle "semua" logic to check all options
|
||||
if (key === "semua" && newState.semua) {
|
||||
setAgendaType("all");
|
||||
return {
|
||||
semua: true,
|
||||
nasional: true,
|
||||
polda: true,
|
||||
polres: true,
|
||||
satker: true,
|
||||
international: true,
|
||||
// Jika key === semua dan sebelumnya belum aktif, aktifkan semua
|
||||
if (key === "semua") {
|
||||
const newChecked = !prev.semua;
|
||||
newState = {
|
||||
semua: newChecked,
|
||||
nasional: newChecked,
|
||||
polda: newChecked,
|
||||
polres: newChecked,
|
||||
satker: newChecked,
|
||||
international: newChecked,
|
||||
};
|
||||
|
||||
if (newChecked) {
|
||||
setAgendaType("0,1,2,3,4,5");
|
||||
} else {
|
||||
setAgendaType("");
|
||||
}
|
||||
|
||||
return newState;
|
||||
}
|
||||
|
||||
// Uncheck "semua" if any other option is selected
|
||||
if (key !== "semua") {
|
||||
newState.semua = false;
|
||||
}
|
||||
// Jika key bukan "semua"
|
||||
newState[key] = !prev[key];
|
||||
newState.semua = false; // Uncheck "semua" jika yang dipilih adalah individu
|
||||
|
||||
// Set agendaType based on the selected checkbox
|
||||
if (newState.nasional) setAgendaType("mabes");
|
||||
else if (newState.polda) setAgendaType("polda");
|
||||
else if (newState.polres) setAgendaType("polres");
|
||||
else if (newState.satker) setAgendaType("satker");
|
||||
else if (newState.international) setAgendaType("international");
|
||||
else setAgendaType(""); // Reset if no checkbox is selected
|
||||
// Hitung ulang agendaType berdasarkan pilihan
|
||||
const selectedKeys = Object.entries(newState)
|
||||
.filter(([k, v]) => v && k !== "semua")
|
||||
.map(([k]) => wilayahValueMap[k]);
|
||||
|
||||
setAgendaType(selectedKeys.join(","));
|
||||
|
||||
return newState;
|
||||
});
|
||||
|
|
@ -269,12 +313,25 @@ const EventModal = ({
|
|||
const agendaTypeList: string[] = [];
|
||||
const assignedToLevelList: string[] = [];
|
||||
|
||||
// Mapping dari checkbox wilayah ke agendaType
|
||||
Object.keys(wilayahPublish).forEach((key) => {
|
||||
if (wilayahPublish[key as keyof typeof wilayahPublish]) {
|
||||
agendaTypeList.push(wilayahValueMap[key]);
|
||||
}
|
||||
});
|
||||
// // Mapping dari checkbox wilayah ke agendaType
|
||||
// Object.keys(wilayahPublish).forEach((key) => {
|
||||
// if (wilayahPublish[key as keyof typeof wilayahPublish]) {
|
||||
// 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
|
||||
if (wilayahPublish.polda && selectedPolda.length > 0) {
|
||||
|
|
@ -558,6 +615,7 @@ const EventModal = ({
|
|||
confirmButtonText: "OK",
|
||||
}).then(() => {
|
||||
router.push(redirect);
|
||||
window.location.reload();
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -784,7 +842,8 @@ const EventModal = ({
|
|||
{wilayahPublish.polda && (
|
||||
<UnitMapping
|
||||
unit="Polda"
|
||||
isDetail={false}
|
||||
isDetail={isDetailMode} // jika Anda punya kondisi detail
|
||||
initData={selectedPolda}
|
||||
sendDataToParent={(data: any) =>
|
||||
setSelectedPolda(data)
|
||||
}
|
||||
|
|
@ -802,8 +861,9 @@ const EventModal = ({
|
|||
</label>
|
||||
{wilayahPublish.polres && (
|
||||
<UnitMapping
|
||||
isDetail={false}
|
||||
unit="Polres"
|
||||
isDetail={isDetailMode}
|
||||
initData={selectedPolres}
|
||||
sendDataToParent={(data: any) =>
|
||||
setSelectedPolres(data)
|
||||
}
|
||||
|
|
@ -821,8 +881,9 @@ const EventModal = ({
|
|||
</label>
|
||||
{wilayahPublish.satker && (
|
||||
<UnitMapping
|
||||
isDetail={false}
|
||||
unit="Satker"
|
||||
isDetail={isDetailMode}
|
||||
initData={selectedSatker}
|
||||
sendDataToParent={(data: any) =>
|
||||
setSelectedSatker(data)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -202,6 +202,23 @@ export default function FormAskExpert() {
|
|||
const details = response?.data?.data;
|
||||
|
||||
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();
|
||||
|
|
@ -515,7 +532,7 @@ export default function FormAskExpert() {
|
|||
return (
|
||||
<Card>
|
||||
<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 ? (
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<div className="gap-5 mb-5">
|
||||
|
|
@ -617,7 +634,14 @@ export default function FormAskExpert() {
|
|||
}
|
||||
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>
|
||||
</div>
|
||||
))}
|
||||
|
|
@ -625,6 +649,46 @@ export default function FormAskExpert() {
|
|||
</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 className="mt-5 space-y-2">
|
||||
|
|
|
|||
|
|
@ -636,7 +636,7 @@ export default function FormDoItYourself() {
|
|||
return (
|
||||
<Card>
|
||||
<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 ? (
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<div className="gap-5 mb-5">
|
||||
|
|
|
|||
|
|
@ -10,42 +10,23 @@ import * as z from "zod";
|
|||
import Swal from "sweetalert2";
|
||||
import withReactContent from "sweetalert2-react-content";
|
||||
import { useParams, useRouter } from "next/navigation";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
||||
import JoditEditor from "jodit-react";
|
||||
import {
|
||||
acceptAssignment,
|
||||
acceptAssignmentTa,
|
||||
createAssignmentResponse,
|
||||
createTask,
|
||||
deleteAssignmentResponse,
|
||||
deleteTask,
|
||||
finishTask,
|
||||
finishTaskTa,
|
||||
getAcceptance,
|
||||
getAcceptanceAssignmentStatus,
|
||||
getAssignmentResponseList,
|
||||
getMediaUpload,
|
||||
getMediaUploadTa,
|
||||
getTask,
|
||||
getTaskTa,
|
||||
getUserLevelForAssignments,
|
||||
getUserLevelForExpert,
|
||||
} from "@/service/task";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "@/components/ui/dialog";
|
||||
import {
|
||||
ChevronDown,
|
||||
ChevronUp,
|
||||
Dock,
|
||||
DotSquare,
|
||||
ImageIcon,
|
||||
|
|
@ -61,14 +42,20 @@ import { close, error, loading } from "@/lib/swal";
|
|||
import { getCookiesDecrypt } from "@/lib/utils";
|
||||
import { Avatar, AvatarImage } from "@/components/ui/avatar";
|
||||
import { successCallback } from "@/config/swal";
|
||||
import FileUploader from "../shared/file-uploader";
|
||||
import { AudioRecorder } from "react-audio-voice-recorder";
|
||||
import Image from "next/image";
|
||||
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||
import WavesurferPlayer from "@wavesurfer/react";
|
||||
import WaveSurfer from "wavesurfer.js";
|
||||
import { InputGroup, InputGroupText } from "@/components/ui/input-group";
|
||||
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({
|
||||
uniqueCode: z.string().min(1, { message: "Judul diperlukan" }),
|
||||
|
|
@ -252,7 +239,11 @@ export default function FormTaskTaDetail() {
|
|||
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 [dataTable, setDataTable] = React.useState<any[]>([]);
|
||||
const [totalData, setTotalData] = React.useState<number>(1);
|
||||
|
|
@ -327,37 +318,37 @@ export default function FormTaskTaDetail() {
|
|||
// setPlatformTypeVisible(selectedValue === 2);
|
||||
// };
|
||||
|
||||
const handleExpertiseOutputChange = (
|
||||
key: keyof typeof expertise,
|
||||
value: boolean
|
||||
) => {
|
||||
if (key === "semua") {
|
||||
const newState = {
|
||||
semua: value,
|
||||
komunikasi: value,
|
||||
hukum: value,
|
||||
bahasa: value,
|
||||
ekonomi: value,
|
||||
politik: value,
|
||||
sosiologi: value,
|
||||
ilmuadministrasipemerintah: value,
|
||||
ti: value,
|
||||
};
|
||||
setExpertiseOutput(newState);
|
||||
} else {
|
||||
const updated = {
|
||||
...expertise,
|
||||
[key]: value,
|
||||
};
|
||||
// const handleExpertiseOutputChange = (
|
||||
// key: keyof typeof expertise,
|
||||
// value: boolean
|
||||
// ) => {
|
||||
// if (key === "semua") {
|
||||
// const newState = {
|
||||
// semua: value,
|
||||
// komunikasi: value,
|
||||
// hukum: value,
|
||||
// bahasa: value,
|
||||
// ekonomi: value,
|
||||
// politik: value,
|
||||
// sosiologi: value,
|
||||
// ilmuadministrasipemerintah: value,
|
||||
// ti: value,
|
||||
// };
|
||||
// setExpertiseOutput(newState);
|
||||
// } else {
|
||||
// const updated = {
|
||||
// ...expertise,
|
||||
// [key]: value,
|
||||
// };
|
||||
|
||||
const allChecked = ["video", "audio", "image", "text"].every(
|
||||
(k) => updated[k as keyof typeof expertise]
|
||||
);
|
||||
// const allChecked = ["video", "audio", "image", "text"].every(
|
||||
// (k) => updated[k as keyof typeof expertise]
|
||||
// );
|
||||
|
||||
updated.semua = allChecked;
|
||||
setExpertiseOutput(updated);
|
||||
}
|
||||
};
|
||||
// updated.semua = allChecked;
|
||||
// setExpertiseOutput(updated);
|
||||
// }
|
||||
// };
|
||||
|
||||
const handleExpertOutputChange = (
|
||||
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(() => {
|
||||
async function fetchPoldaPolres() {
|
||||
setIsLoading(true);
|
||||
|
|
@ -458,6 +511,16 @@ export default function FormTaskTaDetail() {
|
|||
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;
|
||||
setImageUploadedFiles(
|
||||
attachment?.filter((file: any) => file.fileTypeId == 1)
|
||||
|
|
@ -542,7 +605,7 @@ export default function FormTaskTaDetail() {
|
|||
confirmButtonText: "OK",
|
||||
}).then((result) => {
|
||||
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 () => {
|
||||
loading();
|
||||
console.log("Id user :", userId);
|
||||
const response = await acceptAssignment(id);
|
||||
const response = await acceptAssignmentTa(id);
|
||||
|
||||
if (response?.error) {
|
||||
error(response?.message);
|
||||
|
|
@ -773,7 +836,7 @@ export default function FormTaskTaDetail() {
|
|||
);
|
||||
|
||||
async function finishAssignment() {
|
||||
const response = finishTask(id);
|
||||
const response = finishTaskTa(id);
|
||||
|
||||
// if (response.error) {
|
||||
// error(response.message);
|
||||
|
|
@ -1011,48 +1074,98 @@ export default function FormTaskTaDetail() {
|
|||
<div className="mt-5 space-y-2">
|
||||
<Label>{t("areas-expertise")}</Label>
|
||||
<div className="flex flex-wrap gap-4">
|
||||
{Object.keys(expertise).map((key) => (
|
||||
<div className="flex items-center gap-2" key={key}>
|
||||
{userCompetencies?.map((item: any) => (
|
||||
<div className="flex items-center gap-2" key={item.id}>
|
||||
<Checkbox
|
||||
id={key}
|
||||
checked={expertise[key as keyof typeof expertise]}
|
||||
onCheckedChange={(value) =>
|
||||
handleExpertiseOutputChange(
|
||||
key as keyof typeof expertise,
|
||||
value as boolean
|
||||
)
|
||||
}
|
||||
id={`comp-${item.id}`}
|
||||
checked={selectedCompetencies.has(item.id)}
|
||||
onCheckedChange={() => handleCompetencyChange(item.id)}
|
||||
disabled
|
||||
/>
|
||||
<Label htmlFor={key}>
|
||||
{key.charAt(0).toUpperCase() + key.slice(1)}
|
||||
</Label>
|
||||
<Label htmlFor={`comp-${item.id}`}>{item.name}</Label>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-5 space-y-2">
|
||||
<Label>{t("choose-expert")}</Label>
|
||||
{/* <Label>{t("choose-expert")}</Label>
|
||||
<div className="flex flex-wrap gap-4">
|
||||
{Object.keys(expert).map((key) => (
|
||||
<div className="flex items-center gap-2" key={key}>
|
||||
<Checkbox
|
||||
id={key}
|
||||
checked={expert[key as keyof typeof expert]}
|
||||
onCheckedChange={(value) =>
|
||||
handleExpertOutputChange(
|
||||
key as keyof typeof expert,
|
||||
value as boolean
|
||||
)
|
||||
}
|
||||
/>
|
||||
<Label htmlFor={key}>
|
||||
{key.charAt(0).toUpperCase() + key.slice(1)}
|
||||
</Label>
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<Button variant="soft" size="sm" color="primary">
|
||||
[{"Pilih Tenaga Ahli"}]
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="sm:max-w-[425px] md:max-w-[500px] lg:max-w-[1500px]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Daftar Tenaga Ahli</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="grid grid-cols-2 gap-2 max-h-[400px] overflow-y-auto">
|
||||
{listExpert?.map((expert: any) => (
|
||||
<div key={expert.id} className="border p-2">
|
||||
<Label className="flex items-center">
|
||||
<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>
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import {
|
|||
getTask,
|
||||
getTaskTa,
|
||||
getUserLevelForAssignments,
|
||||
getUserLevelForExpert,
|
||||
} from "@/service/task";
|
||||
import {
|
||||
Dialog,
|
||||
|
|
@ -40,6 +41,7 @@ import {
|
|||
Dock,
|
||||
ImageIcon,
|
||||
Music,
|
||||
Trash2,
|
||||
VideoIcon,
|
||||
} from "lucide-react";
|
||||
import FileUploader from "../shared/file-uploader";
|
||||
|
|
@ -52,6 +54,7 @@ import { Upload } from "tus-js-client";
|
|||
import { error, loading } from "@/lib/swal";
|
||||
import dynamic from "next/dynamic";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { getListCompetencies } from "@/service/management-user/management-user";
|
||||
|
||||
const taskSchema = z.object({
|
||||
// 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 [type, setType] = useState<string>("1");
|
||||
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 [urlInputs, setUrlInputs] = useState<Url[]>([]);
|
||||
const [refresh] = useState(false);
|
||||
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 [isLoading, setIsLoading] = useState(false);
|
||||
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(() => {
|
||||
async function fetchPoldaPolres() {
|
||||
setIsLoading(true);
|
||||
|
|
@ -285,12 +357,22 @@ export default function FormTaskTaEdit() {
|
|||
}
|
||||
|
||||
if (details?.assignedToLevel) {
|
||||
const levels = new Set(
|
||||
details.assignedToLevel.split(",").map(Number)
|
||||
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));
|
||||
}
|
||||
|
||||
const attachment = details?.files;
|
||||
setImageUploadedFiles(
|
||||
attachment?.filter((file: any) => file.fileTypeId == 1)
|
||||
|
|
@ -376,7 +458,7 @@ export default function FormTaskTaEdit() {
|
|||
confirmButtonText: "OK",
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
router.push("/en/contributor/task");
|
||||
router.push("/en/contributor/task-ta");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
|
@ -393,9 +475,9 @@ export default function FormTaskTaEdit() {
|
|||
});
|
||||
};
|
||||
|
||||
const handlePoldaPolresChange = () => {
|
||||
return Array.from(checkedLevels).join(","); // Mengonversi Set ke string
|
||||
};
|
||||
// const handlePoldaPolresChange = () => {
|
||||
// return Array.from(checkedLevels).join(","); // Mengonversi Set ke string
|
||||
// };
|
||||
|
||||
const handleUnitChange = (
|
||||
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 fileTypeMapping = {
|
||||
all: "1",
|
||||
|
|
@ -483,16 +569,11 @@ export default function FormTaskTaEdit() {
|
|||
const requestData: {
|
||||
id?: any;
|
||||
title: string;
|
||||
assignedToLevel: any;
|
||||
assignedToUsers: any;
|
||||
assignmentTypeId: string;
|
||||
fileTypeOutput: string;
|
||||
narration: string;
|
||||
platformType: string | null;
|
||||
assignmentMainTypeId: any;
|
||||
assignmentType: string;
|
||||
assignedToRole: string;
|
||||
broadcastType: string;
|
||||
expertCompetencies: string;
|
||||
attachmentUrl: string[];
|
||||
} = {
|
||||
|
|
@ -500,17 +581,12 @@ export default function FormTaskTaEdit() {
|
|||
// assignmentType,
|
||||
// assignmentCategory,
|
||||
id: detail?.id || null,
|
||||
assignedToLevel: handlePoldaPolresChange(),
|
||||
assignedToUsers: assignmentPurposeString,
|
||||
assignedToUsers: handleExpertChange(),
|
||||
assignedToRole: selectedTarget,
|
||||
assignmentType: taskType,
|
||||
broadcastType: broadcastType,
|
||||
assignmentMainTypeId: mainType,
|
||||
assignmentTypeId: type,
|
||||
fileTypeOutput: selectedOutputs,
|
||||
narration: data.naration,
|
||||
platformType: "",
|
||||
expertCompetencies: "1,2,3",
|
||||
expertCompetencies: Array.from(selectedCompetencies).join(","),
|
||||
title: data.title,
|
||||
attachmentUrl: links,
|
||||
};
|
||||
|
|
@ -808,47 +884,97 @@ export default function FormTaskTaEdit() {
|
|||
<div className="mt-5 space-y-2">
|
||||
<Label>{t("areas-expertise")}</Label>
|
||||
<div className="flex flex-wrap gap-4">
|
||||
{Object.keys(expertise).map((key) => (
|
||||
<div className="flex items-center gap-2" key={key}>
|
||||
{userCompetencies?.map((item: any) => (
|
||||
<div className="flex items-center gap-2" key={item.id}>
|
||||
<Checkbox
|
||||
id={key}
|
||||
checked={expertise[key as keyof typeof expertise]}
|
||||
onCheckedChange={(value) =>
|
||||
handleExpertiseOutputChange(
|
||||
key as keyof typeof expertise,
|
||||
value as boolean
|
||||
)
|
||||
}
|
||||
id={`comp-${item.id}`}
|
||||
checked={selectedCompetencies.has(item.id)}
|
||||
onCheckedChange={() => handleCompetencyChange(item.id)}
|
||||
/>
|
||||
<Label htmlFor={key}>
|
||||
{key.charAt(0).toUpperCase() + key.slice(1)}
|
||||
</Label>
|
||||
<Label htmlFor={`comp-${item.id}`}>{item.name}</Label>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-5 space-y-2">
|
||||
<Label>{t("choose-expert")}</Label>
|
||||
<div className="flex flex-wrap gap-4">
|
||||
{Object.keys(expert).map((key) => (
|
||||
<div className="flex items-center gap-2" key={key}>
|
||||
<Checkbox
|
||||
id={key}
|
||||
checked={expert[key as keyof typeof expert]}
|
||||
onCheckedChange={(value) =>
|
||||
handleExpertOutputChange(
|
||||
key as keyof typeof expert,
|
||||
value as boolean
|
||||
)
|
||||
}
|
||||
/>
|
||||
<Label htmlFor={key}>
|
||||
{key.charAt(0).toUpperCase() + key.slice(1)}
|
||||
</Label>
|
||||
</div>
|
||||
))}
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<Button variant="soft" size="sm" color="primary">
|
||||
[{"Pilih Tenaga Ahli"}]
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="sm:max-w-[425px] md:max-w-[500px] lg:max-w-[1500px]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Daftar Tenaga Ahli</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="grid grid-cols-2 gap-2 max-h-[400px] overflow-y-auto">
|
||||
{listExpert?.map((expert: any) => (
|
||||
<div key={expert.id} className="border p-2">
|
||||
<Label className="flex items-center">
|
||||
<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 className="mt-5 space-y-2">
|
||||
|
|
@ -1079,13 +1205,42 @@ export default function FormTaskTaEdit() {
|
|||
/>
|
||||
</div>
|
||||
))}
|
||||
<button
|
||||
type="button"
|
||||
className="mt-4 bg-green-500 text-white px-4 py-2 rounded"
|
||||
onClick={handleAddLink}
|
||||
>
|
||||
{t("add-links")}
|
||||
</button>
|
||||
<div className="mt-4 space-y-2">
|
||||
<Label className="">{t("news-links")}</Label>
|
||||
{links.map((link, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="flex items-center gap-2 mt-2"
|
||||
>
|
||||
<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>
|
||||
|
|
|
|||
|
|
@ -784,6 +784,8 @@
|
|||
"assignment-type": "Assignment Type",
|
||||
"description-task": "Description Task",
|
||||
"form-task": "Form Task",
|
||||
"form-task-ta": "Form Ask the Expert",
|
||||
"form-task-ta-do": "Form Do it yourself",
|
||||
"assignment-selection": "Assignment Recipient",
|
||||
"custom": "Costum",
|
||||
"assigment-type": "Assigment Type",
|
||||
|
|
|
|||
|
|
@ -571,47 +571,33 @@
|
|||
"divisionNews": "Berita Satker",
|
||||
"areaCoverage": "Liputan Wilayah & Satker",
|
||||
"calendar": "KALENDER ACARA",
|
||||
"january": "Januari",
|
||||
"february": "February",
|
||||
"march": "March",
|
||||
"may": "May",
|
||||
"june": "Juni",
|
||||
"july": "Juli",
|
||||
"august": "Agustus",
|
||||
"october": "Oktober",
|
||||
"december": "Desember",
|
||||
"eventList": "Daftar Acara",
|
||||
"noEvent": "Tidak ada acara yang tersedia",
|
||||
"eventDetails": "Detail Acara",
|
||||
"date": "Tanggal:",
|
||||
"selectEvent": "Pilih acara untuk melihat detail",
|
||||
"regionalPolice": "Polda Jajaran",
|
||||
"policeDivision": "Satuan Kerja Polri",
|
||||
"close": "Tutup",
|
||||
"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.",
|
||||
"survey3": "SURVEY SEKARANG",
|
||||
"survey4": "SURVEI KEPUASAN PENGGUNA MEDIAHUB POLRI",
|
||||
"survey5": "Kami menghargai pendapat Anda! Survei ini bertujuan untuk meningkatkan kualitas layanan MediaHub Polri.",
|
||||
"survey6": "1. Seberapa sering Anda mengakses MediaHub Polri?",
|
||||
"survey7": "2. Bagaimana pengalaman Anda dalam mengakses website ini?",
|
||||
"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?",
|
||||
"survey10": "5. Apa saran atau masukan Anda?"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
"january": "Januari",
|
||||
"february": "February",
|
||||
"march": "March",
|
||||
"may": "May",
|
||||
"june": "Juni",
|
||||
"july": "Juli",
|
||||
"august": "Agustus",
|
||||
"october": "Oktober",
|
||||
"december": "Desember",
|
||||
"eventList": "Daftar Acara",
|
||||
"noEvent": "Tidak ada acara yang tersedia",
|
||||
"eventDetails": "Detail Acara",
|
||||
"date": "Tanggal:",
|
||||
"selectEvent": "Pilih acara untuk melihat detail",
|
||||
"regionalPolice": "Polda Jajaran",
|
||||
"policeDivision": "Satuan Kerja Polri",
|
||||
"close": "Tutup",
|
||||
"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.",
|
||||
"survey3": "SURVEY SEKARANG",
|
||||
"survey4": "SURVEI KEPUASAN PENGGUNA MEDIAHUB POLRI",
|
||||
"survey5": "Kami menghargai pendapat Anda! Survei ini bertujuan untuk meningkatkan kualitas layanan MediaHub Polri.",
|
||||
"survey6": "1. Seberapa sering Anda mengakses MediaHub Polri?",
|
||||
"survey7": "2. Bagaimana pengalaman Anda dalam mengakses website ini?",
|
||||
"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?",
|
||||
"survey10": "5. Apa saran atau masukan Anda?"
|
||||
},
|
||||
"FilterPage": {
|
||||
"image": "Foto",
|
||||
|
|
@ -798,6 +784,8 @@
|
|||
"assignment-type": "Jenis Penugasan",
|
||||
"description-task": "Narasi Penugasan",
|
||||
"form-task": "Form Penugasan",
|
||||
"form-task-ta": "Form Tanya Tenaga Ahli",
|
||||
"form-task-ta-do": "Form Tenaga Ahli Kurasi",
|
||||
"assignment-selection": "Penerima Tugas",
|
||||
"custom": "Kostum",
|
||||
"assigment-type": "Jenis Tugas",
|
||||
|
|
|
|||
|
|
@ -108,6 +108,11 @@ export async function acceptAssignment(id: any) {
|
|||
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) {
|
||||
const url = `assignment/finish-acceptance?id=${id}`;
|
||||
return httpPostInterceptor(url, id);
|
||||
|
|
@ -128,6 +133,11 @@ export async function finishTask(id: any) {
|
|||
return httpPostInterceptor(url);
|
||||
}
|
||||
|
||||
export async function finishTaskTa(id: any) {
|
||||
const url = `assignment-expert/finish?id=${id}`;
|
||||
return httpPostInterceptor(url);
|
||||
}
|
||||
|
||||
export async function listTaskTa(
|
||||
page: any,
|
||||
title: string = "",
|
||||
|
|
|
|||
Loading…
Reference in New Issue