yazid138 3 éve
szülő
commit
e6294dd392

+ 15 - 0
actions/log.js

@@ -16,6 +16,21 @@ export const getLog = async (token, ptId) => {
 	}
 };
 
+export const getLogPT = async (token) => {
+	try {
+		let url = `/pemantauan/pt`;
+		const res = await axios.get(url, {
+			headers: {
+				Authorization: token,
+			},
+		});
+		return res.data;
+	} catch (error) {
+		console.log("error", error);
+		return false;
+	}
+};
+
 export const getLogPublic = async ({ ptId, laporanId }) => {
 	try {
 		let url = `/log/public?ptId=${ptId}&laporan=${laporanId}`;

+ 19 - 4
actions/pelanggaran.js

@@ -1,4 +1,5 @@
-import { API_URL } from "@/env";
+// import { API_URL } from "@/env";
+import axios from "../config/axios";
 
 export const getPelanggaranId = async (id) => {
 	try {
@@ -22,10 +23,24 @@ export const getPelanggaranId = async (id) => {
 	}
 };
 
-export const getPelanggaran = async () => {
+export const getPelanggaran = async (token) => {
 	try {
-		const res = await fetch(API_URL + "/pelanggaran");
-		return await res.json();
+		const res = await axios.get("/pelanggaran", {
+			headers: {
+				Authorization: token,
+			},
+		});
+		return await res.data;
+	} catch (error) {
+		console.log("error", error);
+		return false;
+	}
+};
+
+export const getPelanggaranPublic = async (token) => {
+	try {
+		const res = await axios.get("/public/pelanggaran");
+		return await res.data;
 	} catch (error) {
 		console.log("error", error);
 		return false;

+ 5 - 10
actions/pelaporan.js

@@ -5,12 +5,12 @@ export const getPelaporan = async (token, query = {}) => {
 	try {
 		let url = "/laporan";
 		if (query != null) {
-			const { number, ptId, penjadwalan, pemeriksaan, active, role, orgId } = query;
+			const { number, ptId, jadwal, pemeriksaan, active, role, orgId } = query;
 			url += "?";
 			const parseURL = [];
 			if (number) parseURL.push(`number=${number}`);
 			if (ptId) parseURL.push(`ptId=${ptId}`);
-			if (penjadwalan) parseURL.push(`penjadwalan=true`);
+			if (jadwal) parseURL.push(`jadwal=true`);
 			if (pemeriksaan) parseURL.push(`pemeriksaan=true`);
 			if (active) parseURL.push(`active=${active}`);
 			if (role) {
@@ -48,14 +48,9 @@ export const getPelaporanPublic = async ({ number, noHp }) => {
 	}
 };
 
-export const createPelaporan = async (data) => {
-	try {
-		const res = await axiosAPI.post("/pelaporan/create", data);
-		return res.data;
-	} catch (error) {
-		console.log("error", error);
-		return false;
-	}
+export const createPelaporan = async (token, data) => {
+	const res = await axiosAPI.post("/laporan/create", data, { headers: { Authorization: token } });
+	return res.data;
 };
 
 export const addStatus = async ({ number, ptId }, data) => {

+ 10 - 21
actions/penjadwalan.js

@@ -1,22 +1,11 @@
-import { post } from "../config/request";
-
-export const updateJadwal = async ({ number, ptId }, data) => {
-	try {
-		// const myHeaders = new Headers();
-		// myHeaders.append("Content-Type", "application/json");
-
-		// const raw = JSON.stringify(data);
-
-		// const requestOptions = {
-		// 	method: "POST",
-		// 	body: raw,
-		// 	headers: myHeaders,
-		// };
-
-		const res = await post(`/pelaporan/jadwal/add?number=${number}&ptId=${ptId}`, data);
-		return res.data;
-	} catch (error) {
-		console.log("error", error);
-		return false;
-	}
+import axios from "../config/axios";
+
+export const updateJadwal = async (token, id, data) => {
+	// try {
+	const res = await axios.put(`/laporan/jadwal/update/${id}`, data, { headers: { Authorization: token } });
+	return res.data;
+	// } catch (error) {
+	// console.log("error", error);
+	// return false;
+	// }
 };

+ 113 - 102
components/Extras/calendar.view.js

@@ -1,7 +1,7 @@
 import React, { Component } from "react";
 import ContentWrapper from "@/components/Layout/ContentWrapper";
-import { Card, CardBody, CardHeader, CardTitle, Button, Row, Col } from "reactstrap";
-import { getPelaporan, addStatus, removeLaporan, activeLaporan, changeRoleData } from "@/actions/pelaporan";
+import { Card, CardBody, CardHeader, CardTitle, Button, Row, Col, FormGroup, Input } from "reactstrap";
+import { getPelaporan, getOneLaporan, addStatus, removeLaporan, activeLaporan, changeRoleData } from "@/actions/pelaporan";
 import { updateJadwal } from "@/actions/penjadwalan";
 import DetailLaporan from "@/components/Main/DetailLaporan";
 import Link from "next/link";
@@ -18,6 +18,9 @@ import { connect } from "react-redux";
 import Loader from "@/components/Common/Loader";
 import Router from "next/router";
 import { ToastContainer, toast } from "react-toastify";
+import { Formik, Form, Field, ErrorMessage } from "formik";
+import * as Yup from "yup";
+import Datetime from "react-datetime";
 
 const status = [
 	{ value: "Ditindaklanjuti DIKTI", label: "Ditindaklanjuti DIKTI", className: "State-ACT" },
@@ -30,9 +33,13 @@ const statusLLDIKTI = [
 	{ value: "Delegasi ke DIKTI", label: "Delegasi ke DIKTI", className: "State-ACT" },
 	{ value: "Ditutup", label: "Ditutup", className: "State-ACT" },
 ];
-class Calendar extends Component {
-	calendarEvents = events;
 
+const jadwalSchema = Yup.object().shape({
+	judul: Yup.string().required("Required"),
+	dari_tanggal: Yup.date().required("Required"),
+	sampai_tanggal: Yup.date().required("Required"),
+});
+class Calendar extends Component {
 	calendarPlugins = [interactionPlugin, dayGridPlugin, timeGridPlugin, listPlugin, bootstrapPlugin];
 
 	calendarHeader = {
@@ -58,49 +65,34 @@ class Calendar extends Component {
 	static getInitialProps = ({ query }) => ({ query });
 
 	async componentDidMount() {
-		/* initialize the external events */
-		new Draggable(this.refs.externalEventsList, {
-			itemSelector: ".fce-event",
-			eventData: function (eventEl) {
-				return {
-					title: eventEl.innerText.trim(),
-				};
-			},
-		});
+		const { token, query } = this.props;
 
-		const { organisasi, peran } = this.props.user.peran[0];
+		const dataLaporan = await getPelaporan(token, { jadwal: true });
 
-		const queryDataLaporan = { role: peran.id === 2021 ? "lldikti" : "dikti", penjadwalan: true, active: true };
-		if (peran.id === 2021) queryDataLaporan.orgId = organisasi.id;
-		const dataLaporan = await getPelaporan(queryDataLaporan);
-
-		const laporan = await getPelaporan({ number: this.props.query.number, ptId: this.props.query.ptId });
+		const laporan = await getOneLaporan(token, query.id);
 
 		this.setState({ dataLaporan });
 		this.getDataEvent();
-		await this.defaultStatus(laporan.data[0]);
-		// const cek = this.state.dataLaporan.data.filter((e) => e._number === this.props.query.number && e.pt_id == this.props.query.ptId)[0];
+		this.defaultStatus();
 		this.setState({ laporan });
 		let color = "#" + Math.floor(Math.random() * 16777215).toString(16);
-		if (laporan.data[0].penjadwalan) {
-			color = laporan.data[0].penjadwalan.background_color;
+		if (laporan.data.jadwal) {
+			color = laporan.data.jadwal.warna;
 		}
-		this.setState({ externalEvents: [{ id: this.props.query.number, color, name: `Jadwal Pemeriksaan - No.Laporan : ${this.props.query.number} - ${laporan.data[0].pt.nama}`, allDay: true }] });
+		this.setState({ externalEvents: [{ id: query.id, color, name: `Jadwal Pemeriksaan - No.Laporan : ${laporan.data.no_laporan} - ${laporan.data.pt.nama}`, allDay: true }] });
 	}
 
-	getStatus = () => (this.props.user.peran[0].peran.id === 2021 ? statusLLDIKTI : status);
+	getStatus = () => (this.props.user.role.id === 2021 ? statusLLDIKTI : status);
 
 	getDataEvent = () => {
-		const dataEvent = this.state.dataLaporan.data
-			.filter((e) => e.penjadwalan)
-			.map((e) => ({
-				id: e._id,
-				title: e.penjadwalan.title,
-				start: new Date(e.penjadwalan.from_date),
-				end: new Date(e.penjadwalan.to_date),
-				backgroundColor: e.penjadwalan.background_color, //red
-				borderColor: e.penjadwalan.background_color,
-			}));
+		const dataEvent = this.state.dataLaporan.data.map((e) => ({
+			id: e._id,
+			title: e.jadwal.judul,
+			start: new Date(e.jadwal.dari_tanggal),
+			end: new Date(e.jadwal.sampai_tanggal),
+			backgroundColor: e.jadwal.warna,
+			borderColor: e.jadwal.warna,
+		}));
 		this.setState({ dataEvent });
 	};
 
@@ -113,10 +105,6 @@ class Calendar extends Component {
 		this.setState({ selectedEvent: data });
 	};
 
-	addEvent(event) {
-		this.calendarEvents.push(event);
-	}
-
 	handleEventReceive = (info) => {
 		var styles = getComputedStyle(info.draggedEl);
 		info.event.setProp("backgroundColor", styles.backgroundColor);
@@ -124,30 +112,37 @@ class Calendar extends Component {
 		this.handleEventCalendar(info);
 	};
 
-	handleEventCalendar = ({ event }) => {
-		const number = this.props.query.number;
-		const ptId = this.props.query.ptId;
-		const data = {
-			title: event.title,
-			from_date: event.start,
-			to_date: event.end || event.start,
-			background_color: event.backgroundColor,
+	handleEventCalendar = async (data) => {
+		const { token, query } = this.props;
+		const { id } = query;
+		const { laporan } = this.state;
+		const warna = "#" + Math.floor(Math.random() * 16777215).toString(16);
+		const d = {
+			judul: data.judul,
+			dari_tanggal: data.dari_tanggal,
+			sampai_tanggal: data.sampai_tanggal,
+			warna,
 		};
-		toast.promise(updateJadwal({ number, ptId }, data), {
+		if (laporan.data.jadwal) {
+			d.warna = laporan.data.jadwal.warna;
+		}
+		await toast.promise(updateJadwal(token, id, d), {
 			pending: "Loading",
 			success: "Success",
 			error: "Error",
 		});
+		// this.state.dataEvent.push({
+		// 	title: data.judul,
+		// 	start: new Date(data.dari_tanggal),
+		// 	end: new Date(data.sampai_tanggal),
+		// 	backgroundColor: warna,
+		// 	borderColor: warna,
+		// });
 	};
 
-	defaultStatus = async (data) => {
-		const { ptId, number } = this.props.query;
+	defaultStatus = async () => {
 		const status = this.getStatus();
-		if (!data.status) {
-			await addStatus({ number, ptId }, { status: status[0].value });
-			return this.setState({ selectedOption: status[0] });
-		}
-		return this.setState({ selectedOption: status.filter((e) => e.value === data.status)[0] });
+		return this.setState({ selectedOption: status[0] });
 	};
 
 	handleChangeSelect = (selectedOption) => this.setState({ selectedOption });
@@ -177,6 +172,7 @@ class Calendar extends Component {
 
 	render() {
 		const { externalEvents, laporan, selectedOption, selectedEvent } = this.state;
+		console.log(this.state.dataLaporan);
 		return (
 			<ContentWrapper>
 				<div className="content-heading">
@@ -193,7 +189,7 @@ class Calendar extends Component {
 							<Col>
 								<Card className="card-default">
 									<CardBody>
-										<DetailLaporan noStatus query={this.props.query} data={laporan.data[0]} handleChangeSelect={this.handleChangeSelect} />
+										<DetailLaporan noStatus data={laporan.data} />
 									</CardBody>
 								</Card>
 							</Col>
@@ -226,50 +222,70 @@ class Calendar extends Component {
 										<>
 											<Card className="card-default" title="">
 												<CardHeader>
-													<CardTitle tag="h4">Daftar Pemeriksaan</CardTitle>
+													<CardTitle tag="h4">Input Jadwal</CardTitle>
 												</CardHeader>
 												<CardBody>
-													<div className="external-events" ref="externalEventsList">
-														{externalEvents.map((ev) => (
-															<div
-																className="fce-event"
-																style={{
-																	backgroundColor: ev.color,
-																}}
-																key={ev.name + ev.color}
-																data-id={ev.id}
-															>
-																{ev.name}
-															</div>
-														))}
-													</div>
+													<Formik
+														enableReinitialize={true}
+														initialValues={{
+															judul: laporan.data?.jadwal.judul || "",
+															dari_tanggal: moment(laporan.data?.jadwal.dari_tanggal) || new Date(),
+															sampai_tanggal: moment(laporan.data?.jadwal.sampai_tanggal) || new Date(),
+														}}
+														validationSchema={jadwalSchema}
+														onSubmit={this.handleEventCalendar}
+													>
+														{({ isSubmitting }) => (
+															<Form>
+																<FormGroup>
+																	<label className="col-form-label">Judul</label>
+																	<Field name="judul">{({ field, form }) => <Input type="text" placeholder="judul" {...field} />}</Field>
+																	<ErrorMessage name="judul" component="div" className="form-text text-danger" />
+																</FormGroup>
+																<FormGroup>
+																	<label className="col-form-label">Dari Tanggal</label>
+																	<Field name="dari_tanggal">
+																		{({ field, form }) => (
+																			<Datetime
+																				timeFormat={false}
+																				inputProps={{ className: "form-control" }}
+																				value={field.value}
+																				onChange={(e) => {
+																					form.setFieldValue(field.name, e);
+																				}}
+																			/>
+																		)}
+																	</Field>
+																	<ErrorMessage name="dari_tanggal" component="div" className="form-text text-danger" />
+																</FormGroup>
+																<FormGroup>
+																	<label className="col-form-label">Sampai Tanggal</label>
+																	<Field name="sampai_tanggal">
+																		{({ field, form }) => (
+																			<Datetime
+																				timeFormat={false}
+																				inputProps={{ className: "form-control" }}
+																				value={field.value}
+																				onChange={(e) => {
+																					form.setFieldValue(field.name, e);
+																				}}
+																			/>
+																		)}
+																	</Field>
+																	<ErrorMessage name="sampai_tanggal" component="div" className="form-text text-danger" />
+																</FormGroup>
+																<FormGroup row>
+																	<div className="col-xl-12">
+																		<Button color="primary" block type="submit" disabled={isSubmitting}>
+																			Simpan Jadwal
+																		</Button>
+																	</div>
+																</FormGroup>
+															</Form>
+														)}
+													</Formik>
 												</CardBody>
 											</Card>
-											{laporan.data && laporan.data[0].penjadwalan && (
-												<Card className="card-default">
-													<CardHeader>
-														<CardTitle tag="h4">Jadwal Pemeriksaan</CardTitle>
-													</CardHeader>
-													<CardBody>
-														<table className="table">
-															<tbody>
-																<tr>
-																	<td>Judul</td>
-																	<td>{laporan.data[0].penjadwalan.title}</td>
-																</tr>
-																<tr>
-																	<td>Waktu</td>
-																	<td>{`${moment(laporan.data[0].penjadwalan.from_date).format("DD MMMM YYYY")} ${
-																		moment(laporan.data[0].penjadwalan.from_date).format("DD MMMM YYYY") === moment(laporan.data[0].penjadwalan.to_date).format("DD MMMM YYYY")
-																			? ""
-																			: `- ${moment(laporan.data[0].penjadwalan.to_date).add(-1, "d").format("DD MMMM YYYY")}`
-																	}`}</td>
-																</tr>
-															</tbody>
-														</table>
-													</CardBody>
-												</Card>
-											)}
 										</>
 									)}
 									<div className="mb-3">
@@ -298,13 +314,8 @@ class Calendar extends Component {
 										events={this.state.dataEvent}
 										themeSystem={"bootstrap"}
 										header={this.calendarHeader}
-										editable={true}
-										droppable={true}
 										deepChangeDetection={true}
 										eventClick={this.eventClick}
-										eventReceive={this.handleEventReceive}
-										eventDrop={this.handleEventCalendar}
-										eventResize={this.handleEventCalendar}
 									></FullCalendar>
 								</CardBody>
 							</Card>
@@ -316,5 +327,5 @@ class Calendar extends Component {
 	}
 }
 
-const mapStateToProps = (state) => ({ user: state.user });
+const mapStateToProps = (state) => ({ user: state.user, token: state.token });
 export default connect(mapStateToProps)(Calendar);

+ 7 - 11
components/Layout/MenuPT.js

@@ -5,7 +5,7 @@ const MenuPT = [
 	},
 	{
 		name: "Pemantauan",
-		path: "/app/pt/pemantauan",
+		path: "/pt/pemantauan",
 		icon: "icon-notebook",
 		translate: "sidebar.nav.PT_PEMANTAUAN",
 	},
@@ -16,15 +16,15 @@ const MenuPT = [
 		submenu: [
 			{
 				name: "a. Permohonan Keberatan",
-				path: "/app/pt/keberatan",
+				path: "/pt/keberatan",
 			},
 			{
 				name: "b. Jawaban keberatan",
-				path: "/app/pt/jawaban-keberatan",
+				path: "/pt/jawaban-keberatan",
 			},
 			{
 				name: "c. Jawaban banding",
-				path: "/app/pt/jawaban-banding",
+				path: "/pt/jawaban-banding",
 			},
 		],
 	},
@@ -35,21 +35,17 @@ const MenuPT = [
 		submenu: [
 			{
 				name: "a. Permohonan",
-				path: "/app/pt/pencabutan-sanksi",
+				path: "/pt/pencabutan-sanksi",
 			},
 			{
 				name: "b. Jawaban",
-				path: "/app/pt/jawaban-pencabutan-sanksi",
+				path: "/pt/jawaban-pencabutan-sanksi",
 			},
-			// {
-			// 	name: "c. Jawaban banding",
-			// 	path: "/app/pt/jawaban-banding",
-			// },
 		],
 	},
 	{
 		name: "Dokumen Perbaikan",
-		path: "/app/pt/dokumen-perbaikan",
+		path: "/pt/dokumen-perbaikan",
 		icon: "icon-notebook",
 		translate: "sidebar.nav.PT_DOKUMEN_PERBAIKAN",
 	},

+ 7 - 5
components/Main/DetailLaporan.js

@@ -9,7 +9,7 @@ function DetailLaporan({ data, noTitle = false, noStatus = false }) {
 
 	return (
 		<>
-			{(!data.user.isPrivate || user.role.id === 2020) && (
+			{(data.user.isPrivate || user.role.id === 2020) && (
 				<>
 					{noTitle ? "" : <p className="lead bb">Identitas Pelapor - {data.user.isPublic ? "Umum" : "Internal"}</p>}
 					<FormGroup row>
@@ -41,7 +41,9 @@ function DetailLaporan({ data, noTitle = false, noStatus = false }) {
 							</FormGroup>
 							<FormGroup row>
 								<Col md="4">Foto Kartu Identitas:</Col>
-								<Col md="8">{/* <img src={API_URL + data.user_id.files[0].path} height={200} alt="Foto Identitas" /> */}</Col>
+								<Col md="8">
+									<img src={data.user.dokumen.path} height={200} alt="Foto Identitas" />
+								</Col>
 							</FormGroup>
 						</>
 					)}
@@ -87,16 +89,16 @@ function DetailLaporan({ data, noTitle = false, noStatus = false }) {
 						<strong>{moment(data.createdAt).format("D MMMM YYYY")}</strong>
 					</Col>
 				</FormGroup>
-				{/* {!noStatus && data.status ? (
+				{!noStatus && data.aktif ? (
 					<FormGroup row>
 						<Col md="4">Status:</Col>
 						<Col md="8">
-							<div className="badge badge-info">{data.role_data}</div>
+							<div className="badge badge-info">{data.role_data === "dikti" ? "Ditindaklanjuti DIKTI" : "Ditindaklanjuti LLDIKTI"}</div>
 						</Col>
 					</FormGroup>
 				) : (
 					""
-				)} */}
+				)}
 				<FormGroup row>
 					<Col md="4">Dokumen Pendukung:</Col>
 					<Col md="8">

+ 5 - 4
components/Main/Timeline.js

@@ -12,9 +12,9 @@ function Timeline({ data, noFile = false }) {
 						.filter((e) => moment(e.createdAt).format("DD MMMM YYYY") === value)
 						.map((data, i) => (
 							<>
-								<li className={data.role === "PT" ? "timeline-inverted" : ""}>
-									<div className={`timeline-badge ${data.role === "PT" ? " danger" : "info"}`}>
-										<em className={`fas fa-${data.role === "PT" ? "graduation-cap" : "file"}`}></em>
+								<li className={data.user.role.id === 2022 ? "timeline-inverted" : ""}>
+									<div className={`timeline-badge ${data.user.role.id === 2022 ? " danger" : "info"}`}>
+										<em className={`fas fa-${data.user.role.id === 2022 ? "graduation-cap" : "file"}`}></em>
 									</div>
 
 									<div className="timeline-card">
@@ -32,7 +32,8 @@ function Timeline({ data, noFile = false }) {
 														<br />
 														{data.keterangan}
 														<br />
-														<p className="text-muted">{moment(data.createdAt).format("hh:mm")}</p>
+														{data.sanksi?.no_sanksi ? `No. Sanksi ${data.sanksi.no_sanksi}` : `No. Laporan ${data.laporan.no_laporan}`}
+														<p className="text-muted m-0">{moment(data.createdAt).format("hh:mm")}</p>
 													</p>
 												</div>
 												{!noFile && data.dokumen.length ? (

+ 125 - 81
components/Pelaporan/InputData.js

@@ -1,13 +1,15 @@
 import React, { Component } from "react";
 import Router from "next/router";
-import { getPelanggaran } from "@/actions/pelanggaran";
+import { getPelanggaranPublic } from "@/actions/pelanggaran";
 import { createPelaporan } from "@/actions/pelaporan";
 import Select from "react-select";
 import { Row, Col, FormGroup, Input } from "reactstrap";
 import { connect } from "react-redux";
 import { ToastContainer, toast } from "react-toastify";
-
+import moment from "moment";
 import "react-toastify/dist/ReactToastify.css";
+import { Formik, Form, Field, ErrorMessage } from "formik";
+import * as Yup from "yup";
 
 let Dropzone = null;
 class DropzoneWrapper extends Component {
@@ -24,6 +26,12 @@ class DropzoneWrapper extends Component {
 }
 
 const selectInstanceId = 1;
+const laporanSchema = Yup.object().shape({
+	no_laporan: Yup.string().required("Harap Diisi"),
+	keterangan: Yup.string().min(3).max(200).required("Harap Diisi"),
+	pelanggaran: Yup.array().min(1).required("Harap Diisi"),
+	dokumen: Yup.array().notRequired(),
+});
 export class InputData extends Component {
 	constructor(props) {
 		super(props);
@@ -32,7 +40,7 @@ export class InputData extends Component {
 			splitButtonOpen: false,
 			selectedOptionMulti: [],
 			stat: "Waiting to add files..",
-			pelaporanNumber: Math.floor(Date.now() * Math.random()),
+			pelaporanNumber: moment(new Date()).format("YYMMDD") + "" + Math.floor(Math.random() * 10000),
 			keteranganLaporan: "",
 			files: [],
 			pelanggaran: [],
@@ -40,7 +48,7 @@ export class InputData extends Component {
 	}
 
 	componentDidMount = async () => {
-		const pelanggaran = await getPelanggaran();
+		const pelanggaran = await getPelanggaranPublic();
 		this.setState({ pelanggaran });
 	};
 
@@ -98,38 +106,29 @@ export class InputData extends Component {
 		});
 	};
 
-	onSubmit = async (e) => {
-		const { user } = this.props;
-		e.preventDefault();
+	onSubmit = async (data) => {
+		const { token, query } = this.props;
 		const formdata = new FormData();
-		formdata.append("number", this.state.pelaporanNumber);
-		formdata.append("user_id", user._id);
-		formdata.append("pt_id", this.props.query.ptId);
-		formdata.append("description", this.state.keteranganLaporan);
-		formdata.append("is_public", false);
-		if (user.peran[0].peran.id === 2021) formdata.append("role_data", "lldikti");
-		formdata.append("pelanggaran", this.state.selectedOptionMulti.map((e) => e.value).join());
-		if (this.state.files.length > 0) {
-			this.state.files.forEach((e) => {
-				formdata.append("files", e);
+		formdata.append("no_laporan", data.no_laporan);
+		formdata.append("pt_id", query.ptId);
+		formdata.append("keterangan", data.keterangan);
+		formdata.append("pelanggaran_id", data.pelanggaran.join());
+		if (data.dokumen.length > 0) {
+			data.dokumen.forEach((e) => {
+				formdata.append("dokumen", e);
 			});
 		}
 
-		const create = createPelaporan(formdata);
+		const create = createPelaporan(token, formdata);
 		await toast.promise(create, {
 			pending: "Loading...",
 			success: "Berhasil buat laporan",
 			error: "Gagal buat laporan",
 		});
-		// console.log(create);
-		// console.log(create);
-		// await this.props.dispatch(createPelaporan(formdata));
-		// this.props.dispatch(listPelaporan());
-		if (create) {
-			Router.push({
-				pathname: "/app/pelaporan",
-			});
-		}
+
+		Router.push({
+			pathname: "/app/pelaporan",
+		});
 	};
 
 	render() {
@@ -141,62 +140,107 @@ export class InputData extends Component {
 			</Col>
 		));
 		return (
-			<form className="form-horizontal" method="get" action="/" onSubmit={this.onSubmit}>
-				{/* <ToastContainer /> */}
-				<FormGroup row>
-					<label className="col-md-2 col-form-label">Nomor Pelaporan</label>
-					<div className="col-md-10">
-						<Input type="text" disabled value={this.state.pelaporanNumber} />
-						<span className="form-text">Nomor pelaporan akan digenerate otomatis dari sistem</span>
-					</div>
-				</FormGroup>
-				<FormGroup row>
-					<label className="col-md-2 col-form-label">Jenis Pelanggaran</label>
-					<div className="col-md-10">
-						<Select instanceId={selectInstanceId + 1} isMulti value={selectedOptionMulti} onChange={this.handleChangeSelectMulti} options={pelanggaran.data ? this.optionsJenisPelanggaran(pelanggaran) : []} required />
-						<span className="form-text">Pilih Jenis Pelanggaran</span>
-					</div>
-				</FormGroup>
-				<FormGroup row>
-					<label className="col-md-2 col-form-label">Keterangan Laporan</label>
-					<div className="col-md-10">
-						<Input type="textarea" value={this.state.keteranganLaporan} onChange={this.setKeteranganPelaporan} required />
-						<span className="form-text">Deskripsi pelaporan minimum karakter 50 maksimum 200 karakter</span>
-					</div>
-				</FormGroup>
-				<FormGroup row>
-					<label className="col-md-2 col-form-label">Upload File Pendukung</label>
-					<div className="col-md-10">
-						<DropzoneWrapper className="" onDrop={this.onDrop}>
-							{({ getRootProps, getInputProps, isDragActive }) => {
-								return (
-									<div {...getRootProps()} className={"dropzone card p-3 " + (isDragActive ? "dropzone-drag-active" : "")}>
-										<input {...getInputProps()} />
-										<div className="dropzone-previews flex">{this.state.files.length > 0 ? <Row>{thumbs}</Row> : <div className="text-center dz-default dz-message">Drop files here to upload</div>}</div>
-										<div className="d-flex align-items-center">
-											<small className="ml-auto">
-												<button type="button" className="btn btn-link" onClick={this.clearFiles}>
-													Clear files
-												</button>
-											</small>
-										</div>
-									</div>
-								);
-							}}
-						</DropzoneWrapper>
-					</div>
-				</FormGroup>
-				<FormGroup row>
-					<div className="col-xl-10">
-						<button className="btn btn-sm btn-primary" type="submit">
-							Submit Laporan
-						</button>
-					</div>
-				</FormGroup>
-			</form>
+			<Formik
+				initialValues={{
+					no_laporan: this.state.pelaporanNumber,
+					keterangan: "",
+					pelanggaran: [],
+					dokumen: [],
+				}}
+				validationSchema={laporanSchema}
+				onSubmit={this.onSubmit}
+			>
+				{({ isSubmitting }) => (
+					<Form>
+						<FormGroup row>
+							<label className="col-md-2 col-form-label">Nomor Pelaporan</label>
+							<div className="col-md-10">
+								<Input type="text" name="no_laporan" disabled value={this.state.pelaporanNumber} />
+								<span className="form-text">Nomor pelaporan akan digenerate otomatis dari sistem</span>
+							</div>
+						</FormGroup>
+
+						<FormGroup row>
+							<label className="col-md-2 col-form-label">Jenis Pelanggaran</label>
+							<div className="col-md-10">
+								<Field name="pelanggaran">
+									{({ field, form, meta }) => (
+										<Select
+											instanceId={selectInstanceId + 1}
+											name="pelanggaran"
+											isMulti
+											value={selectedOptionMulti}
+											onChange={(e) => {
+												this.handleChangeSelectMulti(e);
+												form.setFieldValue(
+													field.name,
+													e.map((e) => e.value)
+												);
+											}}
+											options={pelanggaran.data ? this.optionsJenisPelanggaran(pelanggaran) : []}
+											required
+										/>
+									)}
+								</Field>
+								<ErrorMessage name="pelanggaran" component="div" className="form-text text-danger" />
+							</div>
+						</FormGroup>
+
+						<FormGroup row>
+							<label className="col-md-2 col-form-label">Keterangan Laporan</label>
+							<div className="col-md-10">
+								<Field name="keterangan">{({ field }) => <Input type="textarea" placeholder="Keterangan Laporan" {...field} />}</Field>
+								<ErrorMessage name="keterangan" component="div" className="form-text text-danger" />
+							</div>
+						</FormGroup>
+
+						<FormGroup row>
+							<label className="col-md-2 col-form-label">Upload File Pendukung</label>
+							<div className="col-md-10">
+								<Field name="dokumen">
+									{({ field, form, meta }) => (
+										<DropzoneWrapper
+											className=""
+											onDrop={(e) => {
+												this.onDrop(e);
+												form.setFieldValue(field.name, e);
+											}}
+										>
+											{({ getRootProps, getInputProps, isDragActive }) => {
+												return (
+													<div {...getRootProps()} className={"dropzone card p-3 " + (isDragActive ? "dropzone-drag-active" : "")}>
+														<input name="dokumen" {...getInputProps()} />
+														<div className="dropzone-previews flex">{this.state.files.length > 0 ? <Row>{thumbs}</Row> : <div className="text-center dz-default dz-message">Drop files here to upload</div>}</div>
+														<div className="d-flex align-items-center">
+															<small className="ml-auto">
+																<button type="button" className="btn btn-link" onClick={this.clearFiles}>
+																	Clear files
+																</button>
+															</small>
+														</div>
+													</div>
+												);
+											}}
+										</DropzoneWrapper>
+									)}
+								</Field>
+								<ErrorMessage name="dokumen" component="div" className="form-text text-danger" />
+							</div>
+						</FormGroup>
+
+						<FormGroup row>
+							<div className="col-xl-10">
+								<button className="btn btn-sm btn-primary" type="submit" disabled={isSubmitting}>
+									Submit Laporan
+								</button>
+							</div>
+						</FormGroup>
+					</Form>
+				)}
+			</Formik>
 		);
 	}
 }
 
-const mapStateToProps = (state) => ({ user: state.user });
+const mapStateToProps = (state) => ({ user: state.user, token: state.token });
 export default connect(mapStateToProps)(InputData);

+ 92 - 0
package-lock.json

@@ -1857,6 +1857,11 @@
 				}
 			}
 		},
+		"@swc/helpers": {
+			"version": "0.2.14",
+			"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.2.14.tgz",
+			"integrity": "sha512-wpCQMhf5p5GhNg2MmGKXzUNwxe7zRiCsmqYsamez2beP7mKPCSiu+BjZcdN95yYSzO857kr0VfQewmGpS77nqA=="
+		},
 		"@types/cookie": {
 			"version": "0.4.1",
 			"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz",
@@ -1881,6 +1886,11 @@
 			"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz",
 			"integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA=="
 		},
+		"@types/lodash": {
+			"version": "4.14.180",
+			"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.180.tgz",
+			"integrity": "sha512-XOKXa1KIxtNXgASAnwj7cnttJxS4fksBRywK/9LzRV5YxrF80BXZIGeQSuoESQ/VkUj30Ae0+YcuHc15wJCB2g=="
+		},
 		"@types/parse-json": {
 			"version": "4.0.0",
 			"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
@@ -4245,6 +4255,11 @@
 			"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
 			"integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ="
 		},
+		"deepmerge": {
+			"version": "2.2.1",
+			"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz",
+			"integrity": "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA=="
+		},
 		"define-properties": {
 			"version": "1.1.3",
 			"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
@@ -4445,6 +4460,15 @@
 			"resolved": "https://registry.npmjs.org/draftjs-utils/-/draftjs-utils-0.10.2.tgz",
 			"integrity": "sha512-EstHqr3R3JVcilJrBaO/A+01GvwwKmC7e4TCjC7S94ZeMh4IVmf60OuQXtHHpwItK8C2JCi3iljgN5KHkJboUg=="
 		},
+		"dropzone": {
+			"version": "6.0.0-beta.2",
+			"resolved": "https://registry.npmjs.org/dropzone/-/dropzone-6.0.0-beta.2.tgz",
+			"integrity": "sha512-k44yLuFFhRk53M8zP71FaaNzJYIzr99SKmpbO/oZKNslDjNXQsBTdfLs+iONd0U0L94zzlFzRnFdqbLcs7h9fQ==",
+			"requires": {
+				"@swc/helpers": "^0.2.13",
+				"just-extend": "^5.0.0"
+			}
+		},
 		"duplexer2": {
 			"version": "0.1.4",
 			"resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz",
@@ -5268,6 +5292,20 @@
 				"mime-types": "^2.1.12"
 			}
 		},
+		"formik": {
+			"version": "2.2.9",
+			"resolved": "https://registry.npmjs.org/formik/-/formik-2.2.9.tgz",
+			"integrity": "sha512-LQLcISMmf1r5at4/gyJigGn0gOwFbeEAlji+N9InZF6LIMXnFNkO42sCI8Jt84YZggpD4cPWObAZaxpEFtSzNA==",
+			"requires": {
+				"deepmerge": "^2.1.1",
+				"hoist-non-react-statics": "^3.3.0",
+				"lodash": "^4.17.21",
+				"lodash-es": "^4.17.21",
+				"react-fast-compare": "^2.0.1",
+				"tiny-warning": "^1.0.2",
+				"tslib": "^1.10.0"
+			}
+		},
 		"forwarded": {
 			"version": "0.1.2",
 			"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
@@ -6415,6 +6453,11 @@
 				"set-immediate-shim": "~1.0.1"
 			}
 		},
+		"just-extend": {
+			"version": "5.1.1",
+			"resolved": "https://registry.npmjs.org/just-extend/-/just-extend-5.1.1.tgz",
+			"integrity": "sha512-b+z6yF1d4EOyDgylzQo5IminlUmzSeqR1hs/bzjBNjuGras4FXq/6TrzjxfN0j+TmI0ltJzTNlqXUMCniciwKQ=="
+		},
 		"jwt-decode": {
 			"version": "3.1.2",
 			"resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz",
@@ -7054,6 +7097,11 @@
 			"resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz",
 			"integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ=="
 		},
+		"nanoclone": {
+			"version": "0.2.1",
+			"resolved": "https://registry.npmjs.org/nanoclone/-/nanoclone-0.2.1.tgz",
+			"integrity": "sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA=="
+		},
 		"nanomatch": {
 			"version": "1.2.13",
 			"resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
@@ -8243,6 +8291,11 @@
 				"warning": "^4.0.0"
 			}
 		},
+		"property-expr": {
+			"version": "2.0.5",
+			"resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.5.tgz",
+			"integrity": "sha512-IJUkICM5dP5znhCckHSv30Q4b5/JA5enCtkRHYaOVOAocnH/1BQEYTC5NMfT3AVl/iXKdr3aqQbQn9DxyWknwA=="
+		},
 		"proxy-addr": {
 			"version": "2.0.6",
 			"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
@@ -8685,6 +8738,11 @@
 				"prop-types": "^15.7.2"
 			}
 		},
+		"react-fast-compare": {
+			"version": "2.0.4",
+			"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz",
+			"integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw=="
+		},
 		"react-flot": {
 			"version": "1.3.0",
 			"resolved": "https://registry.npmjs.org/react-flot/-/react-flot-1.3.0.tgz",
@@ -10502,6 +10560,11 @@
 			"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
 			"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
 		},
+		"toposort": {
+			"version": "2.0.2",
+			"resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz",
+			"integrity": "sha1-riF2gXXRVZ1IvvNUILL0li8JwzA="
+		},
 		"tough-cookie": {
 			"version": "2.5.0",
 			"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
@@ -11434,6 +11497,35 @@
 					"dev": true
 				}
 			}
+		},
+		"yup": {
+			"version": "0.32.11",
+			"resolved": "https://registry.npmjs.org/yup/-/yup-0.32.11.tgz",
+			"integrity": "sha512-Z2Fe1bn+eLstG8DRR6FTavGD+MeAwyfmouhHsIUgaADz8jvFKbO/fXc2trJKZg+5EBjh4gGm3iU/t3onKlXHIg==",
+			"requires": {
+				"@babel/runtime": "^7.15.4",
+				"@types/lodash": "^4.14.175",
+				"lodash": "^4.17.21",
+				"lodash-es": "^4.17.21",
+				"nanoclone": "^0.2.1",
+				"property-expr": "^2.0.4",
+				"toposort": "^2.0.2"
+			},
+			"dependencies": {
+				"@babel/runtime": {
+					"version": "7.17.8",
+					"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.8.tgz",
+					"integrity": "sha512-dQpEpK0O9o6lj6oPu0gRDbbnk+4LeHlNcBpspf6Olzt3GIX4P1lWF1gS+pHLDFlaJvbR6q7jCfQ08zA4QJBnmA==",
+					"requires": {
+						"regenerator-runtime": "^0.13.4"
+					}
+				},
+				"regenerator-runtime": {
+					"version": "0.13.9",
+					"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz",
+					"integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA=="
+				}
+			}
 		}
 	}
 }

+ 4 - 1
package.json

@@ -45,9 +45,11 @@
 		"deep-equal": "1.1.1",
 		"dotenv": "^16.0.0",
 		"draft-js": "0.11.7",
+		"dropzone": "^6.0.0-beta.2",
 		"easy-pie-chart": "2.1.7",
 		"express": "4.17.1",
 		"flot": "themicon/flot",
+		"formik": "^2.2.9",
 		"get-size": "2.0.3",
 		"history": "4.10.1",
 		"html5sortable": "0.11.1",
@@ -103,7 +105,8 @@
 		"swr": "^1.0.1",
 		"validator": "10.11.0",
 		"weather-icons": "erikflowers/weather-icons",
-		"whirl": "themicon/whirl"
+		"whirl": "themicon/whirl",
+		"yup": "^0.32.11"
 	},
 	"devDependencies": {
 		"@types/reactstrap": "^8.7.2",

+ 5 - 2
pages/app/pelaporan/search.js

@@ -19,8 +19,11 @@ class Search extends Component {
 	};
 
 	componentDidMount = async () => {
-		const dataPembina = await getPembina(this.props.token);
-		this.setState({ pembina: dataPembina.data });
+		const { user, token } = this.props;
+		if (user.role.id === 2020) {
+			const dataPembina = await getPembina(token);
+			this.setState({ pembina: dataPembina.data });
+		}
 	};
 
 	optionsPembina = (pembina) => {

+ 5 - 2
pages/app/pemantauan/index.js

@@ -18,8 +18,11 @@ class Search extends Component {
 	};
 
 	componentDidMount = async () => {
-		const dataPembina = await getPembina(this.props.token);
-		if (dataPembina) this.setState({ pembina: dataPembina.data });
+		const { user, token } = this.props;
+		if (user.role.id === 2020) {
+			const dataPembina = await getPembina(token);
+			this.setState({ pembina: dataPembina.data });
+		}
 	};
 
 	optionsPembina = (pembina) => {

+ 3 - 5
pages/app/penjadwalan/index.js

@@ -16,10 +16,8 @@ class Penjadwalan extends Component {
 	}
 
 	componentDidMount = async () => {
-		const { organisasi, peran } = this.props.user.peran[0];
-		const query = { role: peran.id === 2021 ? "lldikti" : "dikti" };
-		if (peran.id === 2021) query.orgId = organisasi.id;
-		const pelaporan = await getPelaporan(query);
+		const { token } = this.props;
+		const pelaporan = await getPelaporan(token);
 		this.setState({ pelaporan });
 	};
 
@@ -39,5 +37,5 @@ class Penjadwalan extends Component {
 	}
 }
 
-const mapStateToProps = (state) => ({ user: state.user });
+const mapStateToProps = (state) => ({ user: state.user, token: state.token });
 export default connect(mapStateToProps)(Penjadwalan);

+ 10 - 6
pages/pt/pemantauan.js

@@ -4,9 +4,8 @@ import Header from "@/components/Main/Header";
 import DetailPT from "@/components/Main/DetailPT";
 import { getLogPT } from "@/actions/log";
 import { getPT } from "@/actions/PT";
-import { getPelaporan } from "@/actions/pelaporan";
 import { Row, Col } from "reactstrap";
-import Timeline from "@/components/PT/Timeline";
+import Timeline from "@/components/Main/Timeline";
 import { connect } from "react-redux";
 import Loader from "@/components/Common/Loader";
 
@@ -21,10 +20,9 @@ class Pemantauan extends Component {
 
 	componentDidMount = async () => {
 		const { token } = this.props;
-		// const log = await getLogPT(token);
+		const log = await getLogPT(token);
 		const pt = await getPT(token);
-		// const pelaporan = await getPelaporan({ ptId: user.peran[0].organisasi.id });
-		this.setState({ pt });
+		this.setState({ log, pt });
 	};
 
 	render() {
@@ -34,7 +32,13 @@ class Pemantauan extends Component {
 				{pt?.data ? <Header data={pt.data} /> : <Loader />}
 				<div className="p-3">
 					<Row>
-						{/* {log && pelaporan.data ? <Col xl="9"><Timeline data={log} /></Col> : <Loader />} */}
+						{log.data ? (
+							<Col xl="9">
+								<Timeline data={log.data} />
+							</Col>
+						) : (
+							<Loader />
+						)}
 						<Col xl="3">{pt.data && <DetailPT data={pt.data} />}</Col>
 					</Row>
 				</div>