From bd527e102c97d11ce02dbef638a55aa5420e2193 Mon Sep 17 00:00:00 2001 From: Anang Yusman Date: Wed, 7 May 2025 21:21:55 +0800 Subject: [PATCH] [QUDO-53,QUDO-54] feat:update form create penugasan Ta, update sidebar SPV,update table hasil upload media Ta --- .../admin/add-experts/create/page.tsx | 33 +- .../task-ta/components/columns.tsx | 20 +- .../form/task-ta/task-ta-detail-form.tsx | 3 +- components/form/task-ta/task-ta-form.tsx | 308 ++++++------------ components/main/audio-detail.tsx | 4 +- lib/menus.ts | 28 +- service/task.ts | 15 + 7 files changed, 172 insertions(+), 239 deletions(-) diff --git a/app/[locale]/(protected)/admin/add-experts/create/page.tsx b/app/[locale]/(protected)/admin/add-experts/create/page.tsx index 8c908a52..c9d865e9 100644 --- a/app/[locale]/(protected)/admin/add-experts/create/page.tsx +++ b/app/[locale]/(protected)/admin/add-experts/create/page.tsx @@ -32,6 +32,7 @@ import { saveUserRolePlacements, } from "@/service/management-user/management-user"; import { loading } from "@/config/swal"; +import { Eye, EyeOff } from "lucide-react"; const FormSchema = z.object({ name: z.string({ @@ -79,6 +80,14 @@ export default function AddExpertForm() { const [userCompetencies, setUserCompetencies] = useState(); const [userExperiences, setUserExperiences] = useState(); const [userLevels, setUserLevels] = useState(); + const [passwordType, setPasswordType] = useState("password"); + const [showPassword, setShowPassword] = useState(false); + + const togglePasswordType = () => { + setPasswordType((prevType) => + prevType === "password" ? "text" : "password" + ); + }; const roleSelection = [ { @@ -115,7 +124,7 @@ export default function AddExpertForm() { username: data.username, email: data.email, password: data.password, - adress: "", + address: "", roleId: "EXP-ID", phoneNumber: data.phoneNumber, userCompetencyId: data.skills, @@ -308,12 +317,22 @@ export default function AddExpertForm() { render={({ field }) => ( Password - +
+ + +
)} diff --git a/app/[locale]/(protected)/contributor/task-ta/components/columns.tsx b/app/[locale]/(protected)/contributor/task-ta/components/columns.tsx index 0350e387..7397b64c 100644 --- a/app/[locale]/(protected)/contributor/task-ta/components/columns.tsx +++ b/app/[locale]/(protected)/contributor/task-ta/components/columns.tsx @@ -16,7 +16,7 @@ import { Link } from "@/components/navigation"; import { useRouter } from "next/navigation"; import { useToast } from "@/components/ui/use-toast"; import { deleteCategory } from "@/service/settings/settings"; -import { deleteTask } from "@/service/task"; +import { deleteTaskTa } from "@/service/task"; import { error, loading } from "@/lib/swal"; import withReactContent from "sweetalert2-react-content"; import Swal from "sweetalert2"; @@ -109,7 +109,7 @@ const useTableColumns = () => { async function deleteProcess(id: any) { loading(); - const resDelete = await deleteTask(id); + const resDelete = await deleteTaskTa(id); if (resDelete?.error) { error(resDelete.message); @@ -185,13 +185,15 @@ const useTableColumns = () => { )} - TaskDelete(row.original.id)} - className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none" - > - - Delete - + {roleId == 11 && ( + TaskDelete(row.original.id)} + className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none" + > + + Delete + + )} ); diff --git a/components/form/task-ta/task-ta-detail-form.tsx b/components/form/task-ta/task-ta-detail-form.tsx index dc69a11e..1c0ff814 100644 --- a/components/form/task-ta/task-ta-detail-form.tsx +++ b/components/form/task-ta/task-ta-detail-form.tsx @@ -31,6 +31,7 @@ import { getAcceptanceAssignmentStatus, getAssignmentResponseList, getMediaUpload, + getMediaUploadTa, getTask, getTaskTa, getUserLevelForAssignments, @@ -407,7 +408,7 @@ export default function FormTaskTaDetail() { const fetchAllData = async () => { try { - const response = await getMediaUpload(id, userLevelId); + const response = await getMediaUploadTa(id, userLevelId); setUploadResults(response?.data?.data || []); } catch (error) { console.error("Error fetching all data:", error); diff --git a/components/form/task-ta/task-ta-form.tsx b/components/form/task-ta/task-ta-form.tsx index 16cb4b9f..4d206cda 100644 --- a/components/form/task-ta/task-ta-form.tsx +++ b/components/form/task-ta/task-ta-form.tsx @@ -25,6 +25,7 @@ import { createTaskTa, getTask, getUserLevelForAssignments, + getUserLevelForExpert, } from "@/service/task"; import { Dialog, @@ -54,6 +55,11 @@ import { DateRange } from "react-day-picker"; import TimePicker from "react-time-picker"; import "react-time-picker/dist/TimePicker.css"; import "react-clock/dist/Clock.css"; +import { + AdministrationLevelList, + getListCompetencies, + getListExperiences, +} from "@/service/management-user/management-user"; const taskSchema = z.object({ title: z.string().min(1, { message: "Judul diperlukan" }), @@ -129,7 +135,14 @@ export default function FormTaskTa() { const [detail, setDetail] = useState(); const [refresh] = useState(false); const [listDest, setListDest] = useState([]); - const [checkedLevels, setCheckedLevels] = useState(new Set()); + const [userExperiences, setUserExperiences] = useState(); + const [userLevels, setUserLevels] = useState(); + const [userCompetencies, setUserCompetencies] = useState([]); + const [selectedCompetencies, setSelectedCompetencies] = useState>( + new Set() + ); + const [listExpert, setListExpert] = useState([]); + const [checkedLevels, setCheckedLevels] = useState>(new Set()); const [expandedPolda, setExpandedPolda] = useState([{}]); const [isLoading, setIsLoading] = useState(false); const [audioFile, setAudioFile] = useState(null); @@ -171,37 +184,57 @@ export default function FormTaskTa() { mode: "all", }); - // const handleRadioChange = (event: React.ChangeEvent) => { - // const selectedValue = Number(event.target.value); - // setMainType(selectedValue); + useEffect(() => { + getDataAdditional(); + }, []); - // setPlatformTypeVisible(selectedValue === 2); + async function getDataAdditional() { + const resCompetencies = await getListCompetencies(); + console.log("competency", resCompetencies); + setUserCompetencies(resCompetencies?.data?.data); + } useEffect(() => { - async function fetchPoldaPolres() { + async function fetchListExpert() { setIsLoading(true); try { - const response = await getUserLevelForAssignments(); - setListDest(response?.data?.data.list); - console.log("polda", response?.data?.data?.list); - const initialExpandedState = response?.data?.data.list.reduce( - (acc: any, polda: any) => { - acc[polda.id] = false; - return acc; - }, - {} - ); - setExpandedPolda(initialExpandedState); - console.log("polres", initialExpandedState); + 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); } } - fetchPoldaPolres(); + 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); + } + + // Hapus duplikat expert berdasarkan ID + const uniqueExperts = Array.from( + new Map(allExperts.map((e) => [e.id, e])).values() + ); + + setListExpert(uniqueExperts); + }; + + if (selectedCompetencies.size > 0) { + fetchExpertsForCompetencies(); + } else { + setListExpert([]); + } + }, [selectedCompetencies]); + // }; const handleCheckboxChange = (levelId: number) => { setCheckedLevels((prev) => { @@ -215,10 +248,22 @@ export default function FormTaskTa() { }); }; - const handlePoldaPolresChange = () => { + const handleExpertChange = () => { return Array.from(checkedLevels).join(","); // Mengonversi Set ke string }; + 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; + }); + }; + const handleUnitChange = ( key: keyof typeof unitSelection, value: boolean @@ -305,61 +350,22 @@ export default function FormTaskTa() { }; const save = async (data: TaskSchema) => { - const fileTypeMapping = { - all: "1", - video: "2", - audio: "4", - image: "3", - text: "5", - }; - - const unitMapping = { - allUnit: "0", - mabes: "1", - polda: "2", - polres: "3", - satker: "4", - }; - const assignmentPurposeString = Object.keys(unitSelection) - .filter((key) => unitSelection[key as keyof typeof unitSelection]) - .map((key) => unitMapping[key as keyof typeof unitMapping]) - .join(","); - - const selectedOutputs = Object.keys(expertise) - .filter((key) => expertise[key as keyof typeof expertise]) - .map((key) => fileTypeMapping[key as keyof typeof fileTypeMapping]) - .join(","); - const requestData: { id?: number; 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[]; } = { ...data, - // assignmentType, - // assignmentCategory, - assignedToLevel: handlePoldaPolresChange(), - assignedToUsers: assignmentPurposeString, - assignedToRole: selectedTarget, + assignedToUsers: handleExpertChange(), 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, }; @@ -622,121 +628,7 @@ export default function FormTaskTa() {

{errors.title.message}

)} -
-
- - -
-
- {Object.keys(unitSelection).map((key) => ( -
- - handleUnitChange( - key as keyof typeof unitSelection, - value as boolean - ) - } - /> - -
- ))} -
-
- - - - - - - Daftar Wilayah Polda dan Polres - -
- {listDest.map((polda: any) => ( -
- - {expandedPolda[polda.id] && ( -
- - {polda?.subDestination?.map((polres: any) => ( - - ))} -
- )} -
- ))} -
-
-
-
-
+
- {Object.keys(expertise).map((key) => ( -
+ {userCompetencies?.map((item: any) => ( +
- handleExpertiseOutputChange( - key as keyof typeof expertise, - value as boolean - ) - } + id={`comp-${item.id}`} + checked={selectedCompetencies.has(item.id)} + onCheckedChange={() => handleCompetencyChange(item.id)} /> - +
))}
@@ -815,23 +700,34 @@ export default function FormTaskTa() {
- {Object.keys(expert).map((key) => ( -
- - handleExpertOutputChange( - key as keyof typeof expert, - value as boolean - ) - } - /> - -
- ))} + + + + + + + Daftar Tenaga Ahli + +
+ {listExpert?.map((expert: any) => ( +
+ +
+ ))} +
+
+
diff --git a/components/main/audio-detail.tsx b/components/main/audio-detail.tsx index 703d3e28..e89ff5f6 100644 --- a/components/main/audio-detail.tsx +++ b/components/main/audio-detail.tsx @@ -47,8 +47,8 @@ const formWaveSurferOptions = (ref: any) => ({ barWidth: 3, barRadius: 3, responsive: true, - height: 150, // If true, normalize by the maximum peak instead of 1.0. - normalize: true, // Use the PeakCache to improve rendering speed of large waveforms. + height: 150, + normalize: true, partialRender: true, }); diff --git a/lib/menus.ts b/lib/menus.ts index d23e581c..4aa522c5 100644 --- a/lib/menus.ts +++ b/lib/menus.ts @@ -2763,20 +2763,20 @@ export function getMenuList(pathname: string, t: any): Group[] { icon: "uiw:user-delete", children: [], }, - { - href: "/supervisor/communications/ptt", - label: t("ptt"), - active: pathname.includes("/communications/ptt"), - icon: "clarity:employee-group-line", - children: [], - }, - { - href: "/supervisor/communications/web-chat", - label: t("web-chat"), - active: pathname.includes("/communications/web-chat"), - icon: "clarity:employee-group-line", - children: [], - }, + // { + // href: "/supervisor/communications/ptt", + // label: t("ptt"), + // active: pathname.includes("/communications/ptt"), + // icon: "clarity:employee-group-line", + // children: [], + // }, + // { + // href: "/supervisor/communications/web-chat", + // label: t("web-chat"), + // active: pathname.includes("/communications/web-chat"), + // icon: "clarity:employee-group-line", + // children: [], + // }, ], }, ], diff --git a/service/task.ts b/service/task.ts index 8c744c06..2dd24898 100644 --- a/service/task.ts +++ b/service/task.ts @@ -53,6 +53,11 @@ export async function getMediaUpload(id: any, userLevelId: any) { return httpGetInterceptor(url); } +export async function getMediaUploadTa(id: any, userLevelId: any) { + const url = `/assignment-expert/media-uploads?id=${id}&userLevelId=${userLevelId}`; + return httpGetInterceptor(url); +} + export async function forwardTask(data: any) { const url = "assignment/forward"; return httpPostInterceptor(url, data); @@ -63,11 +68,21 @@ export async function deleteTask(id: any) { return httpDeleteInterceptor(url); } +export async function deleteTaskTa(id: any) { + const url = `assignment-expert?id=${id}`; + return httpDeleteInterceptor(url); +} + export async function getUserLevelForAssignments() { const url = "/users/user-levels/assignment"; return httpGetInterceptor(url); } +export async function getUserLevelForExpert(id: any) { + const url = `/users/assignment-expert?competencyIds=${id}`; + return httpGetInterceptor(url); +} + export async function getAssignmentResponseList(id: any) { const url = `assignment/response?assignmentId=${id}`; return httpGetInterceptor(url);