yazid138 3 years ago
parent
commit
547e3ab421
97 changed files with 3125 additions and 1419 deletions
  1. 50 11
      actions/PT.js
  2. 10 14
      actions/auth.js
  3. 5 11
      actions/banding.js
  4. 5 6
      actions/cabutSanksi.js
  5. 3 4
      actions/docPerbaikan.js
  6. 5 6
      actions/keberatan.js
  7. 25 7
      actions/log.js
  8. 29 4
      actions/pelanggaran.js
  9. 53 46
      actions/pelaporan.js
  10. 4 9
      actions/pemeriksaan.js
  11. 10 21
      actions/penjadwalan.js
  12. 26 21
      actions/sanksi.js
  13. 3 4
      components/Banding/Riwayat.js
  14. 6 6
      components/Banding/TableSanksi.js
  15. 4 6
      components/DocPerbaikan/Riwayat.js
  16. 143 129
      components/Extras/calendar.view.js
  17. 4 4
      components/Keberatan/Riwayat.js
  18. 6 6
      components/Keberatan/TableSanksi.js
  19. 1 1
      components/Layout/Head.js
  20. 5 1
      components/Layout/Header.js
  21. 11 2
      components/Layout/Menu.js
  22. 11 2
      components/Layout/MenuLLDIKTI.js
  23. 7 11
      components/Layout/MenuPT.js
  24. 1 1
      components/Layout/Sidebar.js
  25. 2 3
      components/Layout/SidebarUserBlock.js
  26. 24 20
      components/Main/DetailLaporan.js
  27. 8 8
      components/Main/DetailSanksi.js
  28. 23 28
      components/Main/Login.js
  29. 3 4
      components/Main/PermohonanPT.js
  30. 5 5
      components/Main/TableLaporan.js
  31. 6 7
      components/Main/TableSanksi.js
  32. 27 12
      components/Main/Timeline.js
  33. 5 6
      components/PT/DocPerbaikan/Riwayat.js
  34. 31 28
      components/PT/JawabanBanding/DetailJawaban.js
  35. 6 7
      components/PT/JawabanBanding/TableSanksiJawaban.js
  36. 5 7
      components/PT/JawabanKeberatan/DetailJawaban.js
  37. 20 16
      components/PT/JawabanKeberatan/ModalPermohonan.js
  38. 5 7
      components/PT/JawabanKeberatan/Riwayat.js
  39. 6 6
      components/PT/JawabanKeberatan/TableSanksiJawaban.js
  40. 6 6
      components/PT/JawabanPencabutanSanksi/TableSanksiJawaban.js
  41. 22 25
      components/PT/Keberatan/ModalPermohonan.js
  42. 5 7
      components/PT/Keberatan/Riwayat.js
  43. 17 33
      components/PT/Riwayat.js
  44. 5 5
      components/PT/TableSanksi.js
  45. 3 3
      components/PT/TableSanksiJawaban.js
  46. 125 81
      components/Pelaporan/InputData.js
  47. 98 65
      components/Pemeriksaan/InputEvaluasi.js
  48. 67 0
      components/Pemeriksaan/TableLaporan.js
  49. 8 10
      components/Pemeriksaan/TableRiwayat.js
  50. 7 8
      components/PencabutanSanksi/Riwayat.js
  51. 6 6
      components/PencabutanSanksi/TableSanksi.js
  52. 67 0
      components/Penjadwalan/TableLaporan.js
  53. 11 15
      components/Public/DetailLaporan.js
  54. 5 5
      components/Sanksi/TableLaporan.js
  55. 4 5
      components/Sanksi/TablePenetapanSanksi.js
  56. 1 35
      config/axios.js
  57. 109 19
      package-lock.json
  58. 5 1
      package.json
  59. 119 78
      pages/app/banding/detail.js
  60. 6 3
      pages/app/banding/index.js
  61. 139 86
      pages/app/keberatan/detail.js
  62. 4 6
      pages/app/keberatan/index.js
  63. 8 5
      pages/app/pelaporan/detail.js
  64. 2 5
      pages/app/pelaporan/index.js
  65. 7 6
      pages/app/pelaporan/new.js
  66. 12 9
      pages/app/pelaporan/search.js
  67. 14 14
      pages/app/pemantauan-perbaikan/detail.js
  68. 4 6
      pages/app/pemantauan-perbaikan/index.js
  69. 13 10
      pages/app/pemantauan/index.js
  70. 9 7
      pages/app/pemantauan/timeline.js
  71. 5 7
      pages/app/pemeriksaan/index.js
  72. 13 11
      pages/app/pemeriksaan/new.js
  73. 134 91
      pages/app/pencabutan-sanksi/detail.js
  74. 4 6
      pages/app/pencabutan-sanksi/index.js
  75. 4 6
      pages/app/penjadwalan/index.js
  76. 11 9
      pages/app/sanksi/detail.js
  77. 4 6
      pages/app/sanksi/index.js
  78. 31 28
      pages/app/sanksi/proses.js
  79. 195 159
      pages/laporan/new/index.js
  80. 50 34
      pages/pemantauan.js
  81. 265 0
      pages/pt/dokumen-perbaikan/detail.js
  82. 37 0
      pages/pt/dokumen-perbaikan/index.js
  83. 72 0
      pages/pt/jawaban-banding/detail.js
  84. 37 0
      pages/pt/jawaban-banding/index.js
  85. 102 0
      pages/pt/jawaban-keberatan/detail.js
  86. 37 0
      pages/pt/jawaban-keberatan/index.js
  87. 121 0
      pages/pt/jawaban-pencabutan-sanksi/detail.js
  88. 37 0
      pages/pt/jawaban-pencabutan-sanksi/index.js
  89. 99 0
      pages/pt/keberatan/detail.js
  90. 36 0
      pages/pt/keberatan/index.js
  91. 51 0
      pages/pt/pemantauan.js
  92. 211 0
      pages/pt/pencabutan-sanksi/detail.js
  93. 37 0
      pages/pt/pencabutan-sanksi/index.js
  94. 11 0
      store/actions/token.js
  95. 0 1
      store/actions/user.js
  96. 2 0
      store/reducers/reducers.js
  97. 8 0
      store/reducers/token.reducer.js

+ 50 - 11
actions/PT.js

@@ -1,20 +1,22 @@
-import { get } from "@/config/request";
+import axios from "@/config/axios";
 
-export const getPT = async (params) => {
+export const getPT = async (token, params) => {
 	try {
-		let url = "/perguruan-tinggi";
+		let url = "/pt";
 		if (params) {
 			url += "?";
-			if (params.id) {
-				url += `id=${params.id}`;
-			} else if (params.search || params.pembina) {
+			if (params.search || params.pembina) {
 				const parseURL = [];
 				if (params.search) parseURL.push(`search=${params.search}`);
 				if (params.pembina) parseURL.push(`pembina=${params.pembina}`);
-				url += parseURL.join('&')
+				url += parseURL.join("&");
 			}
 		}
-		const response = await get(url);
+		const response = await axios.get(url, {
+			headers: {
+				Authorization: token,
+			},
+		});
 		return response.data;
 	} catch (error) {
 		console.log("error", error);
@@ -22,16 +24,53 @@ export const getPT = async (params) => {
 	}
 };
 
-export const getPembina = async (params) => {
+export const getOnePT = async (token, pt_id) => {
 	try {
-		let url = "/perguruan-tinggi/lembaga";
+		const response = await axios.get(`/pt/${pt_id}`, {
+			headers: {
+				Authorization: token,
+			},
+		});
+		return response.data;
+	} catch (error) {
+		console.log("error", error);
+		return false;
+	}
+};
+
+export const ptPublic = async (params) => {
+	try {
+		let url = "/public/pt";
+		if (params) {
+			url += "?";
+			if (params.search) {
+				const parseURL = [];
+				if (params.search) parseURL.push(`search=${params.search}`);
+				url += parseURL.join("&");
+			}
+		}
+		const response = await axios.get(url);
+		return response.data;
+	} catch (error) {
+		console.log("error", error);
+		return false;
+	}
+};
+
+export const getPembina = async (token, params) => {
+	try {
+		let url = "/lembaga";
 		if (params) {
 			url += "?";
 			if (params.search) {
 				url += `search=${params.search}`;
 			}
 		}
-		const response = await get(url);
+		const response = await axios.get(url, {
+			headers: {
+				Authorization: token,
+			},
+		});
 		return response.data;
 	} catch (error) {
 		console.log("error", error);

+ 10 - 14
actions/auth.js

@@ -2,22 +2,18 @@ import { get } from "../config/request";
 import axiosAPI from "../config/axios";
 
 export const login = async (username, password) => {
-	try {
-		const data = {
-			username,
-			password,
-		};
+	const data = {
+		username,
+		password,
+	};
 
-		const response = await axiosAPI.post("/login", data, {
-			headers: {
-				"Content-Type": "application/json",
-			},
-		});
+	const response = await axiosAPI.post("/auth/login", data, {
+		headers: {
+			"Content-Type": "application/json",
+		},
+	});
 
-		return response.data;
-	} catch (error) {
-		if (error.response) return error.response.data;
-	}
+	return response.data;
 };
 
 export const refreshToken = async () => {

+ 5 - 11
actions/banding.js

@@ -1,27 +1,21 @@
-import { post } from "../config/request";
+import axios from "../config/axios";
 
-export const addBanding = async ({ noSanksi, ptId }, data) => {
+export const addBanding = async (token, id, data) => {
 	try {
-		const res = await post(`/banding/add?noSanksi=${noSanksi}&ptId=${ptId}`, data);
-		console.log(res);
-		// addLog({ status: "SUCCESS", action: "CREATE", from: { id: result.added._id, data: "banding" }, description: "membuat permohonan banding" });
+		const res = await axios.post(`/sanksi/banding/create/${id}`, data, { headers: { Authorization: token } });
 		return res.data;
 	} catch (error) {
 		console.log("error", error);
-		// addLog({ status: "FAIL", action: "ADD", from: { data: "banding" }, description: error.message || "membuat permohonan banding" });
 		return false;
 	}
 };
 
-export const addJawabanBanding = async ({ noSanksi, ptId }, data) => {
+export const addJawabanBanding = async (token, id, data) => {
 	try {
-		const res = await post(`/banding/jawaban/add?noSanksi=${noSanksi}&ptId=${ptId}`, data);
-		console.log(res);
-		// addLog({ status: "SUCCESS", action: "CREATE", from: { id: result.added._id, data: "banding" }, description: "membuat permohonan banding" });
+		const res = await axios.post(`/sanksi/banding/jawaban/create/${id}`, data, { headers: { Authorization: token } });
 		return res.data;
 	} catch (error) {
 		console.log("error", error);
-		// addLog({ status: "FAIL", action: "ADD", from: { data: "banding" }, description: error.message || "membuat permohonan banding" });
 		return false;
 	}
 };

+ 5 - 6
actions/cabutSanksi.js

@@ -1,9 +1,9 @@
 import { post } from "../config/request";
+import axios from "../config/axios";
 
-export const addCabutSanksi = async ({ noSanksi, ptId }, data) => {
+export const addCabutSanksi = async (token, id, data) => {
 	try {
-		const res = await post(`/cabut-sanksi/add?noSanksi=${noSanksi}&ptId=${ptId}`, data);
-		console.log(res);
+		const res = await axios.post(`/sanksi/cabut-sanksi/create/${id}`, data, { headers: { Authorization: token } });
 		return res.data;
 	} catch (error) {
 		console.log("error", error);
@@ -11,10 +11,9 @@ export const addCabutSanksi = async ({ noSanksi, ptId }, data) => {
 	}
 };
 
-export const addJawabanCabutSanksi = async ({ noSanksi, ptId }, data) => {
+export const addJawabanCabutSanksi = async (token, id, data) => {
 	try {
-		const res = await post(`/cabut-sanksi/jawaban/add?noSanksi=${noSanksi}&ptId=${ptId}`, data);
-		console.log(res);
+		const res = await axios.post(`/sanksi/cabut-sanksi/jawaban/create/${id}`, data, { headers: { Authorization: token } });
 		return res.data;
 	} catch (error) {
 		console.log("error", error);

+ 3 - 4
actions/docPerbaikan.js

@@ -1,9 +1,8 @@
-import { post } from "../config/request";
+import axios from "../config/axios";
 
-export const addDocPerbaikan = async ({ noSanksi, ptId }, data) => {
+export const addDocPerbaikan = async (token, id, data) => {
 	try {
-		const res = await post(`/doc-perbaikan/add?noSanksi=${noSanksi}&ptId=${ptId}`, data);
-		console.log(res);
+		const res = await axios.post(`/sanksi/perbaikan/add/${id}`, data, { headers: { Authorization: token } });
 		return res.data;
 	} catch (error) {
 		console.log("error", error);

+ 5 - 6
actions/keberatan.js

@@ -1,9 +1,9 @@
 import { post } from "../config/request";
+import axiosAPI from "../config/axios";
 
-export const addKeberatan = async ({ noSanksi, ptId }, data) => {
+export const addKeberatan = async (token, id, data) => {
 	try {
-		const res = await post(`/keberatan/add?noSanksi=${noSanksi}&ptId=${ptId}`, data);
-		console.log(res);
+		const res = await axiosAPI.post(`/sanksi/keberatan/create/${id}`, data, { headers: { Authorization: token } });
 		return res.data;
 	} catch (error) {
 		console.log("error", error);
@@ -11,10 +11,9 @@ export const addKeberatan = async ({ noSanksi, ptId }, data) => {
 	}
 };
 
-export const addJawabanKeberatan = async ({ noSanksi, ptId }, data) => {
+export const addJawabanKeberatan = async (token, id, data) => {
 	try {
-		const res = await post(`/keberatan/jawaban/add?noSanksi=${noSanksi}&ptId=${ptId}`, data);
-		console.log(res);
+		const res = await axiosAPI.post(`/sanksi/keberatan/jawaban/create/${id}`, data, { headers: { Authorization: token } });
 		return res.data;
 	} catch (error) {
 		console.log("error", error);

+ 25 - 7
actions/log.js

@@ -1,10 +1,14 @@
 import { get } from "../config/request";
+import axios from "@/config/axios";
 
-export const getLog = async ({ ptId, isPT }) => {
+export const getLog = async (token, ptId) => {
 	try {
-		let url = `/log?ptId=${ptId}`;
-		if (isPT) url += `&isPT=true`;
-		const res = await get(url);
+		let url = `/pemantauan/${ptId}`;
+		const res = await axios.get(url, {
+			headers: {
+				Authorization: token,
+			},
+		});
 		return res.data;
 	} catch (error) {
 		console.log("error", error);
@@ -12,10 +16,24 @@ export const getLog = async ({ ptId, isPT }) => {
 	}
 };
 
-export const getLogPublic = async ({ ptId, laporanId }) => {
+export const getLogPT = async (token) => {
 	try {
-		let url = `/log/public?ptId=${ptId}&laporan=${laporanId}`;
-		const res = await get(url);
+		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 ({ no_hp, no_laporan }) => {
+	try {
+		const res = await axios.get(`/public/pemantauan?no_hp=${no_hp}&no_laporan=${no_laporan}`);
 		return res.data;
 	} catch (error) {
 		console.log("error", error);

+ 29 - 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,34 @@ export const getPelanggaranId = async (id) => {
 	}
 };
 
-export const getPelanggaran = async () => {
+export const getPelanggaran = async (token, query) => {
 	try {
-		const res = await fetch(API_URL + "/pelanggaran");
-		return await res.json();
+		let url = "/pelanggaran";
+		if (query != null) {
+			const { id } = query;
+			url += "?";
+			const parseURL = [];
+			if (id) {
+				parseURL.push(`id=${id}`);
+			}
+			url += parseURL.join("&");
+		}
+		const res = await axios.get(url, {
+			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;

+ 53 - 46
actions/pelaporan.js

@@ -1,26 +1,23 @@
 import { get, post, del, put } from "../config/request";
 import axiosAPI from "../config/axios";
 
-export const getPelaporan = async (query = {}) => {
+export const getPelaporan = async (token, query = {}) => {
 	try {
-		let url = "/pelaporan";
+		let url = "/laporan";
 		if (query != null) {
-			const { number, ptId, penjadwalan, pemeriksaan, active, role, orgId } = query;
+			const { jadwal, evaluasi } = query;
 			url += "?";
 			const parseURL = [];
-			if (number) parseURL.push(`number=${number}`);
-			if (ptId) parseURL.push(`ptId=${ptId}`);
-			if (penjadwalan) parseURL.push(`penjadwalan=true`);
-			if (pemeriksaan) parseURL.push(`pemeriksaan=true`);
-			if (active) parseURL.push(`active=${active}`);
-			if (role) {
-				parseURL.push(`role=${role}`);
-				if (role === "lldikti" && orgId) parseURL.push(`orgId=${orgId}`);
+			if (jadwal) {
+				parseURL.push(`jadwal=true`);
+			} else if (evaluasi) {
+				parseURL.push(`evaluasi=true`);
 			}
+
 			url += parseURL.join("&");
 		}
 
-		const res = await get(url);
+		const res = await axiosAPI.get(url, { headers: { Authorization: token } });
 		return res.data;
 	} catch (error) {
 		console.log("error", error);
@@ -28,19 +25,9 @@ export const getPelaporan = async (query = {}) => {
 	}
 };
 
-export const getPelaporanPublic = async ({ number, noHp }) => {
-	try {
-		const res = await get(`/pelaporan/public?number=${number}&noHp=${noHp}`);
-		return res.data;
-	} catch (error) {
-		console.log("error", error.response.data);
-		return false;
-	}
-};
-
-export const createPelaporan = async (data) => {
+export const getOneLaporan = async (token, id) => {
 	try {
-		const res = await axiosAPI.post("/pelaporan/create", data);
+		const res = await axiosAPI.get(`/laporan/${id}`, { headers: { Authorization: token } });
 		return res.data;
 	} catch (error) {
 		console.log("error", error);
@@ -48,42 +35,62 @@ export const createPelaporan = async (data) => {
 	}
 };
 
-export const addStatus = async ({ number, ptId }, data) => {
+export const getPelaporanPublic = async ({ number, noHp }) => {
 	try {
-		const res = await post(`/pelaporan/status/add?number=${number}&ptId=${ptId}`, data);
+		const res = await get(`/pelaporan/public?number=${number}&noHp=${noHp}`);
 		return res.data;
 	} catch (error) {
-		console.log("error", error);
+		console.log("error", error.response.data);
 		return false;
 	}
 };
 
-export const removeLaporan = async ({ number, ptId }) => {
-	try {
-		const res = await del(`/pelaporan/remove?number=${number}&ptId=${ptId}`);
-		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 activeLaporan = async ({ number, ptId }) => {
+export const createLaporanPublic = async (data) => {
 	try {
-		const res = await put(`/pelaporan/active?number=${number}&ptId=${ptId}`);
+		const res = await axiosAPI.post("/public/laporan/create", data);
 		return res.data;
 	} catch (error) {
-		console.log("error", error);
+		console.log("error", error.response.data);
 		return false;
 	}
 };
 
-export const changeRoleData = async ({ number, ptId }, data) => {
-	try {
-		const res = await post(`/pelaporan/change-role-data?number=${number}&ptId=${ptId}`, data);
-		return res.data;
-	} catch (error) {
-		console.log("error", error);
-		return false;
-	}
+// export const addStatus = async ({ number, ptId }, data) => {
+// 	try {
+// 		const res = await post(`/pelaporan/status/add?number=${number}&ptId=${ptId}`, data);
+// 		return res.data;
+// 	} catch (error) {
+// 		console.log("error", error);
+// 		return false;
+// 	}
+// };
+
+export const updateLaporan = async (token, id, data) => {
+	const res = await axiosAPI.put(`/laporan/update/${id}`, data, { headers: { Authorization: token } });
+	return res.data;
 };
+
+// export const activeLaporan = async ({ number, ptId }) => {
+// 	try {
+// 		const res = await put(`/pelaporan/active?number=${number}&ptId=${ptId}`);
+// 		return res.data;
+// 	} catch (error) {
+// 		console.log("error", error);
+// 		return false;
+// 	}
+// };
+
+// export const changeRoleData = async ({ number, ptId }, data) => {
+// 	try {
+// 		const res = await post(`/pelaporan/change-role-data?number=${number}&ptId=${ptId}`, data);
+// 		return res.data;
+// 	} catch (error) {
+// 		console.log("error", error);
+// 		return false;
+// 	}
+// };

+ 4 - 9
actions/pemeriksaan.js

@@ -1,11 +1,6 @@
-import { post } from "../config/request";
+import axiosAPI from "../config/axios";
 
-export const insertPemeriksaan = async ({ number, ptId }, data) => {
-	try {
-		const res = await post(`/pelaporan/pemeriksaan/create?number=${number}&ptId=${ptId}`, data);
-		return res.data;
-	} catch (error) {
-		console.log("error", error);
-		return false;
-	}
+export const insertPemeriksaan = async (token, id, data) => {
+	const res = await axiosAPI.post(`/laporan/evaluasi/add/${id}`, data, { headers: { Authorization: token } });
+	return res.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;
+	// }
 };

+ 26 - 21
actions/sanksi.js

@@ -1,9 +1,26 @@
-import { post, get } from "../config/request";
+import axiosAPI from "../config/axios";
 
-export const createSanksi = async ({ number, ptId }, data) => {
+export const createSanksi = async (token, id, data) => {
+	const res = await axiosAPI.post(`/sanksi/create/${id}`, data, { headers: { Authorization: token } });
+	return res.data;
+};
+
+export const getSanksi = async (token, query = {}) => {
 	try {
-		const res = await post(`/sanksi/create?number=${number}&ptId=${ptId}`, data);
-		console.log(res);
+		let url = "/sanksi";
+		if (query != {}) {
+			const { keberatan, jawaban, banding, cabutSanksi, perbaikan } = query;
+			url += "?";
+			const parseURL = [];
+			if (keberatan) parseURL.push(`keberatan=true`);
+			if (banding) parseURL.push(`banding=true`);
+			if (cabutSanksi) parseURL.push(`cabutSanksi=true`);
+			if (perbaikan) parseURL.push(`perbaikan=true`);
+			if (jawaban) parseURL.push(`jawaban=true`);
+			url += parseURL.join("&");
+		}
+
+		const res = await axiosAPI.get(url, { headers: { Authorization: token } });
 		return res.data;
 	} catch (error) {
 		console.log("error", error);
@@ -11,29 +28,17 @@ export const createSanksi = async ({ number, ptId }, data) => {
 	}
 };
 
-export const getSanksi = async (query = {}) => {
+export const getOneSanksi = async (token, id, query = {}) => {
 	try {
-		let url = "/sanksi";
-		if (query != null) {
-			const { ptId, noSanksi, keberatan, jawaban, banding, active, cabutSanksi, docPerbaikan, role, orgId } = query;
+		let url = `/sanksi/${id}`;
+		if (query != {}) {
+			const { banding } = query;
 			url += "?";
 			const parseURL = [];
-			if (noSanksi) parseURL.push(`noSanksi=${noSanksi}`);
-			if (ptId) parseURL.push(`ptId=${ptId}`);
-			if (keberatan) parseURL.push(`keberatan=true`);
 			if (banding) parseURL.push(`banding=true`);
-			if (cabutSanksi) parseURL.push(`cabutSanksi=true`);
-			if (docPerbaikan) parseURL.push(`docPerbaikan=true`);
-			if (jawaban) parseURL.push(`jawaban=true`);
-			if (role) {
-				parseURL.push(`role=${role}`);
-				if (role === "lldikti" && orgId) parseURL.push(`orgId=${orgId}`);
-			}
-			parseURL.push(`active=${active || "true"}`);
 			url += parseURL.join("&");
 		}
-
-		const res = await get(url);
+		const res = await axiosAPI.get(url, { headers: { Authorization: token } });
 		return res.data;
 	} catch (error) {
 		console.log("error", error);

+ 3 - 4
components/Banding/Riwayat.js

@@ -1,7 +1,6 @@
 import Datatable from "@/components/Tables/Datatable";
 import moment from "moment";
 import { Card, CardHeader, CardBody, CardTitle } from "reactstrap";
-import { API_URL } from "@/env";
 
 function Riwayat({ data }) {
 	return (
@@ -25,11 +24,11 @@ function Riwayat({ data }) {
 									<td>{moment(data.createAt).format("DD MMMM YYYY")}</td>
 									<td>{data.status}</td>
 									<td>
-										{data.files.map((e) => (
+										{data.dokumen.map((e) => (
 											<>
 												<em className="fa-lg far fa-file-code"></em>
-												<a className="text-muted" href={API_URL + e.path} target="_blank" download={e.name}>
-													{e.name}
+												<a className="text-muted" href={e.path} target="_blank" download={e.judul}>
+													{e.judul}
 												</a>
 											</>
 										))}

+ 6 - 6
components/Banding/TableSanksi.js

@@ -23,25 +23,25 @@ function TableSanksi({ listData, to, linkName }) {
 								? listData.map((data) => {
 										return (
 											<tr key={data._id}>
-												<td>{data.sanksi.no_sanksi}</td>
+												<td>{data.no_sanksi}</td>
 												<td>
 													<div className="media align-items-center">
 														<div className="media-body d-flex">
 															<div>
-																<h4 className="m-0">{data.pt.nama}</h4>
-																<p>{data.description.length > 25 ? data.description.substring(0, 25) + "..." : data.description}</p>
+																<h4 className="m-0">{data.laporan.pt.nama}</h4>
+																<p>{data.keterangan.length > 25 ? data.keterangan.substring(0, 25) + "..." : data.keterangan}</p>
 															</div>
 														</div>
 													</div>
 												</td>
-												<td>{moment(data.sanksi.createdAt).fromNow()}</td>
-												<td>{data.sanksi.banding.jawaban ? <div className="badge badge-info">Sudah Dijawab</div> : <div className="badge badge-danger">Belum Dijawab</div>}</td>
+												<td>{moment(data.createdAt).fromNow()}</td>
+												<td>{data.jawaban.banding ? <div className="badge badge-info">Sudah Dijawab</div> : <div className="badge badge-danger">Belum Dijawab</div>}</td>
 												<td>
 													<div className="ml-auto">
 														<Link
 															href={{
 																pathname: to,
-																query: { noSanksi: data.sanksi.no_sanksi, ptId: data.pt_id },
+																query: { id: data._id },
 															}}
 														>
 															<Button color="primary" size="sm">

+ 4 - 6
components/DocPerbaikan/Riwayat.js

@@ -1,10 +1,8 @@
 import Datatable from "@/components/Tables/Datatable";
 import moment from "moment";
 import { Card, CardHeader, CardBody, CardTitle } from "reactstrap";
-import { API_URL } from "@/env";
 
 function Riwayat({ data }) {
-	console.log(data);
 	return (
 		<Card className="card-default">
 			<CardHeader>
@@ -25,13 +23,13 @@ function Riwayat({ data }) {
 								? data.map((value) => (
 										<tr>
 											<td>{moment(value.createAt).format("DD MMMM YYYY")}</td>
-											<td>{value.description}</td>
+											<td>{value.keterangan}</td>
 											<td>
-												{value.files.map((e) => (
+												{value.dokumen.map((e) => (
 													<>
 														<em className="fa-lg far fa-file-code"></em>
-														<a className="text-muted" href={API_URL + e.path} target="_blank" download={e.name}>
-															{e.name}
+														<a className="text-muted" href={e.path} target="_blank" download={e.judul}>
+															{e.judul}
 														</a>
 													</>
 												))}

+ 143 - 129
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, updateLaporan } from "@/actions/pelaporan";
 import { updateJadwal } from "@/actions/penjadwalan";
 import DetailLaporan from "@/components/Main/DetailLaporan";
 import Link from "next/link";
@@ -11,13 +11,15 @@ import timeGridPlugin from "@fullcalendar/timegrid";
 import interactionPlugin, { Draggable } from "@fullcalendar/interaction";
 import listPlugin from "@fullcalendar/list";
 import bootstrapPlugin from "@fullcalendar/bootstrap";
-import events from "./calendar.events";
 import Select from "react-select";
 import moment from "moment";
 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 +32,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 = {
@@ -52,55 +58,44 @@ class Calendar extends Component {
 			dataEvent: [],
 			laporan: {},
 			selectedOption: null,
+			color: "",
 		};
 	}
 
 	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 { organisasi, peran } = this.props.user.peran[0];
-
-		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 { token, query } = this.props;
+		const laporan = await getOneLaporan(token, query.id);
+		const dataLaporan = await getPelaporan(token, { jadwal: true });
 
 		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({ color });
 	}
 
-	getStatus = () => (this.props.user.peran[0].peran.id === 2021 ? statusLLDIKTI : status);
+	componentShouldUpdate(nextProps, nextState) {
+		return nextState.dataLaporan !== this.state.dataLaporan;
+	}
+
+	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 +108,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,55 +115,55 @@ 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,
-		};
-		toast.promise(updateJadwal({ number, ptId }, data), {
-			pending: "Loading",
-			success: "Success",
-			error: "Error",
-		});
+	handleEventCalendar = async (data) => {
+		const { query, token } = this.props;
+		const { id } = query;
+		const { color, laporan } = this.state;
+
+		await toast.promise(
+			updateJadwal(token, id, {
+				judul: "No.Laporan " + laporan.data.no_laporan + " - " + data.judul,
+				dari_tanggal: data.dari_tanggal,
+				sampai_tanggal: data.sampai_tanggal,
+				warna: color,
+			}),
+			{
+				pending: "Loading",
+				success: "Success",
+				error: "Error",
+			}
+		);
+		const dataLaporan = await getPelaporan(token, { jadwal: true });
+		this.setState({ dataLaporan });
+		this.getDataEvent();
 	};
 
-	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 });
 
 	handleSimpan = async (e) => {
-		const { selectedOption } = this.state;
-		const { ptId, number } = this.props.query;
-		this.setState({ selectedOption });
-		const id = toast.loading("Please wait...");
-		if (selectedOption.value === this.getStatus()[1].value) {
-			const roleId = this.props.user.peran[0].peran.id;
-			await changeRoleData({ number, ptId }, { role_data: roleId === 2021 ? "dikti" : "lldikti" });
-			const statusData = roleId === 2021 ? status : statusLLDIKTI;
-			await addStatus({ number, ptId }, { status: statusData[0].value });
-			toast.update(id, { render: "All is good", type: "success", isLoading: false });
+		const toastid = toast.loading("Please wait...");
+		try {
+			const { selectedOption } = this.state;
+			const { token, query } = this.props;
+			const { id } = query;
+			this.setState({ selectedOption });
+			if (selectedOption.value === this.getStatus()[1].value) {
+				await updateLaporan(token, id, { change_role: "true" });
+			}
+			if (selectedOption.value === this.getStatus()[2].value) {
+				await updateLaporan(token, id, { aktif: "false" });
+			}
 			Router.push("/app/penjadwalan");
-			return;
-		}
-		if (selectedOption.value === this.getStatus()[2].value) {
-			await removeLaporan({ number, ptId });
-		} else if (!this.state.laporan.data[0].active) {
-			await activeLaporan({ number, ptId });
+
+			toast.update(toastid, { render: "All is good", type: "success", isLoading: false, autoClose: true, closeButton: true });
+		} catch (error) {
+			toast.update(toastid, { render: "All is not good", type: "error", isLoading: false, autoClose: true, closeButton: true });
 		}
-		await addStatus({ number, ptId }, { status: selectedOption.value });
-		toast.update(id, { render: "All is good", type: "success", isLoading: false, autoClose: true, closeButton: true });
 	};
 
 	render() {
@@ -193,7 +184,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>
@@ -215,7 +206,7 @@ class Calendar extends Component {
 										</CardHeader>
 										<CardBody>
 											<Select value={selectedOption} onChange={this.handleChangeSelect} options={this.getStatus()} required />
-											<Button onClick={this.handleSimpan} className="mt-2" color="primary" block>
+											<Button onClick={this.handleSimpan} className="mt-2" color="primary" block disabled={laporan.data?.evaluasi.length}>
 												Simpan
 											</Button>
 										</CardBody>
@@ -226,50 +217,78 @@ 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 ? laporan.data.jadwal.judul.split("- ")[1] : "",
+															dari_tanggal: laporan.data?.jadwal?.dari_tanggal ? moment(laporan.data.jadwal.dari_tanggal) : "",
+															sampai_tanggal: laporan.data?.jadwal?.sampai_tanggal ? moment(laporan.data.jadwal.sampai_tanggal) : "",
+														}}
+														validationSchema={jadwalSchema}
+														onSubmit={this.handleEventCalendar}
+													>
+														{() => (
+															<Form>
+																<FormGroup>
+																	<label className="col-form-label">Warna</label>
+																	<div className="badge d-block" style={{ backgroundColor: this.state.color, color: "white" }}>
+																		{this.state.color}
+																	</div>
+																</FormGroup>
+
+																<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={laporan.data?.evaluasi.length}>
+																			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 +317,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 +330,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);

+ 4 - 4
components/Keberatan/Riwayat.js

@@ -25,13 +25,13 @@ function Riwayat({ data }) {
 								<tr>
 									<td>{moment(data.createAt).format("DD MMMM YYYY")}</td>
 									<td>{data.status}</td>
-									<td>{data.description}</td>
+									<td>{data.keterangan}</td>
 									<td>
-										{data.files.map((e) => (
+										{data.dokumen.map((e) => (
 											<>
 												<em className="fa-lg far fa-file-code"></em>
-												<a className="text-muted" href={API_URL + e.path} target="_blank" download={e.name}>
-													{e.name}
+												<a className="text-muted" href={e.path} target="_blank" download={e.judul}>
+													{e.judul}
 												</a>
 											</>
 										))}

+ 6 - 6
components/Keberatan/TableSanksi.js

@@ -23,25 +23,25 @@ function TableSanksi({ listData, to, linkName }) {
 								? listData.map((data) => {
 										return (
 											<tr key={data._id}>
-												<td>{data.sanksi.no_sanksi}</td>
+												<td>{data.no_sanksi}</td>
 												<td>
 													<div className="media align-items-center">
 														<div className="media-body d-flex">
 															<div>
-																<h4 className="m-0">{data.pt.nama}</h4>
-																<p>{data.description.length > 25 ? data.description.substring(0, 25) + "..." : data.description}</p>
+																<h4 className="m-0">{data.laporan.pt.nama}</h4>
+																<p>{data.keterangan.length > 25 ? data.keterangan.substring(0, 25) + "..." : data.keterangan}</p>
 															</div>
 														</div>
 													</div>
 												</td>
-												<td>{moment(data.sanksi.createdAt).fromNow()}</td>
-												<td>{data.sanksi.keberatan.jawaban ? <div className="badge badge-info">Sudah Dijawab</div> : <div className="badge badge-danger">Belum Dijawab</div>}</td>
+												<td>{moment(data.createdAt).fromNow()}</td>
+												<td>{data.jawaban?.keberatan ? <div className="badge badge-info">Sudah Dijawab</div> : <div className="badge badge-danger">Belum Dijawab</div>}</td>
 												<td>
 													<div className="ml-auto">
 														<Link
 															href={{
 																pathname: to,
-																query: { noSanksi: data.sanksi.no_sanksi, ptId: data.pt_id },
+																query: { id: data._id },
 															}}
 														>
 															<Button color="primary" size="sm">

+ 1 - 1
components/Layout/Head.js

@@ -7,7 +7,7 @@ const defaultDescription = "";
 const Head = (props) => (
 	<NextHead>
 		<meta charSet="UTF-8" />
-		<title>PTB-DiktiRistek</title>
+		<title>DiktiRistek</title>
 		<meta name="description" content={props.description || defaultDescription} />
 		<meta name="viewport" content="width=device-width, initial-scale=1" />
 		<link rel="icon" href="/static/img/logo-single.png" />

+ 5 - 1
components/Layout/Header.js

@@ -57,7 +57,9 @@ class Header extends Component {
 
 	handleLogout = async (e) => {
 		e.preventDefault();
-		await logout();
+		// await logout();
+		this.props.tokenNull();
+		this.props.userNull();
 		// if (cek.success) {
 		Router.push({ pathname: "/app" });
 		// }
@@ -223,6 +225,8 @@ Header.propTypes = {
 const mapStateToProps = (state) => ({ settings: state.settings });
 const mapDispatchToProps = (dispatch) => ({
 	actions: bindActionCreators(actions, dispatch),
+	tokenNull: () => dispatch({ type: "SET_TOKEN", payload: null }),
+	userNull: () => dispatch({ type: "SET_USER", payload: null }),
 });
 
 export default connect(mapStateToProps, mapDispatchToProps)(Header);

+ 11 - 2
components/Layout/Menu.js

@@ -11,9 +11,18 @@ const Menu = [
 	},
 	{
 		name: "Pelaporan",
-		path: "/app/pelaporan",
 		icon: "icon-notebook",
-		translate: "sidebar.nav.PELAPORAN",
+		translate: "sidebar.nav.LAPORAN",
+		submenu: [
+			{
+				name: "List Laporan",
+				path: "/app/pelaporan",
+			},
+			{
+				name: "Laporan ditutup",
+				path: "/app/laporan-ditutup",
+			},
+		],
 	},
 	{
 		name: "Penjadwalan Evaluasi",

+ 11 - 2
components/Layout/MenuLLDIKTI.js

@@ -11,9 +11,18 @@ const Menu = [
 	},
 	{
 		name: "Pelaporan",
-		path: "/app/pelaporan",
 		icon: "icon-notebook",
-		translate: "sidebar.nav.PELAPORAN",
+		translate: "sidebar.nav.LAPORAN",
+		submenu: [
+			{
+				name: "List Laporan",
+				path: "/app/pelaporan",
+			},
+			{
+				name: "Laporan ditutup",
+				path: "/app/laporan-ditutup",
+			},
+		],
 	},
 	{
 		name: "Penjadwalan Evaluasi",

+ 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",
 	},

+ 1 - 1
components/Layout/Sidebar.js

@@ -148,7 +148,7 @@ class Sidebar extends Component {
 	async componentDidMount() {
 		// const user = await getUser();
 		const user = this.props.user;
-		this.menu = user.peran[0].peran.id === 2022 ? MenuPT : user.peran[0].peran.id === 2021 ? MenuLLDIKTI : Menu;
+		this.menu = user.role.id === 2022 ? MenuPT : user.role.id === 2021 ? MenuLLDIKTI : Menu;
 		// prepare the flags to handle menu collapsed states
 		this.buildCollapseList();
 

+ 2 - 3
components/Layout/SidebarUserBlock.js

@@ -8,13 +8,12 @@ class SidebarUserBlock extends Component {
 	state = {
 		showUserBlock: true,
 		user: {},
-		role: "",
+		role: {},
 	};
 
 	async componentDidMount() {
-		// const user = await getUser();
 		const user = this.props.user;
-		this.setState({ user, role: user.peran[0].peran });
+		this.setState({ user, role: user.role });
 	}
 
 	componentDidUpdate(oldProps) {

+ 24 - 20
components/Main/DetailLaporan.js

@@ -1,54 +1,58 @@
-import React, { useEffect, useState } from "react";
 import Scrollable from "@/components/Common/Scrollable";
 import moment from "moment";
 import { Col, FormGroup } from "reactstrap";
 import { useSelector } from "react-redux";
-import { getPT } from "@/actions/PT";
-import { API_URL } from "@/env";
 
 function DetailLaporan({ data, noTitle = false, noStatus = false }) {
 	const user = useSelector((state) => state.user);
-
 	return (
 		<>
-			{(!data.user_id.isPrivate || user.peran[0].peran.id === 2020) && (
+			{(!data.user.isPrivate || user.role.id === 2020) && (
 				<>
-					{noTitle ? "" : <p className="lead bb">Identitas Pelapor - {data.user_id.isPublic ? "Umum" : "Internal"}</p>}
+					{noTitle ? "" : <p className="lead bb">Identitas Pelapor - {data.user.isPublic ? "Umum" : "Internal"}</p>}
 					<FormGroup row>
 						<Col md="4">Nama Pelapor:</Col>
 						<Col md="8">
-							<strong>{data.user_id.nama}</strong>
+							<strong>{data.user.nama}</strong>
 						</Col>
 					</FormGroup>
 					<FormGroup row>
 						<Col md="4">Nomor yang dapat dihubungi:</Col>
 						<Col md="8">
-							<strong>{data.user_id.no_hp}</strong>
+							<strong>{data.user.no_hp}</strong>
 						</Col>
 					</FormGroup>
 					<FormGroup row>
 						<Col md="4">Email:</Col>
 						<Col md="8">
-							<strong>{data.user_id.email}</strong>
+							<strong>{data.user.email}</strong>
 						</Col>
 					</FormGroup>
 
-					{data.user_id.isPublic && (
+					{data.user.isPublic && (
 						<>
 							<FormGroup row>
 								<Col md="4">Alamat:</Col>
 								<Col md="8">
-									<strong>{data.user_id.alamat}</strong>
+									<strong>{data.user.alamat}</strong>
 								</Col>
 							</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" />
+									<img src={data.user.foto.path} height={200} alt="Foto Identitas" />
 								</Col>
 							</FormGroup>
 						</>
 					)}
+					{data.user.isPrivate && (
+						<FormGroup row>
+							<Col md="4">Dirahasiakan</Col>
+							<Col md="8">
+								<strong>Ya</strong>
+							</Col>
+						</FormGroup>
+					)}
 				</>
 			)}
 			{noTitle ? "" : <p className="lead bb">Detail Laporan</p>}
@@ -56,7 +60,7 @@ function DetailLaporan({ data, noTitle = false, noStatus = false }) {
 				<FormGroup row>
 					<Col md="4">Nomor Laporan:</Col>
 					<Col md="8">
-						<strong>{data._number}</strong>
+						<strong>{data.no_laporan}</strong>
 					</Col>
 				</FormGroup>
 				<FormGroup row>
@@ -81,7 +85,7 @@ function DetailLaporan({ data, noTitle = false, noStatus = false }) {
 					<Col md="4">Keterangan Laporan:</Col>
 					<Col md="8">
 						<Scrollable height="100px" className="list-group">
-							<p>{data.description}</p>
+							<p>{data.keterangan}</p>
 						</Scrollable>
 					</Col>
 				</FormGroup>
@@ -91,30 +95,30 @@ 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.status}</div>
+							<div className="badge badge-info">{data.role_data === "dikti" ? "Ditindaklanjuti DIKTI" : "Ditindaklanjuti LLDIKTI"}</div>
 						</Col>
 					</FormGroup>
 				) : (
 					""
 				)}
 				<FormGroup row>
-					<Col md="4">File Pendukung:</Col>
+					<Col md="4">Dokumen Pendukung:</Col>
 					<Col md="8">
 						<Scrollable height="120px" className="list-group">
 							<table className="table table-bordered bg-transparent">
 								<tbody>
-									{data.files.map((e, index) => (
+									{data.dokumen.map((e, index) => (
 										<tr key={`files-${index}`}>
 											<td>
 												<em className="fa-lg far fa-file-code"></em>
 											</td>
 											<td>
-												<a className="text-muted" href={API_URL + e.path} target="_blank" download={e.name}>
-													{e.name}
+												<a className="text-muted" href={e.path} target="_blank" download={e.judul}>
+													{e.judul}
 												</a>
 											</td>
 										</tr>

+ 8 - 8
components/Main/DetailSanksi.js

@@ -11,13 +11,13 @@ function DetailSanksi({ data, noTitle = false }) {
 				<FormGroup row>
 					<Col md="4">Nomor Sanksi:</Col>
 					<Col md="8">
-						<strong>{data.sanksi.no_sanksi}</strong>
+						<strong>{data.no_sanksi}</strong>
 					</Col>
 				</FormGroup>
 				<FormGroup row>
 					<Col md="4">Nama Perguruan Tinggi:</Col>
 					<Col md="8">
-						<strong>{data.pt.nama}</strong>
+						<strong>{data.laporan.pt.nama}</strong>
 					</Col>
 				</FormGroup>
 
@@ -25,14 +25,14 @@ function DetailSanksi({ data, noTitle = false }) {
 					<Col md="4">Keterangan:</Col>
 					<Col md="8">
 						<Scrollable height="100px" className="list-group">
-							<p>{data.sanksi.description}</p>
+							<p>{data.keterangan}</p>
 						</Scrollable>
 					</Col>
 				</FormGroup>
 				<FormGroup row>
 					<Col md="4">Dibuat Pada:</Col>
 					<Col md="8">
-						<strong>{moment(data.sanksi.createdAt).format("D MMMM YYYY")}</strong>
+						<strong>{moment(data.createdAt).format("D MMMM YYYY")}</strong>
 					</Col>
 				</FormGroup>
 				<FormGroup row>
@@ -41,14 +41,14 @@ function DetailSanksi({ data, noTitle = false }) {
 						<Scrollable height="120px" className="list-group">
 							<table className="table table-bordered bg-transparent">
 								<tbody>
-									{data.sanksi.files.map((e) => (
+									{data.dokumen.map((e) => (
 										<tr>
 											<td>
 												<em className="fa-lg far fa-file-code"></em>
 											</td>
 											<td>
-												<a className="text-muted" href={API_URL + e.path} target="_blank" download={e.name}>
-													{e.name}
+												<a className="text-muted" href={e.path} target="_blank" download={e.judul}>
+													{e.judul}
 												</a>
 											</td>
 										</tr>
@@ -70,7 +70,7 @@ function DetailSanksi({ data, noTitle = false }) {
 										</tr>
 									</thead>
 									<tbody>
-										{data.sanksi.pelanggaran.map((jp, index) => (
+										{data.pelanggaran.map((jp, index) => (
 											<tr key={jp._id}>
 												<td width={50}>
 													<div className="media align-items-center">

+ 23 - 28
components/Main/Login.js

@@ -46,42 +46,36 @@ class Login extends Component {
 	};
 
 	onSubmit = async (e) => {
-		const form = e.target;
-		const inputs = [...form.elements].filter((i) => ["INPUT", "SELECT"].includes(i.nodeName));
+		try {
+			const form = e.target;
+			const inputs = [...form.elements].filter((i) => ["INPUT", "SELECT"].includes(i.nodeName));
 
-		const { errors, hasError } = FormValidator.bulkValidate(inputs);
+			const { errors, hasError } = FormValidator.bulkValidate(inputs);
 
-		this.setState({
-			[form.name]: {
-				...this.state[form.name],
-				errors,
-			},
-		});
+			this.setState({
+				[form.name]: {
+					...this.state[form.name],
+					errors,
+				},
+			});
 
-		console.log(hasError ? "Form has errors. Check!" : "Form Submitted!");
-		e.preventDefault();
-		if (!hasError) {
-			const { username, password } = this.state.formLogin;
-			const auth = await login(username, password);
-			if (auth.success) {
-				axiosAPI.defaults.headers.Authorization = `Bearer ${auth.access_token}`;
-				const dataUser = await getUser();
-				this.props.setUser(dataUser.data);
-				if (dataUser.data.peran[0].peran.id === 2022) {
-					const org_id = dataUser.data.peran[0].organisasi.id;
-					const pt = await getPT({ id: org_id });
-					if (pt?.success) {
-						this.props.setPT(pt.data);
-					}
-					Router.push({ pathname: "/app/pt/pemantauan" });
+			console.log(hasError ? "Form has errors. Check!" : "Form Submitted!");
+			e.preventDefault();
+			if (!hasError) {
+				const { username, password } = this.state.formLogin;
+				const auth = await login(username, password);
+				this.props.setToken(auth.data.token);
+				this.props.setUser(auth.data.user);
+				axiosAPI.defaults.headers.common["Authorization"] = auth.data.token;
+				if (auth.data.user.role.id === 2022) {
+					Router.push({ pathname: "/pt/pemantauan" });
 				} else {
 					Router.push({ pathname: "/app/pemantauan" });
 				}
-			} else {
-				this.setState({ error: auth.message || auth.error });
 			}
+		} catch (error) {
+			this.setState({ error: error.response.data.message || error.response });
 		}
-		// e.preventDefault();
 	};
 
 	/* Simplify error check */
@@ -132,6 +126,7 @@ class Login extends Component {
 const mapStateToProps = (state) => ({ user: state.user });
 const mapDispatchToProps = (dispatch) => ({
 	setUser: (payload) => dispatch({ type: "SET_USER", payload }),
+	setToken: (payload) => dispatch({ type: "SET_TOKEN", payload }),
 	setPT: (payload) => dispatch({ type: "SET_PT", payload }),
 });
 

+ 3 - 4
components/Main/PermohonanPT.js

@@ -1,6 +1,5 @@
 import Scrollable from "@/components/Common/Scrollable";
 import { Col, FormGroup } from "reactstrap";
-import { API_URL } from "@/env";
 
 function PermohonanPT({ data, title = null }) {
 	return (
@@ -13,14 +12,14 @@ function PermohonanPT({ data, title = null }) {
 						<Scrollable height="120px" className="list-group">
 							<table className="table table-bordered bg-transparent">
 								<tbody>
-									{data.files.map((e) => (
+									{data.dokumen.map((e) => (
 										<tr>
 											<td>
 												<em className="fa-lg far fa-file-code"></em>
 											</td>
 											<td>
-												<a className="text-muted" href={API_URL + e.path} target="_blank" download={e.name}>
-													{e.name}
+												<a className="text-muted" href={e.path} target="_blank" download={e.judul}>
+													{e.judul}
 												</a>
 											</td>
 										</tr>

+ 5 - 5
components/Main/TableLaporan.js

@@ -24,32 +24,32 @@ function TableLaporan({ listData, to, linkName, status = false, noBy = false })
 								{listData.map((data) => {
 									return (
 										<tr key={data._id}>
-											<td>{data._number}</td>
+											<td>{data.no_laporan}</td>
 											<td className="text-nowrap">
 												<div className="media align-items-center">
 													<div className="media-body d-flex">
 														<div>
 															<h4 className="m-0">{data.pt.nama}</h4>
-															<p>{data.description.length > 35 ? data.description.substring(0, 35) + "..." : data.description}</p>
+															<p>{data.keterangan.length > 35 ? data.keterangan.substring(0, 35) + "..." : data.keterangan}</p>
 														</div>
 													</div>
 												</div>
 											</td>
 											{status ? (
 												<td>
-													<div className="badge badge-info">{data.status}</div>
+													<div className="badge badge-info">{data.role_data === "dikti" ? "Ditindaklanjuti DIKTI" : "Ditindaklanjuti LLDIKTI"}</div>
 												</td>
 											) : (
 												""
 											)}
-											{!noBy && <td>{data.user_id.isPrivate ? "" : data.user_id.nama}</td>}
+											{!noBy && <td>{data.user.isPrivate ? "" : data.user.nama}</td>}
 											<td>{moment(data.createdAt).fromNow()}</td>
 											<td>
 												<div className="ml-auto">
 													<Link
 														href={{
 															pathname: to,
-															query: { ptId: data.pt_id, number: data._number },
+															query: { id: data._id },
 														}}
 													>
 														<Button color="primary" size="sm">

+ 6 - 7
components/Main/TableSanksi.js

@@ -14,7 +14,7 @@ function TableSanksi({ listData, to, linkName }) {
 								<tr>
 									<th>Nomor Sanksi</th>
 									<th>Keterangan Sanksi</th>
-									{listData?.sanksi?.user_id.nama && <th>Dibuat Oleh</th>}
+									{/* {listData?.user.nama && <th>Dibuat Oleh</th>} */}
 									<th>Created</th>
 									<th></th>
 								</tr>
@@ -24,25 +24,24 @@ function TableSanksi({ listData, to, linkName }) {
 									? listData.map((data) => {
 											return (
 												<tr key={data._id}>
-													<td>{data.sanksi.no_sanksi}</td>
+													<td>{data.no_sanksi}</td>
 													<td>
 														<div className="media align-items-center">
 															<div className="media-body d-flex">
 																<div>
-																	<h4 className="m-0">{data.pt.nama}</h4>
-																	<p>{data.description.length > 25 ? data.description.substring(0, 25) + "..." : data.description}</p>
+																	<h4 className="m-0">{data.laporan.pt.nama}</h4>
+																	<p>{data.keterangan.length > 25 ? data.keterangan.substring(0, 25) + "..." : data.keterangan}</p>
 																</div>
 															</div>
 														</div>
 													</td>
-													{data.sanksi.user_id?.nama && <td>{data.sanksi.user_id.nama}</td>}
-													<td>{moment(data.sanksi.createdAt).fromNow()}</td>
+													<td>{moment(data.createdAt).fromNow()}</td>
 													<td>
 														<div className="ml-auto">
 															<Link
 																href={{
 																	pathname: to,
-																	query: { noSanksi: data.sanksi.no_sanksi, ptId: data.pt_id },
+																	query: { id: data._id },
 																}}
 															>
 																<Button color="primary" size="sm">

+ 27 - 12
components/Main/Timeline.js

@@ -1,7 +1,7 @@
 import moment from "moment";
 import { API_URL } from "@/env";
 
-function Timeline({ data, noFile = false }) {
+function Timeline({ data, noFile = false, noJadwal = false }) {
 	const date = data && [...new Set(data.map((e) => moment(e.createdAt).format("DD MMMM YYYY")))];
 	return (
 		<ul className="timeline-alt">
@@ -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">
@@ -22,24 +22,39 @@ function Timeline({ data, noFile = false }) {
 											<div className="arrow"></div>
 											<div className="popover-body">
 												<div className="d-flex align-items-center mb-3">
-													<img className="mr-3 rounded-circle thumb48" src={`/static/img${data.role === "PT" ? "/univ-avatar.png" : data.role === "UMUM" ? "/user/user.png" : "/logo-single.png"}`} alt="Avatar" />
+													<img
+														className="mr-3 rounded-circle thumb48"
+														src={`/static/img${data.user.role?.id === 2022 ? "/univ-avatar.png" : data.user.isPublic ? "/user/user.png" : "/logo-single.png"}`}
+														alt="Avatar"
+													/>
 													<p className="m-0">
-														<strong>{data.role_name}</strong>
+														<strong>
+															{data.user.nama} - {data.user.isPublic ? "Umum" : data.user.role.nama}
+														</strong>
 														<br />
-														{data.description}
+														{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.files.length ? (
+												{!noJadwal && data.jadwal && (
+													<>
+														<p className="text-muted my-2">Jadwal Pemeriksaan</p>
+														<p>
+															Tanggal {moment(data.jadwal.dari_tanggal).format("DD MMMM YYYY")} - {moment(data.jadwal.sampai_tanggal).format("DD MMMM YYYY")}
+														</p>
+													</>
+												)}
+												{!noFile && data.dokumen.length ? (
 													<>
 														<p className="text-muted my-2">Dokumen</p>
-														{data.files.map((e) => (
+														{data.dokumen.map((e) => (
 															<div className="media bb p-2">
 																<div className="media-body">
 																	<p className="m-0">
-																		<a href={API_URL + e.path} target="_blank" download={e.name}>
-																			<strong>{e.name}</strong>
+																		<a href={e.path} target="_blank" download={e.judul}>
+																			<strong>{e.judul}</strong>
 																		</a>
 																	</p>
 																</div>

+ 5 - 6
components/PT/DocPerbaikan/Riwayat.js

@@ -1,7 +1,6 @@
 import Datatable from "@/components/Tables/Datatable";
 import moment from "moment";
 import { Card, CardHeader, CardBody, CardTitle } from "reactstrap";
-import { API_URL } from "@/env";
 
 function Riwayat({ data }) {
 	return (
@@ -20,17 +19,17 @@ function Riwayat({ data }) {
 							</tr>
 						</thead>
 						<tbody>
-							{data && data.length
+							{data.length
 								? data.map((value) => (
 										<tr>
 											<td>{moment(value.createAt).format("DD MMMM YYYY")}</td>
-											<td>{value.description}</td>
+											<td>{value.keterangan}</td>
 											<td>
-												{value.files.map((e) => (
+												{value.dokumen.map((e) => (
 													<>
 														<em className="fa-lg far fa-file-code"></em>
-														<a className="text-muted" href={API_URL + e.path} target="_blank" download={e.name}>
-															{e.name}
+														<a className="text-muted" href={e.path} target="_blank" download={e.judul}>
+															{e.judul}
 														</a>
 													</>
 												))}

+ 31 - 28
components/PT/JawabanBanding/DetailJawaban.js

@@ -4,7 +4,6 @@ import Link from "next/link";
 import { API_URL } from "@/env";
 
 function DetailJawaban({ data }) {
-	const { jawaban } = data.sanksi.banding;
 	return (
 		<>
 			<p className="lead bb">Jawaban Permohonan Banding</p>
@@ -12,38 +11,42 @@ function DetailJawaban({ data }) {
 				<FormGroup>
 					<label md="4">Jawaban:</label>
 					<div md="8">
-						<h3>{jawaban.status}</h3>
-					</div>
-				</FormGroup>
-				<FormGroup>
-					<label md="4">Dokumen Jawaban:</label>
-					<div md="8">
-						<Scrollable height="120px" className="list-group">
-							<table className="table table-bordered bg-transparent">
-								<tbody>
-									{jawaban.files.map((e) => (
-										<tr>
-											<td>
-												<em className="fa-lg far fa-file-code"></em>
-											</td>
-											<td>
-												<a className="text-muted" href={API_URL + e.path} target="_blank" download={e.name}>
-													{e.name}
-												</a>
-											</td>
-										</tr>
-									))}
-								</tbody>
-							</table>
-						</Scrollable>
+						<h3>{data.status}</h3>
 					</div>
 				</FormGroup>
+				{data.dokumen.length ? (
+					<FormGroup>
+						<label md="4">Dokumen Jawaban:</label>
+						<div md="8">
+							<Scrollable height="120px" className="list-group">
+								<table className="table table-bordered bg-transparent">
+									<tbody>
+										{data.dokumen.map((e) => (
+											<tr>
+												<td>
+													<em className="fa-lg far fa-file-code"></em>
+												</td>
+												<td>
+													<a className="text-muted" href={e.path} target="_blank" download={e.judul}>
+														{e.judul}
+													</a>
+												</td>
+											</tr>
+										))}
+									</tbody>
+								</table>
+							</Scrollable>
+						</div>
+					</FormGroup>
+				) : (
+					""
+				)}
 			</form>
-			{jawaban.status === "Ditolak" && (
+			{data.status === "Ditolak" && (
 				<Link
 					href={{
-						pathname: "/app/pt/dokumen-perbaikan/detail",
-						query: { noSanksi: data.sanksi.no_sanksi },
+						pathname: "/pt/dokumen-perbaikan/detail",
+						query: { id: data._id },
 					}}
 				>
 					<Button color="primary">Perbaiki Dokumen</Button>

+ 6 - 7
components/PT/JawabanBanding/TableSanksiJawaban.js

@@ -19,23 +19,22 @@ function TableSanksi({ listData, to, linkName }) {
 						{listData.map((data) => {
 							return (
 								<tr key={data._id}>
-									<td>{data.sanksi.no_sanksi}</td>
+									<td>{data.no_sanksi}</td>
 									<td className="text-nowrap">
 										<div className="media align-items-center">
 											{/* <img className="img-fluid rounded thumb64" src="/static/img/dummy-search.png" alt="Dummy" /> */}
 											<div className="media-body d-flex">
 												<div>
-													<h4 className="m-0">{data.pt.nama}</h4>
-													{/* <small className="text-muted">0742/O/1990 - www.satyagama.ac.id - info@satyagama.ac.id</small> */}
-													<p>{data.sanksi.description.length > 70 ? data.sanksi.description.substring(0, 70) + "..." : data.sanksi.description}</p>
+													<h4 className="m-0">{data.laporan.pt.nama}</h4>
+													<p>{data.keterangan.length > 70 ? dataketerangan.substring(0, 70) + "..." : data.keterangan}</p>
 												</div>
 											</div>
 										</div>
 									</td>
-									<td>{moment(data.sanksi.createdAt).format("DD MMMM YYYY")}</td>
+									<td>{moment(data.createdAt).format("DD MMMM YYYY")}</td>
 									<td>
-										{data.sanksi.banding?.jawaban ? (
-											<Link href={{ pathname: to, query: { noSanksi: data.sanksi.no_sanksi } }}>
+										{data.jawaban?.banding ? (
+											<Link href={{ pathname: to, query: { id: data._id } }}>
 												<Button color="primary">{linkName}</Button>
 											</Link>
 										) : (

+ 5 - 7
components/PT/JawabanKeberatan/DetailJawaban.js

@@ -1,9 +1,7 @@
 import { FormGroup } from "reactstrap";
 import Scrollable from "@/components/Common/Scrollable";
-import { API_URL } from "@/env";
 
 function DetailJawaban({ data }) {
-	const { jawaban } = data.sanksi.keberatan;
 	return (
 		<>
 			<p className="lead bb">Jawaban Permohonan Keberatan</p>
@@ -11,13 +9,13 @@ function DetailJawaban({ data }) {
 				<FormGroup>
 					<label md="4">Jawaban:</label>
 					<div md="8">
-						<h3>{jawaban.status}</h3>
+						<h3>{data.status}</h3>
 					</div>
 				</FormGroup>
 				<FormGroup>
 					<label md="4">Keterangan:</label>
 					<div md="8">
-						<p>{jawaban.description}</p>
+						<p>{data.keterangan}</p>
 					</div>
 				</FormGroup>
 				<FormGroup>
@@ -26,14 +24,14 @@ function DetailJawaban({ data }) {
 						<Scrollable height="120px" className="list-group">
 							<table className="table table-bordered bg-transparent">
 								<tbody>
-									{jawaban.files.map((e) => (
+									{data.dokumen.map((e) => (
 										<tr>
 											<td>
 												<em className="fa-lg far fa-file-code"></em>
 											</td>
 											<td>
-												<a className="text-muted" href={API_URL + e.path} target="_blank" download={e.name}>
-													{e.name}
+												<a className="text-muted" href={e.path} target="_blank" download={e.judul}>
+													{e.judul}
 												</a>
 											</td>
 										</tr>

+ 20 - 16
components/PT/JawabanKeberatan/ModalPermohonan.js

@@ -3,7 +3,6 @@ import Router from "next/router";
 import { Row, Col, FormGroup, Button, Modal, ModalHeader, ModalBody, ModalFooter } from "reactstrap";
 import { addBanding } from "@/actions/banding";
 import { connect } from "react-redux";
-import { notifBanding } from "@/actions/notifikasi";
 import { toast } from "react-toastify";
 
 let Dropzone = null;
@@ -26,6 +25,7 @@ export class ModalPermohonan extends Component {
 		this.state = {
 			modal1: false,
 			files: [],
+			error: null,
 		};
 	}
 
@@ -68,36 +68,40 @@ export class ModalPermohonan extends Component {
 
 	onSubmit = async (e) => {
 		e.preventDefault();
-		const { user, query, data } = this.props;
-		const { noSanksi } = query;
+		const { query, token } = this.props;
+		const { id } = query;
 		const formdata = new FormData();
 		if (this.state.files.length > 0) {
+			this.setState({
+				modal1: !this.state.modal1,
+			});
+
 			this.state.files.forEach((e) => {
-				formdata.append("files", e);
+				formdata.append("dokumen", e);
 			});
 
-			const id = toast.loading("Please wait...");
-			const added = await addBanding({ noSanksi, ptId: user.peran[0].organisasi.id }, formdata);
+			const toastid = toast.loading("Please wait...");
+			const added = await addBanding(token, id, formdata);
 
-			if (added) {
-				toast.update(id, { render: "All is good", type: "success", isLoading: false, autoClose: true, closeButton: true });
-				const notif = await notifBanding({ lembaga: data.sanksi.user.lembaga, pt_name: user.peran[0].organisasi.nama, no_sanksi: data.sanksi.no_sanksi });
+			if (!added) {
+				toast.update(toastid, { render: "All is not good", type: "error", isLoading: false, autoClose: true, closeButton: true });
+			} else {
+				toast.update(toastid, { render: "All is good", type: "success", isLoading: false, autoClose: true, closeButton: true });
 				Router.push({
-					pathname: "/app/pt/jawaban-keberatan",
+					pathname: "/pt/jawaban-banding",
 				});
 			}
+		} else {
+			this.setState({ error: "Dokumen harus ada" });
 		}
 	};
 
 	handleKirim = (e) => {
-		this.setState({
-			modal1: !this.state.modal1,
-		});
 		this.onSubmit(e);
 	};
 
 	render() {
-		const { files } = this.state;
+		const { files, error } = this.state;
 
 		const thumbs = files.map((file, index) => (
 			<Col md={3} key={index}>
@@ -141,7 +145,7 @@ export class ModalPermohonan extends Component {
 											);
 										}}
 									</DropzoneWrapper>
-									<span className="form-text">Multiple files upload</span>
+									<span className={`form-text ${error ? "text-danger" : ""}`}>{error ? error : "Multiple files upload"}</span>
 								</div>
 							</FormGroup>
 						</form>
@@ -157,5 +161,5 @@ export class ModalPermohonan extends Component {
 	}
 }
 
-const mapStateToProps = (state) => ({ user: state.user });
+const mapStateToProps = (state) => ({ user: state.user, token: state.token });
 export default connect(mapStateToProps)(ModalPermohonan);

+ 5 - 7
components/PT/JawabanKeberatan/Riwayat.js

@@ -1,10 +1,8 @@
 import Datatable from "@/components/Tables/Datatable";
 import moment from "moment";
 import { Card, CardHeader, CardBody, CardTitle } from "reactstrap";
-import { API_URL } from "@/env";
 
 function Riwayat({ data }) {
-	const { banding } = data.sanksi;
 	return (
 		<Card className="card-default">
 			<CardHeader>
@@ -20,15 +18,15 @@ function Riwayat({ data }) {
 							</tr>
 						</thead>
 						<tbody>
-							{banding ? (
+							{data ? (
 								<tr>
-									<td>{moment(banding.createAt).format("DD MMMM YYYY")}</td>
+									<td>{moment(data.createAt).format("DD MMMM YYYY")}</td>
 									<td>
-										{banding.files.map((e) => (
+										{data.dokumen.map((e) => (
 											<>
 												<em className="fa-lg far fa-file-code"></em>
-												<a className="text-muted" href={API_URL + e.path} target="_blank" download={e.name}>
-													{e.name}
+												<a className="text-muted" href={e.path} target="_blank" download={e.judul}>
+													{e.judul}
 												</a>
 											</>
 										))}

+ 6 - 6
components/PT/JawabanKeberatan/TableSanksiJawaban.js

@@ -19,23 +19,23 @@ function TableSanksi({ listData, to, linkName }) {
 						{listData.map((data) => {
 							return (
 								<tr key={data._id}>
-									<td>{data.sanksi.no_sanksi}</td>
+									<td>{data.no_sanksi}</td>
 									<td className="text-nowrap">
 										<div className="media align-items-center">
 											{/* <img className="img-fluid rounded thumb64" src="/static/img/dummy-search.png" alt="Dummy" /> */}
 											<div className="media-body d-flex">
 												<div>
-													<h4 className="m-0">{data.pt.nama}</h4>
+													<h4 className="m-0">{data.laporan.pt.nama}</h4>
 													{/* <small className="text-muted">0742/O/1990 - www.satyagama.ac.id - info@satyagama.ac.id</small> */}
-													<p>{data.sanksi.description.length > 70 ? data.sanksi.description.substring(0, 70) + "..." : data.sanksi.description}</p>
+													<p>{data.keterangan.length > 70 ? data.keterangan.substring(0, 70) + "..." : data.keterangan}</p>
 												</div>
 											</div>
 										</div>
 									</td>
-									<td>{moment(data.sanksi.createdAt).format("DD MMMM YYYY")}</td>
+									<td>{moment(data.createdAt).format("DD MMMM YYYY")}</td>
 									<td>
-										{data.sanksi.keberatan?.jawaban ? (
-											<Link href={{ pathname: to, query: { noSanksi: data.sanksi.no_sanksi } }}>
+										{data.jawaban?.keberatan ? (
+											<Link href={{ pathname: to, query: { id: data._id } }}>
 												<Button color="primary">{linkName}</Button>
 											</Link>
 										) : (

+ 6 - 6
components/PT/JawabanPencabutanSanksi/TableSanksiJawaban.js

@@ -19,21 +19,21 @@ function TableSanksi({ listData, to, linkName }) {
 						{listData.map((data) => {
 							return (
 								<tr key={data._id}>
-									<td>{data.sanksi.no_sanksi}</td>
+									<td>{data.no_sanksi}</td>
 									<td className="text-nowrap">
 										<div className="media align-items-center">
 											<div className="media-body d-flex">
 												<div>
-													<h4 className="m-0">{data.pt.nama}</h4>
-													<p>{data.sanksi.description.length > 70 ? data.sanksi.description.substring(0, 70) + "..." : data.sanksi.description}</p>
+													<h4 className="m-0">{data.laporan.pt.nama}</h4>
+													<p>{data.keterangan.length > 70 ? data.keterangan.substring(0, 70) + "..." : data.keterangan}</p>
 												</div>
 											</div>
 										</div>
 									</td>
-									<td>{moment(data.sanksi.createdAt).format("DD MMMM YYYY")}</td>
+									<td>{moment(data.createdAt).format("DD MMMM YYYY")}</td>
 									<td>
-										{data.sanksi.cabut_sanksi?.jawaban ? (
-											<Link href={{ pathname: to, query: { noSanksi: data.sanksi.no_sanksi } }}>
+										{data.jawaban?.cabut_sanksi ? (
+											<Link href={{ pathname: to, query: { id: data._id } }}>
 												<Button color="primary">{linkName}</Button>
 											</Link>
 										) : (

+ 22 - 25
components/PT/Keberatan/ModalPermohonan.js

@@ -4,7 +4,7 @@ import { Row, Col, FormGroup, Button, Modal, ModalHeader, ModalBody, ModalFooter
 import { addKeberatan } from "@/actions/keberatan";
 import { connect } from "react-redux";
 import { notifKeberatan } from "@/actions/notifikasi";
-import {  toast } from "react-toastify";
+import { toast } from "react-toastify";
 
 let Dropzone = null;
 class DropzoneWrapper extends Component {
@@ -26,6 +26,7 @@ export class ModalPermohonan extends Component {
 		this.state = {
 			modal1: false,
 			files: [],
+			error: null,
 		};
 	}
 
@@ -60,6 +61,7 @@ export class ModalPermohonan extends Component {
 	};
 
 	toggleModal1 = () => {
+		this.setState({ error: null });
 		this.props.toggleModal(false);
 		this.setState({
 			modal1: !this.state.modal1,
@@ -68,43 +70,38 @@ export class ModalPermohonan extends Component {
 
 	onSubmit = async (e) => {
 		e.preventDefault();
-		const { user, query, data } = this.props;
-		const { noSanksi } = query;
+		const { user, query, token } = this.props;
+		const { id } = query;
 		const formdata = new FormData();
 		if (this.state.files.length > 0) {
-			this.state.files.forEach((e) => {
-				formdata.append("files", e);
+			this.setState({
+				modal1: !this.state.modal1,
 			});
 
-			const id = toast.loading("Please wait...");
-			const added = await addKeberatan({ noSanksi, ptId: user.peran[0].organisasi.id }, formdata);
-			// formdata.append("keberatan", added.add.sanksi.keberatan._id);
-			// formdata.append("data", added.add.sanksi.keberatan._id);
-			// formdata.append("model", "Keberatan");
-			// await addDocPerbaikan({ noSanksi, ptId: "0BCE4DB7-B207-445D-8D03-0C54B7688252" }, formdata);
-			// console.log(added);
-			if (added) {
-				// console.log({ lembaga: data.sanksi.user.lembaga, pt_name: user.peran[0].organisasi.nama, no_laporan: data.sanksi.no_sanksi });
-				toast.update(id, { render: "All is good", type: "success", isLoading: false, autoClose: true, closeButton: true });
-				await notifKeberatan({ lembaga: data.sanksi.user.lembaga, pt_name: user.peran[0].organisasi.nama, no_sanksi: data.sanksi.no_sanksi });
-				// console.log(notif);
+			this.state.files.forEach((e) => {
+				formdata.append("dokumen", e);
+			});
+			const tostid = toast.loading("Please wait...");
+			const success = await addKeberatan(token, id, formdata);
+			if (!success) {
+				toast.update(tostid, { render: "All is not good", type: "error", isLoading: false, autoClose: true, closeButton: true });
+			} else {
+				toast.update(tostid, { render: "All is good", type: "success", isLoading: false, autoClose: true, closeButton: true });
 				Router.push({
-					pathname: "/app/pt/keberatan",
+					pathname: "/pt/jawaban-keberatan",
 				});
 			}
+		} else {
+			this.setState({ error: "Dokumen harus ada" });
 		}
 	};
 
 	handleKirim = (e) => {
-		this.setState({
-			modal1: !this.state.modal1,
-		});
 		this.onSubmit(e);
 	};
 
 	render() {
-		const { files } = this.state;
-		// console.log(this.props.data.sanksi);
+		const { files, error } = this.state;
 
 		const thumbs = files.map((file, index) => (
 			<Col md={3} key={index}>
@@ -148,7 +145,7 @@ export class ModalPermohonan extends Component {
 											);
 										}}
 									</DropzoneWrapper>
-									<span className="form-text">Multiple files upload</span>
+									<span className={`form-text ${error ? "text-danger" : ""}`}>{error ? error : "Multiple files upload"}</span>
 								</div>
 							</FormGroup>
 						</form>
@@ -164,5 +161,5 @@ export class ModalPermohonan extends Component {
 	}
 }
 
-const mapStateToProps = (state) => ({ user: state.user });
+const mapStateToProps = (state) => ({ user: state.user, token: state.token });
 export default connect(mapStateToProps)(ModalPermohonan);

+ 5 - 7
components/PT/Keberatan/Riwayat.js

@@ -1,10 +1,8 @@
 import Datatable from "@/components/Tables/Datatable";
 import moment from "moment";
 import { Card, CardHeader, CardBody, CardTitle } from "reactstrap";
-import { API_URL } from "@/env";
 
 function Riwayat({ data }) {
-	const keberatan = data.sanksi.keberatan;
 	return (
 		<Card className="card-default">
 			<CardHeader>
@@ -20,15 +18,15 @@ function Riwayat({ data }) {
 							</tr>
 						</thead>
 						<tbody>
-							{keberatan ? (
+							{data ? (
 								<tr>
-									<td>{moment(keberatan.createAt).format("DD MMMM YYYY")}</td>
+									<td>{moment(data.createAt).format("DD MMMM YYYY")}</td>
 									<td>
-										{keberatan.files.map((e) => (
+										{data.dokumen.map((e) => (
 											<>
 												<em className="fa-lg far fa-file-code"></em>
-												<a className="text-muted" href={API_URL + e.path} target="_blank" download={e.name}>
-													{e.name}
+												<a className="text-muted" href={e.path} target="_blank" download={e.judul}>
+													{e.judul}
 												</a>
 											</>
 										))}

+ 17 - 33
components/PT/Riwayat.js

@@ -18,39 +18,23 @@ function Riwayat({ data }) {
 							</tr>
 						</thead>
 						<tbody>
-							{data.length ? (
-								data.map((value) => (
-									<tr>
-										<td>{moment(value.createAt).format("DD MMMM YYYY")}</td>
-										<td>
-											{value.files.map((e) => (
-												<>
-													<em className="fa-lg far fa-file-code"></em>
-													<a className="text-muted" href={`data:${e.type};base64, ${Buffer.from(e.data).toString("base64")}`} download={e.name}>
-														{e.name}
-													</a>
-												</>
-											))}
-										</td>
-									</tr>
-								))
-							) : data ? (
-								<tr>
-									<td>{moment(data.createAt).format("DD MMMM YYYY")}</td>
-									<td>
-										{data.files.map((e) => (
-											<>
-												<em className="fa-lg far fa-file-code"></em>
-												<a className="text-muted" href={`data:${e.type};base64, ${Buffer.from(e.data).toString("base64")}`} download={e.name}>
-													{e.name}
-												</a>
-											</>
-										))}
-									</td>
-								</tr>
-							) : (
-								""
-							)}
+							{data.length
+								? data.map((value) => (
+										<tr>
+											<td>{moment(value.createAt).format("DD MMMM YYYY")}</td>
+											<td>
+												{value.dokumen.map((e) => (
+													<>
+														<em className="fa-lg far fa-file-code"></em>
+														<a className="text-muted" href={data.path} download={e.judul}>
+															{e.judul}
+														</a>
+													</>
+												))}
+											</td>
+										</tr>
+								  ))
+								: ""}
 						</tbody>
 					</table>
 				</Datatable>

+ 5 - 5
components/PT/TableSanksi.js

@@ -19,20 +19,20 @@ function TableSanksi({ listData, to, linkName }) {
 						{listData.map((data) => {
 							return (
 								<tr key={data._id}>
-									<td>{data.sanksi.no_sanksi}</td>
+									<td>{data.no_sanksi}</td>
 									<td className="text-nowrap">
 										<div className="media align-items-center">
 											<div className="media-body d-flex">
 												<div>
-													<h4 className="m-0">{data.pt.nama}</h4>
-													<p>{data.sanksi.description.length > 70 ? data.sanksi.description.substring(0, 70) + "..." : data.sanksi.description}</p>
+													<h4 className="m-0">{data.laporan.pt.nama}</h4>
+													<p>{data.keterangan.length > 70 ? data.sanksi.description.substring(0, 70) + "..." : data.keterangan}</p>
 												</div>
 											</div>
 										</div>
 									</td>
-									<td>{moment(data.sanksi.createdAt).format("DD MMMM YYYY")}</td>
+									<td>{moment(data.createdAt).format("DD MMMM YYYY")}</td>
 									<td>
-										<Link href={{ pathname: to, query: { noSanksi: data.sanksi.no_sanksi } }}>
+										<Link href={{ pathname: to, query: { id: data._id } }}>
 											<Button color="primary">{linkName}</Button>
 										</Link>
 									</td>

+ 3 - 3
components/PT/TableSanksiJawaban.js

@@ -19,13 +19,13 @@ function TableSanksi({ listData, to, linkName }) {
 						{listData.map((data) => {
 							return (
 								<tr key={data._id}>
-									<td>{data.sanksi.no_sanksi}</td>
+									<td>{data.no_sanksi}</td>
 									<td className="text-nowrap">
 										<div className="media align-items-center">
 											<div className="media-body d-flex">
 												<div>
-													<h4 className="m-0">{data.pt.nama}</h4>
-													<p>{data.sanksi.description}</p>
+													<h4 className="m-0">{data.laporan.pt.nama}</h4>
+													<p>{data.keterangan}</p>
 												</div>
 											</div>
 										</div>

+ 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("DDMM") + "" + Math.floor(Math.random() * 1000000),
 			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);

+ 98 - 65
components/Pemeriksaan/InputEvaluasi.js

@@ -5,8 +5,17 @@ import Datetime from "react-datetime";
 import moment from "moment";
 import { Row, Col, FormGroup, Input } from "reactstrap";
 import { ToastContainer, toast } from "react-toastify";
+import { Formik, Form, Field, ErrorMessage } from "formik";
+import * as Yup from "yup";
 
 const selectInstanceId = 1;
+
+const evaluasiSchema = Yup.object().shape({
+	tanggal: Yup.date().required("Required"),
+	judul: Yup.string().min(3).max(150).required("Required"),
+	dokumen: Yup.array().min(1).required("Required"),
+});
+
 let Dropzone = null;
 
 class DropzoneWrapper extends Component {
@@ -84,25 +93,23 @@ export default class InputEvaluasi extends Component {
 		});
 	};
 
-	onSubmit = async (e) => {
-		e.preventDefault();
-		const { number, ptId } = this.props.query;
+	onSubmit = async (data, { resetForm }) => {
+		const { token, query } = this.props;
+		const { id } = query;
 		const formdata = new FormData();
-		formdata.append("title", this.state.judulEvaluasi);
-		formdata.append("date", this.state.tanggal);
-		if (this.state.files.length > 0) {
-			this.state.files.forEach((e) => {
-				formdata.append("files", e);
-			});
-		}
-		const id = toast.loading("Please wait...");
-		const inserted = await insertPemeriksaan({ number, ptId }, formdata);
-		if (inserted) {
-			toast.update(id, { render: "All is good", type: "success", isLoading: false, autoClose: true, closeButton: true });
-			Router.push({
-				pathname: "/app/pemeriksaan",
-			});
-		}
+		formdata.append("judul", data.judul);
+		formdata.append("tanggal", data.tanggal);
+		data.dokumen.forEach((e) => {
+			formdata.append("dokumen", e);
+		});
+
+		await toast.promise(insertPemeriksaan(token, id, formdata), {
+			pending: "Loading",
+			success: "Success",
+			error: "Error",
+		});
+		this.setState({ files: [] });
+		resetForm();
 	};
 
 	render() {
@@ -116,53 +123,79 @@ export default class InputEvaluasi extends Component {
 		return (
 			<>
 				<p className="lead bb">Evaluasi</p>
-				<form className="form-horizontal" method="get" action="/" onSubmit={this.onSubmit}>
-					<FormGroup>
-						<label>Tanggal Dokumen:</label>
-						<div>
-							<Datetime inputProps={{ className: "form-control" }} value={this.state.tanggal} onChange={this.setTanggal} />
-							{/* <span className="form-text">Tanggal</span> */}
-						</div>
-					</FormGroup>
-					<FormGroup>
-						<label>Judul Dokumen:</label>
-						<div>
-							<Input type="text" value={this.state.judulEvaluasi} onChange={this.setjudulEvaluasi} />
-							{/* <Input type="textarea" value={this.state.keteranganLaporan} onChange={this.setKeteranganPelaporan} /> */}
-							{/* <span className="form-text">Deskripsi pelaporan minimum karakter 50 maksimum 200 karakter</span> */}
-						</div>
-					</FormGroup>
-					<FormGroup>
-						<label>Upload File Pendukung:</label>
-						<div>
-							<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>
-							<span className="form-text">Multiple files upload</span>
-						</div>
-					</FormGroup>
-					<FormGroup>
-						<div>
-							<button className="btn btn-sm btn-primary" type="submit">
-								Simpan Evaluasi
-							</button>
-						</div>
-					</FormGroup>
-				</form>
+				<Formik
+					initialValues={{
+						tanggal: new Date(),
+						judul: "",
+						dokumen: [],
+					}}
+					validationSchema={evaluasiSchema}
+					onSubmit={this.onSubmit}
+				>
+					<Form className="form-horizontal">
+						<FormGroup>
+							<label className="col-form-label">Tanggal Dokumen</label>
+							<Field name="tanggal">
+								{({ field, form }) => (
+									<Datetime
+										timeFormat={false}
+										inputProps={{ className: "form-control" }}
+										value={field.value}
+										onChange={(e) => {
+											form.setFieldValue(field.name, e);
+										}}
+									/>
+								)}
+							</Field>
+							<ErrorMessage name="tanggal" component="div" className="form-text text-danger" />
+						</FormGroup>
+						<FormGroup>
+							<label className="col-form-label">Judul Dokumen</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 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">
+									Simpan Evaluasi
+								</button>
+							</div>
+						</FormGroup>
+					</Form>
+				</Formik>
 			</>
 		);
 	}

+ 67 - 0
components/Pemeriksaan/TableLaporan.js

@@ -0,0 +1,67 @@
+import Datatable from "@/components/Tables/Datatable";
+import { Button } from "reactstrap";
+import Link from "next/link";
+import moment from "moment";
+
+function TableLaporan({ listData, to, linkName }) {
+	return (
+		<div className="card b">
+			<div className="card-body">
+				{listData && (
+					<Datatable options={{ responsive: false }}>
+						<table className="table w-100">
+							<thead>
+								<tr>
+									<th>No.Laporan</th>
+									<th>Deskripsi Laporan</th>
+									<th>Status</th>
+									<th>Created</th>
+									<th></th>
+								</tr>
+							</thead>
+							<tbody>
+								{listData.map((data) => {
+									return (
+										<tr key={data._id}>
+											<td>{data.no_laporan}</td>
+											<td className="text-nowrap">
+												<div className="media align-items-center">
+													<div className="media-body d-flex">
+														<div>
+															<h4 className="m-0">{data.pt.nama}</h4>
+															<p>{data.keterangan.length > 35 ? data.keterangan.substring(0, 35) + "..." : data.keterangan}</p>
+														</div>
+													</div>
+												</div>
+											</td>
+
+											<td>{data.evaluasi?.length ? <div className="badge badge-info">Sudah diperiksa</div> : <div className="badge badge-danger">Belum diperiksa</div>}</td>
+
+											<td>{moment(data.createdAt).fromNow()}</td>
+											<td>
+												<div className="ml-auto">
+													<Link
+														href={{
+															pathname: to,
+															query: { id: data._id },
+														}}
+													>
+														<Button color="primary" size="sm">
+															{linkName}
+														</Button>
+													</Link>
+												</div>
+											</td>
+										</tr>
+									);
+								})}
+							</tbody>
+						</table>
+					</Datatable>
+				)}
+			</div>
+		</div>
+	);
+}
+
+export default TableLaporan;

+ 8 - 10
components/Pemeriksaan/TableRiwayat.js

@@ -1,8 +1,6 @@
 import Datatable from "@/components/Tables/Datatable";
-import { API_URL } from "@/env";
 
 function TableRiwayat({ data }) {
-	console.log(data);
 	return (
 		<Datatable options={{ responsive: true }}>
 			<table className="table table-striped my-4 w-100">
@@ -15,17 +13,17 @@ function TableRiwayat({ data }) {
 					</tr>
 				</thead>
 				<tbody>
-					{data.penjadwalan.pemeriksaan.map((e, index) => (
-						<tr key={`riwayatPemeriksaan-${index}`}>
+					{data.evaluasi.map((e, index) => (
+						<tr key={index}>
 							<td>{moment(e.createdAt).format("D MMMM YYYY")}</td>
-							<td>{moment(e.date).format("D MMMM YYYY")}</td>
-							<td>{e.title}</td>
+							<td>{moment(e.tanggal).format("D MMMM YYYY")}</td>
+							<td>{e.judul}</td>
 							<td>
-								{e.files.map((e, index) => (
+								{e.dokumen.map((e, index) => (
 									<>
-										<em className="fa-lg far fa-file-code"></em>
-										<a className="text-muted" href={API_URL + e.path} target="_blank" download={e.name}>
-											{e.name}
+										<em key="index" className="fa-lg far fa-file-code"></em>
+										<a className="text-muted" href={e.path} target="_blank" download={e.judul}>
+											{e.judul}
 										</a>
 										<br />
 									</>

+ 7 - 8
components/PencabutanSanksi/Riwayat.js

@@ -4,7 +4,6 @@ import { Card, CardHeader, CardBody, CardTitle } from "reactstrap";
 import { API_URL } from "@/env";
 
 function Riwayat({ data }) {
-	const { jawaban } = data.sanksi.cabut_sanksi;
 	return (
 		<Card className="card-default">
 			<CardHeader>
@@ -22,17 +21,17 @@ function Riwayat({ data }) {
 							</tr>
 						</thead>
 						<tbody>
-							{jawaban ? (
+							{data ? (
 								<tr>
-									<td>{moment(jawaban.createAt).format("DD MMMM YYYY")}</td>
-									<td>{jawaban.status}</td>
-									<td>{jawaban.description}</td>
+									<td>{moment(data.createAt).format("DD MMMM YYYY")}</td>
+									<td>{data.status}</td>
+									<td>{data.keterangan}</td>
 									<td>
-										{jawaban.files.map((e) => (
+										{data.dokumen.map((e) => (
 											<>
 												<em className="fa-lg far fa-file-code"></em>
-												<a className="text-muted" href={API_URL + e.path} target="_blank" download={e.name}>
-													{e.name}
+												<a className="text-muted" href={e.path} target="_blank" download={e.judul}>
+													{e.judul}
 												</a>
 											</>
 										))}

+ 6 - 6
components/PencabutanSanksi/TableSanksi.js

@@ -23,25 +23,25 @@ function TableSanksi({ listData, to, linkName }) {
 								? listData.map((data) => {
 										return (
 											<tr key={data._id}>
-												<td>{data.sanksi.no_sanksi}</td>
+												<td>{data.no_sanksi}</td>
 												<td>
 													<div className="media align-items-center">
 														<div className="media-body d-flex">
 															<div>
-																<h4 className="m-0">{data.pt.nama}</h4>
-																<p>{data.description.length > 25 ? data.description.substring(0, 25) + "..." : data.description}</p>
+																<h4 className="m-0">{data.laporan.pt.nama}</h4>
+																<p>{data.keterangan.length > 25 ? data.keterangan.substring(0, 25) + "..." : data.keterangan}</p>
 															</div>
 														</div>
 													</div>
 												</td>
-												<td>{moment(data.sanksi.createdAt).fromNow()}</td>
-												<td>{data.sanksi.cabut_sanksi.jawaban ? <div className="badge badge-info">Sudah Dijawab</div> : <div className="badge badge-danger">Belum Dijawab</div>}</td>
+												<td>{moment(data.createdAt).fromNow()}</td>
+												<td>{data.jawaban?.cabut_sanksi ? <div className="badge badge-info">Sudah Dijawab</div> : <div className="badge badge-danger">Belum Dijawab</div>}</td>
 												<td>
 													<div className="ml-auto">
 														<Link
 															href={{
 																pathname: to,
-																query: { noSanksi: data.sanksi.no_sanksi, ptId: data.pt_id },
+																query: { id: data._id },
 															}}
 														>
 															<Button color="primary" size="sm">

+ 67 - 0
components/Penjadwalan/TableLaporan.js

@@ -0,0 +1,67 @@
+import Datatable from "@/components/Tables/Datatable";
+import { Button } from "reactstrap";
+import Link from "next/link";
+import moment from "moment";
+
+function TableLaporan({ listData, to, linkName }) {
+	return (
+		<div className="card b">
+			<div className="card-body">
+				{listData && (
+					<Datatable options={{ responsive: false }}>
+						<table className="table w-100">
+							<thead>
+								<tr>
+									<th>No.Laporan</th>
+									<th>Deskripsi Laporan</th>
+									<th>Status</th>
+									<th>Created</th>
+									<th></th>
+								</tr>
+							</thead>
+							<tbody>
+								{listData.map((data) => {
+									return (
+										<tr key={data._id}>
+											<td>{data.no_laporan}</td>
+											<td className="text-nowrap">
+												<div className="media align-items-center">
+													<div className="media-body d-flex">
+														<div>
+															<h4 className="m-0">{data.pt.nama}</h4>
+															<p>{data.keterangan.length > 35 ? data.keterangan.substring(0, 35) + "..." : data.keterangan}</p>
+														</div>
+													</div>
+												</div>
+											</td>
+
+											<td>{data.jadwal ? <div className="badge badge-info">Ada Jadwal</div> : <div className="badge badge-danger">Tidak ada jadwal</div>}</td>
+
+											<td>{moment(data.createdAt).fromNow()}</td>
+											<td>
+												<div className="ml-auto">
+													<Link
+														href={{
+															pathname: to,
+															query: { id: data._id },
+														}}
+													>
+														<Button color="primary" size="sm">
+															{linkName}
+														</Button>
+													</Link>
+												</div>
+											</td>
+										</tr>
+									);
+								})}
+							</tbody>
+						</table>
+					</Datatable>
+				)}
+			</div>
+		</div>
+	);
+}
+
+export default TableLaporan;

+ 11 - 15
components/Public/DetailLaporan.js

@@ -36,7 +36,7 @@ function DetailLaporan({ data }) {
 					<Col md="4">Keterangan Laporan:</Col>
 					<Col md="8">
 						<Scrollable height="100px" className="list-group">
-							<p>{data.description}</p>
+							<p>{data.keterangan}</p>
 						</Scrollable>
 					</Col>
 				</FormGroup>
@@ -46,30 +46,26 @@ function DetailLaporan({ data }) {
 						<strong>{moment(data.createdAt).format("D MMMM YYYY")}</strong>
 					</Col>
 				</FormGroup>
-				{data.status ? (
-					<FormGroup row>
-						<Col md="4">Status:</Col>
-						<Col md="8">
-							<div className="badge badge-info">{data.status}</div>
-						</Col>
-					</FormGroup>
-				) : (
-					""
-				)}
+				<FormGroup row>
+					<Col md="4">Status:</Col>
+					<Col md="8">
+						<div className="badge badge-info">{!data.aktif ? "Laporan ditutup" : data.role_data === "dikti" ? "Ditindaklanjuti DIKTI" : "Ditindaklanjuti LLDIKTI"}</div>
+					</Col>
+				</FormGroup>
 				<FormGroup row>
 					<Col md="4">File Pendukung:</Col>
 					<Col md="8">
 						<Scrollable height="120px" className="list-group">
 							<table className="table table-bordered bg-transparent">
 								<tbody>
-									{data.files.map((e, index) => (
-										<tr key={`files-${index}`}>
+									{data.dokumen.map((e, index) => (
+										<tr key={index}>
 											<td>
 												<em className="fa-lg far fa-file-code"></em>
 											</td>
 											<td>
-												<a className="text-muted" href={API_URL + e.path} target="_blank" download={e.name}>
-													{e.name}
+												<a className="text-muted" href={e.path} target="_blank" download={e.judul}>
+													{e.judul}
 												</a>
 											</td>
 										</tr>

+ 5 - 5
components/Sanksi/TableLaporan.js

@@ -12,7 +12,7 @@ function TableLaporan({ listData }) {
 						<table className="table w-100">
 							<thead>
 								<tr>
-									<th>#ID</th>
+									<th>No.Laporan</th>
 									<th>Deskripsi Laporan</th>
 									<th>Status</th>
 									<th>Created</th>
@@ -23,19 +23,19 @@ function TableLaporan({ listData }) {
 								{listData.map((data) => {
 									return (
 										<tr key={data._id}>
-											<td>{data._number}</td>
+											<td>{data.no_laporan}</td>
 											<td className="text-nowrap">
 												<div className="media align-items-center">
 													<div className="media-body d-flex">
 														<div>
 															<h4 className="m-0">{data.pt.nama}</h4>
-															<p>{data.description.length > 35 ? data.description.substring(0, 35) + "..." : data.description}</p>
+															<p>{data.keterangan.length > 35 ? data.keterangan.substring(0, 35) + "..." : data.keterangan}</p>
 														</div>
 													</div>
 												</div>
 											</td>
 											<td>
-												<div className="badge badge-info">{data.status}</div>
+												<td>{data.sanksi ? <div className="badge badge-info">Sudah ditetapkan</div> : <div className="badge badge-danger">Belum ditetapkan</div>}</td>
 											</td>
 											<td>{moment(data.createdAt).fromNow()}</td>
 											<td>
@@ -43,7 +43,7 @@ function TableLaporan({ listData }) {
 													<Link
 														href={{
 															pathname: data.sanksi ? "/app/sanksi/detail" : "/app/sanksi/proses",
-															query: { ptId: data.pt_id, number: data._number },
+															query: { id: data.sanksi || data._id },
 														}}
 													>
 														<Button color="primary" size="sm">

+ 4 - 5
components/Sanksi/TablePenetapanSanksi.js

@@ -10,14 +10,13 @@ export class TablePenetapanSanksi extends Component {
 		super(props);
 		this.state = {
 			pelanggaran: null,
-			// checkedData: [],
+			checkedData: [],
 		};
 	}
 
 	componentDidMount = async () => {
-		const { user } = this.props;
-		const pelanggaran = await getPelanggaran();
-		if (user.peran[0].peran.id === 2021) pelanggaran.data = pelanggaran.data.filter((e) => e.level_sanksi === 1);
+		const { token } = this.props;
+		const pelanggaran = await getPelanggaran(token);
 		this.setState({ pelanggaran });
 	};
 
@@ -88,5 +87,5 @@ export class TablePenetapanSanksi extends Component {
 	}
 }
 
-const mapStateToProps = (state) => ({ user: state.user });
+const mapStateToProps = (state) => ({ user: state.user, token: state.token });
 export default connect(mapStateToProps)(TablePenetapanSanksi);

+ 1 - 35
config/axios.js

@@ -1,43 +1,9 @@
 import axios from "axios";
-// import jwt_decode from "jwt-decode";
-// require("dotenv").config();
 import { API_URL } from "@/env";
 
 const axiosAPI = axios.create({
-	baseURL: API_URL || "http://localhost:5000",
+	baseURL: API_URL || "http://localhost:5000/v1",
 	withCredentials: true,
 });
 
-// axiosJWT.interceptors.request.use(
-// 	async (config) => {
-// 		// const response = await refreshToken();
-// 		// const decoded = jwt_decode(response.access_token);
-// 		// const expire = decoded.exp;
-// 		// const currentDate = Date.now();
-// 		// if (expire * 1000 < currentDate) {
-// 		const response = await refreshToken();
-// 		// console.log(response);
-// 		if (response.success) config.headers.Authorization = `Bearer ${response.access_token}`;
-// 		// config.withCredentials = true;
-// 		return config;
-// 		// }
-// 	},
-// 	async (error) => {
-// 		const originalConfig = err.config;
-// 		// if (error.response.status === 403 || error.response.status === 401) {
-// 		try {
-// 			const response = await refreshToken();
-// 			if (response.success) {
-// 				// axiosJWT.defaults.headers.Authorization = `Bearer ${response.access_token}`;
-// 				originalConfig.headers.Authorization = `Bearer ${response.access_token}`;
-// 				return axiosJWT(originalConfig);
-// 			}
-// 		} catch (error) {
-// 			return Promise.reject(error);
-// 		}
-// 		// }
-// 		return Promise.reject(error);
-// 	}
-// );
-
 export default axiosAPI;

+ 109 - 19
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",
@@ -3602,6 +3612,11 @@
 				}
 			}
 		},
+		"clsx": {
+			"version": "1.1.1",
+			"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz",
+			"integrity": "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA=="
+		},
 		"code-point-at": {
 			"version": "1.1.0",
 			"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
@@ -4240,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",
@@ -4440,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",
@@ -5263,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",
@@ -6410,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",
@@ -7011,6 +7059,14 @@
 			"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
 			"integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ=="
 		},
+		"moment-timezone": {
+			"version": "0.5.34",
+			"resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.34.tgz",
+			"integrity": "sha512-3zAEHh2hKUs3EXLESx/wsgw6IQdusOT8Bxm3D9UrHPQR7zlMmzwybC8zHEM1tQ4LJwP7fcxrWr8tuBg05fFCbg==",
+			"requires": {
+				"moment": ">= 2.9.0"
+			}
+		},
 		"morris.js.so": {
 			"version": "0.5.1",
 			"resolved": "https://registry.npmjs.org/morris.js.so/-/morris.js.so-0.5.1.tgz",
@@ -7049,6 +7105,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",
@@ -8238,6 +8299,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",
@@ -8680,6 +8746,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",
@@ -8828,26 +8899,11 @@
 			}
 		},
 		"react-toastify": {
-			"version": "4.5.2",
-			"resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-4.5.2.tgz",
-			"integrity": "sha512-KymDDhkcX5EvFht17nO0MCsegM/Kdhyfxhi+WQl2tE3IxJrueOhY6TUnALTfvz7eDRUjPYBGb+ywWqWrGyvBnw==",
+			"version": "8.2.0",
+			"resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-8.2.0.tgz",
+			"integrity": "sha512-Pg2Ju7NngAamarFvLwqrFomJ57u/Ay6i6zfLurt/qPynWkAkOthu6vxfqYpJCyNhHRhR4hu7+bySSeWWJu6PAg==",
 			"requires": {
-				"classnames": "^2.2.6",
-				"prop-types": "^15.6.0",
-				"react-transition-group": "^2.4.0"
-			},
-			"dependencies": {
-				"react-transition-group": {
-					"version": "2.9.0",
-					"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz",
-					"integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==",
-					"requires": {
-						"dom-helpers": "^3.4.0",
-						"loose-envify": "^1.4.0",
-						"prop-types": "^15.6.2",
-						"react-lifecycles-compat": "^3.0.4"
-					}
-				}
+				"clsx": "^1.1.1"
 			}
 		},
 		"react-transition-group": {
@@ -10512,6 +10568,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",
@@ -11444,6 +11505,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=="
+				}
+			}
 		}
 	}
 }

+ 5 - 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",
@@ -64,6 +66,7 @@
 		"loaders.css": "0.1.2",
 		"matchmedia": "0.1.2",
 		"moment": "^2.29.1",
+		"moment-timezone": "^0.5.34",
 		"morris.js.so": "0.5.1",
 		"nestable": "themicon/nestable",
 		"next": "9.5.5",
@@ -103,7 +106,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",

+ 119 - 78
pages/app/banding/detail.js

@@ -7,13 +7,16 @@ import Header from "@/components/Main/Header";
 import DetailPT from "@/components/Main/DetailPT";
 import PermohonanPT from "@/components/Main/PermohonanPT";
 import Riwayat from "@/components/Banding/Riwayat";
-import { getSanksi } from "@/actions/sanksi";
+import { getOneSanksi } from "@/actions/sanksi";
 import { addJawabanBanding } from "@/actions/banding";
 import ContentWrapper from "@/components/Layout/ContentWrapper";
 import { Row, Col, Card, CardBody, FormGroup, Button, Modal, ModalHeader, ModalBody, ModalFooter } from "reactstrap";
 import { getPT } from "@/actions/PT";
 import Loader from "@/components/Common/Loader";
 import { toast } from "react-toastify";
+import { connect } from "react-redux";
+import { Formik, Form, Field, ErrorMessage } from "formik";
+import * as Yup from "yup";
 
 let Dropzone = null;
 class DropzoneWrapper extends Component {
@@ -30,7 +33,10 @@ class DropzoneWrapper extends Component {
 }
 
 const selectInstanceId = 1;
-
+const jawabanBandingSchema = Yup.object().shape({
+	status: Yup.string().required("Harap Diisi"),
+	dokumen: Yup.array().min(1).required(),
+});
 class JawabanBanding extends Component {
 	constructor(props) {
 		super(props);
@@ -39,7 +45,7 @@ class JawabanBanding extends Component {
 			selectedOption: null,
 			files: [],
 			sanksi: {},
-			pt: {},
+			pt: null,
 		};
 	}
 
@@ -48,10 +54,10 @@ class JawabanBanding extends Component {
 	};
 
 	componentDidMount = async () => {
-		const { query } = this.props;
-		const sanksi = await getSanksi(query);
-		const pt = await getPT({ id: query.ptId });
-		this.setState({ pt, sanksi });
+		const { query, token } = this.props;
+		const sanksi = await getOneSanksi(token, query.id, { banding: true });
+		const pt = sanksi.data.laporan.pt;
+		this.setState({ sanksi, pt });
 	};
 
 	toggleModal = () => {
@@ -94,22 +100,24 @@ class JawabanBanding extends Component {
 		});
 	};
 
-	handelSimpan = async (e) => {
-		e.preventDefault();
-		const { selectedOption } = this.state;
-		const { noSanksi, ptId } = this.props.query;
-		const formdata = new FormData();
-		formdata.append("status", selectedOption.value);
-		if (this.state.files.length > 0) {
-			this.state.files.forEach((e) => {
-				formdata.append("files", e);
-			});
+	handelSimpan = async () => {
+		if (this.state.modal === true) {
+			this.toggleModal();
 		}
-
-		const id = toast.loading("Please wait...");
-		const added = await addJawabanBanding({ noSanksi, ptId }, formdata);
-		if (added) {
-			toast.update(id, { render: "All is good", type: "success", isLoading: false, autoClose: true, closeButton: true });
+		const { data } = this.state;
+		const { query, token } = this.props;
+		const { id } = query;
+		const formdata = new FormData();
+		formdata.append("status", data.status);
+		data.dokumen.forEach((e) => {
+			formdata.append("dokumen", e);
+		});
+		const toastid = toast.loading("Please wait...");
+		const added = await addJawabanBanding(token, id, formdata);
+		if (!added) {
+			toast.update(toastid, { render: "All is not good", type: "error", isLoading: false, autoClose: true, closeButton: true });
+		} else {
+			toast.update(toastid, { render: "All is good", type: "success", isLoading: false, autoClose: true, closeButton: true });
 			Router.push({
 				pathname: "/app/banding",
 			});
@@ -138,64 +146,96 @@ class JawabanBanding extends Component {
 						</div>
 					</div>
 					<Row>
-						{sanksi.data && sanksi.data.length ? (
+						{sanksi.data ? (
 							<Col xl={9}>
 								<Card className="card-default">
 									<CardBody>
 										<Row>
 											<Col lg={12}>
-												<DetailSanksi data={sanksi.data[0]} />
-												<PermohonanPT data={sanksi.data[0].sanksi.banding} title="Permohonan Banding" />
+												<DetailSanksi data={sanksi.data} />
+												<PermohonanPT data={sanksi.data.pengajuan.banding} title="Permohonan Banding" />
 												<p className="lead bb">Jawaban</p>
-												<form className="form-horizontal" method="get" action="/" onSubmit={this.onSubmit}>
-													<FormGroup>
-														<label className="row-form-label">Status:</label>
-														<div className="row-md-10">
-															<Select
-																instanceId={selectInstanceId + 1}
-																value={this.state.selectedOption}
-																onChange={this.handleChangeSelect}
-																options={[
-																	{ value: "Ditolak", label: "Ditolak", className: "State-ACT" },
-																	{ value: "Dikabulkan", label: "Dikabulkan", className: "State-ACT" },
-																]}
-																required
-															/>
-															{/* <span className="form-text">Pilih Jenis Pelanggaran</span> */}
-														</div>
-													</FormGroup>
-													<FormGroup>
-														<label className="row-form-label">Dokumen Jawaban:</label>
-														<div className="row-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>
-														<div className="row-xl-10">
-															<Button color="primary" onClick={sanksi.data && sanksi.data.length && sanksi.data[0].sanksi.banding.jawaban ? this.toggleModal : this.handelSimpan}>
-																Simpan
-															</Button>
-														</div>
-													</FormGroup>
-												</form>
+												<Formik
+													initialValues={{
+														status: "",
+														dokumen: [],
+													}}
+													validationSchema={jawabanBandingSchema}
+													onSubmit={async (data) => {
+														this.setState({ data });
+														if (sanksi.data.jawaban?.banding) this.toggleModal();
+														else await this.handelSimpan();
+													}}
+												>
+													{() => (
+														<Form className="form-horizontal">
+															<FormGroup>
+																<label className="row-form-label">Status:</label>
+																<div className="row-md-10">
+																	<Field name="status">
+																		{({ field, form, meta }) => (
+																			<Select
+																				instanceId={selectInstanceId + 1}
+																				value={this.state.selectedOption}
+																				onChange={(e) => {
+																					this.handleChangeSelect(e);
+																					form.setFieldValue(field.name, e.value);
+																				}}
+																				options={[
+																					{ value: "Ditolak", label: "Ditolak", className: "State-ACT" },
+																					{ value: "Dikabulkan", label: "Dikabulkan", className: "State-ACT" },
+																				]}
+																			/>
+																		)}
+																	</Field>
+																	<ErrorMessage name="status" component="div" className="form-text text-danger" />
+																</div>
+															</FormGroup>
+															<FormGroup>
+																<label className="row-form-label">Dokumen Jawaban:</label>
+																<div className="row-md-10">
+																	<Field name="dokumen">
+																		{({ field, form }) => (
+																			<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 {...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 color="primary" type="submit">
+																		Simpan
+																	</Button>
+																</div>
+															</FormGroup>
+														</Form>
+													)}
+												</Formik>
 											</Col>
 										</Row>
 									</CardBody>
@@ -204,12 +244,12 @@ class JawabanBanding extends Component {
 						) : (
 							<Loader />
 						)}
-						<Col xl={3}>{pt?.data ? <DetailPT data={pt.data[0]} /> : <Loader />}</Col>
+						<Col xl={3}>{pt ? <DetailPT data={pt} /> : <Loader />}</Col>
 					</Row>
-					{sanksi.data && sanksi.data.length && (
+					{sanksi.data && (
 						<Row>
 							<Col>
-								<Riwayat data={sanksi.data[0].sanksi.banding.jawaban} />
+								<Riwayat data={sanksi.data.jawaban.banding} />
 							</Col>
 						</Row>
 					)}
@@ -230,4 +270,5 @@ class JawabanBanding extends Component {
 	}
 }
 
-export default JawabanBanding;
+const mapStateToProps = (state) => ({ user: state.user, token: state.token });
+export default connect(mapStateToProps)(JawabanBanding);

+ 6 - 3
pages/app/banding/index.js

@@ -5,6 +5,7 @@ import CaseProgress from "@/components/Main/CaseProgress";
 import TableSanksi from "@/components/Banding/TableSanksi";
 import { getSanksi } from "@/actions/sanksi";
 import Loader from "@/components/Common/Loader";
+import { connect } from "react-redux";
 
 class Banding extends Component {
 	constructor(props) {
@@ -15,7 +16,8 @@ class Banding extends Component {
 	}
 
 	componentDidMount = async () => {
-		const sanksi = await getSanksi({ banding: true });
+		const { token } = this.props;
+		const sanksi = await getSanksi(token, { banding: true });
 		this.setState({ sanksi });
 	};
 
@@ -28,11 +30,12 @@ class Banding extends Component {
 					<Col lg="4">
 						<CaseProgress />
 					</Col>
-					<Col lg="8">{sanksi?.data ? <TableSanksi listData={sanksi.data} to="/app/banding/detail" linkName="Detail" /> : <Loader />}</Col>
+					<Col lg="8">{sanksi.data ? <TableSanksi listData={sanksi.data} to="/app/banding/detail" linkName="Detail" /> : <Loader />}</Col>
 				</Row>
 			</ContentWrapper>
 		);
 	}
 }
 
-export default Banding;
+const mapStateToProps = (state) => ({ user: state.user, token: state.token });
+export default connect(mapStateToProps)(Banding);

+ 139 - 86
pages/app/keberatan/detail.js

@@ -7,13 +7,16 @@ import Header from "@/components/Main/Header";
 import DetailPT from "@/components/Main/DetailPT";
 import PermohonanPT from "@/components/Main/PermohonanPT";
 import Riwayat from "@/components/Keberatan/Riwayat";
-import { getSanksi } from "@/actions/sanksi";
+import { getOneSanksi } from "@/actions/sanksi";
 import { addJawabanKeberatan } from "@/actions/keberatan";
 import ContentWrapper from "@/components/Layout/ContentWrapper";
 import { Row, Col, Card, CardBody, FormGroup, Input, Button, Modal, ModalHeader, ModalBody, ModalFooter } from "reactstrap";
 import { getPT } from "@/actions/PT";
 import Loader from "@/components/Common/Loader";
 import { toast } from "react-toastify";
+import { connect } from "react-redux";
+import { Formik, Form, Field, ErrorMessage } from "formik";
+import * as Yup from "yup";
 
 let Dropzone = null;
 class DropzoneWrapper extends Component {
@@ -30,7 +33,11 @@ class DropzoneWrapper extends Component {
 }
 
 const selectInstanceId = 1;
-
+const jawabanKeberatanSchema = Yup.object().shape({
+	status: Yup.string().required("Harap Diisi"),
+	keterangan: Yup.string().min(3).max(200).required("Harap Diisi"),
+	dokumen: Yup.array().notRequired(),
+});
 class DetailKeberatan extends Component {
 	constructor(props) {
 		super(props);
@@ -40,7 +47,8 @@ class DetailKeberatan extends Component {
 			files: [],
 			keterangan: "",
 			sanksi: {},
-			pt: {},
+			pt: null,
+			data: {},
 		};
 	}
 
@@ -49,10 +57,10 @@ class DetailKeberatan extends Component {
 	};
 
 	componentDidMount = async () => {
-		const { query } = this.props;
-		const sanksi = await getSanksi(query);
-		const pt = await getPT({ id: query.ptId });
-		this.setState({ pt, sanksi });
+		const { query, token } = this.props;
+		const sanksi = await getOneSanksi(token, query.id);
+		const pt = sanksi.data.laporan.pt;
+		this.setState({ sanksi, pt });
 	};
 
 	toggleModal = () => {
@@ -95,25 +103,28 @@ class DetailKeberatan extends Component {
 		});
 	};
 
-	handelSimpan = async (e) => {
-		e.preventDefault();
-		const { keterangan, selectedOption } = this.state;
-		const { noSanksi, ptId } = this.props.query;
+	handelSimpan = async () => {
+		if (this.state.modal === true) {
+			this.toggleModal();
+		}
+		const { data } = this.state;
+		const { query, token } = this.props;
+		const { id } = query;
 		const formdata = new FormData();
-		formdata.append("description", keterangan);
-		formdata.append("status", selectedOption.value);
-		if (this.state.files.length > 0) {
-			this.state.files.forEach((e) => {
-				formdata.append("files", e);
+		formdata.append("keterangan", data.keterangan);
+		formdata.append("status", data.status);
+		data.dokumen.forEach((e) => {
+			formdata.append("dokumen", e);
+		});
+		const toastid = toast.loading("Please wait...");
+		const added = await addJawabanKeberatan(token, id, formdata);
+		if (!added) {
+			toast.update(toastid, { render: "All is not good", type: "error", isLoading: false, autoClose: true, closeButton: true });
+		} else {
+			toast.update(toastid, { render: "All is good", type: "success", isLoading: false, autoClose: true, closeButton: true });
+			Router.push({
+				pathname: "/app/keberatan",
 			});
-			const id = toast.loading("Please wait...");
-			const added = await addJawabanKeberatan({ noSanksi, ptId }, formdata);
-			if (added) {
-				toast.update(id, { render: "All is good", type: "success", isLoading: false, autoClose: true, closeButton: true });
-				Router.push({
-					pathname: "/app/keberatan",
-				});
-			}
 		}
 	};
 
@@ -139,80 +150,121 @@ class DetailKeberatan extends Component {
 						</div>
 					</div>
 					<Row>
-						{sanksi.data && sanksi.data.length ? (
+						{sanksi.data ? (
 							<Col xl="9">
 								<Card className="card-default">
 									<CardBody>
 										<Row>
 											<Col lg={12}>
-												<DetailSanksi data={sanksi.data[0]} />
+												<DetailSanksi data={sanksi.data} />
 											</Col>
 										</Row>
 										<Row>
 											<Col lg={12}>
-												<PermohonanPT data={sanksi.data[0].sanksi.keberatan} title="Permohonan Keberatan" />
+												<PermohonanPT data={sanksi.data.pengajuan.keberatan} title="Permohonan Keberatan" />
 											</Col>
 										</Row>
 										<Row>
 											<Col lg={12}>
 												<p className="lead bb">Jawaban</p>
-												<form className="form-horizontal" method="get" action="/" onSubmit={this.onSubmit}>
-													<FormGroup>
-														<label className="row-form-label">Status:</label>
-														<div className="row-md-10">
-															<Select
-																instanceId={selectInstanceId + 1}
-																value={this.state.selectedOption}
-																onChange={this.handleChangeSelect}
-																options={[
-																	{ value: "Menolak", label: "Menolak", className: "State-ACT" },
-																	{ value: "Mengubah Keputusan", label: "Mengubah Keputusan", className: "State-ACT" },
-																	{ value: "Membatalkan Keputusan", label: "Membatalkan Keputusan", className: "State-ACT" },
-																]}
-																required
-															/>
-															{/* <span className="form-text">Pilih Jenis Pelanggaran</span> */}
-														</div>
-													</FormGroup>
-													<FormGroup>
-														<label className="row-form-label">Keterangan Jawaban:</label>
-														<div className="row-md-10">
-															<Input type="textarea" value={this.state.keterangan} onChange={(e) => this.setState({ keterangan: e.target.value })} required />
-															{/* <span className="form-text">Deskripsi pelaporan minimum karakter 50 maksimum 200 karakter</span> */}
-														</div>
-													</FormGroup>
-													<FormGroup>
-														<label className="row-form-label">Dokumen Jawaban:</label>
-														<div className="row-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>
+												<Formik
+													initialValues={{
+														status: "",
+														keterangan: "",
+														dokumen: [],
+													}}
+													validationSchema={jawabanKeberatanSchema}
+													onSubmit={async (data) => {
+														this.setState({ data });
+														if (sanksi.data.jawaban?.keberatan) this.toggleModal();
+														else await this.handelSimpan();
+													}}
+												>
+													{() => (
+														<Form className="form-horizontal">
+															<FormGroup>
+																<label className="row-form-label">Status:</label>
+																<div className="row-md-10">
+																	<Field name="status">
+																		{({ field, form, meta }) => (
+																			<Select
+																				instanceId={selectInstanceId + 1}
+																				value={this.state.selectedOption}
+																				onChange={(e) => {
+																					this.handleChangeSelect(e);
+																					form.setFieldValue(field.name, e.value);
+																				}}
+																				options={[
+																					{ value: "Menolak", label: "Menolak", className: "State-ACT" },
+																					{ value: "Mengubah Keputusan", label: "Mengubah Keputusan", className: "State-ACT" },
+																					{ value: "Membatalkan Keputusan", label: "Membatalkan Keputusan", className: "State-ACT" },
+																				]}
+																			/>
+																		)}
+																	</Field>
+																	<ErrorMessage name="status" component="div" className="form-text text-danger" />
+																</div>
+															</FormGroup>
+															<FormGroup>
+																<label className="row-form-label">Keterangan Jawaban:</label>
+																<div className="row-md-10">
+																	<Field name="keterangan">{({ field }) => <Input type="textarea" {...field} />}</Field>
+																	<ErrorMessage name="keterangan" component="div" className="form-text text-danger" />
+																	{/* <span className="form-text">Deskripsi pelaporan minimum karakter 50 maksimum 200 karakter</span> */}
+																</div>
+															</FormGroup>
+															<FormGroup>
+																<label className="row-form-label">Dokumen Jawaban:</label>
+																<div className="row-md-10">
+																	<Field name="dokumen">
+																		{({ field, form }) => (
+																			<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 {...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>
 													<div className="row-xl-10"> */}
-													<Button color="primary" onClick={sanksi.data && sanksi.data.length && sanksi.data[0].sanksi.keberatan.jawaban ? this.toggleModal : this.handelSimpan}>
-														Simpan
-													</Button>
-													{/* </div>
+															<FormGroup row>
+																<div className="col-xl-10">
+																	<Button color="primary" type="submit">
+																		Simpan
+																	</Button>
+																</div>
+															</FormGroup>
+															{/* <Button color="primary" onClick={sanksi.data.jawaban?.keberatan ? this.toggleModal : this.handelSimpan}>
+															Simpan
+														</Button> */}
+															{/* </div>
 												</FormGroup> */}
-												</form>
+														</Form>
+													)}
+												</Formik>
 											</Col>
 										</Row>
 									</CardBody>
@@ -221,12 +273,12 @@ class DetailKeberatan extends Component {
 						) : (
 							<Loader />
 						)}
-						<Col xl="3">{pt?.data ? <DetailPT data={pt.data[0]} /> : <Loader />}</Col>
+						<Col xl="3">{pt ? <DetailPT data={pt} /> : <Loader />}</Col>
 					</Row>
-					{sanksi.data && sanksi.data.length && (
+					{sanksi.data && (
 						<Row>
 							<Col>
-								<Riwayat data={sanksi.data[0].sanksi.keberatan.jawaban} />
+								<Riwayat data={sanksi.data.jawaban?.keberatan ? sanksi.data.jawaban.keberatan : null} />
 							</Col>
 						</Row>
 					)}
@@ -247,4 +299,5 @@ class DetailKeberatan extends Component {
 	}
 }
 
-export default DetailKeberatan;
+const mapStateToProps = (state) => ({ user: state.user, token: state.token });
+export default connect(mapStateToProps)(DetailKeberatan);

+ 4 - 6
pages/app/keberatan/index.js

@@ -16,10 +16,8 @@ class Keberatan extends Component {
 	}
 
 	componentDidMount = async () => {
-		const { organisasi, peran } = this.props.user.peran[0];
-		const query = { role: peran.id === 2021 ? "lldikti" : "dikti", keberatan: true };
-		if (peran.id === 2021) query.orgId = organisasi.id;
-		const sanksi = await getSanksi(query);
+		const { token } = this.props;
+		const sanksi = await getSanksi(token, { keberatan: true });
 		this.setState({ sanksi });
 	};
 
@@ -32,12 +30,12 @@ class Keberatan extends Component {
 					<Col lg="4">
 						<CaseProgress />
 					</Col>
-					<Col lg="8">{sanksi?.data ? <TableSanksi listData={sanksi.data} to="/app/keberatan/detail" linkName="Detail" /> : <Loader />}</Col>
+					<Col lg="8">{sanksi.data ? <TableSanksi listData={sanksi.data} to="/app/keberatan/detail" linkName="Detail" /> : <Loader />}</Col>
 				</Row>
 			</ContentWrapper>
 		);
 	}
 }
 
-const mapStateToProps = (state) => ({ user: state.user });
+const mapStateToProps = (state) => ({ user: state.user, token: state.token });
 export default connect(mapStateToProps)(Keberatan);

+ 8 - 5
pages/app/pelaporan/detail.js

@@ -1,12 +1,13 @@
 import React, { Component } from "react";
 import Header from "@/components/Main/Header";
 import DetailPT from "@/components/Main/DetailPT";
-import { getPelaporan } from "@/actions/pelaporan";
+import { getOneLaporan } from "@/actions/pelaporan";
 import DetailLaporan from "@/components/Main/DetailLaporan";
 import Link from "next/link";
 import ContentWrapper from "@/components/Layout/ContentWrapper";
 import { Row, Col, Card, CardBody } from "reactstrap";
 import Loader from "@/components/Common/Loader";
+import { connect } from "react-redux";
 
 class DetailPelaporan extends Component {
 	constructor(props) {
@@ -22,12 +23,13 @@ class DetailPelaporan extends Component {
 
 	componentDidMount = async () => {
 		const { query } = this.props;
-		const pelaporan = await getPelaporan({ ptId: query.ptId, number: query.number });
+		const pelaporan = await getOneLaporan(this.props.token, query.id);
 		this.setState({ pelaporan });
 	};
 
 	render() {
 		const { pelaporan } = this.state;
+		console.log(pelaporan);
 		return (
 			<ContentWrapper unwrap>
 				{/* <Header /> */}
@@ -46,7 +48,7 @@ class DetailPelaporan extends Component {
 								<Card className="card-default">
 									<CardBody>
 										<Row>
-											<Col lg={12}>{<DetailLaporan data={pelaporan.data[0]} />}</Col>
+											<Col lg={12}>{<DetailLaporan data={pelaporan.data} />}</Col>
 										</Row>
 									</CardBody>
 								</Card>
@@ -54,7 +56,7 @@ class DetailPelaporan extends Component {
 								<Loader />
 							)}
 						</Col>
-						<Col xl="3">{pelaporan.data ? <DetailPT data={pelaporan.data[0].pt} /> : <Loader />}</Col>
+						<Col xl="3">{pelaporan.data ? <DetailPT data={pelaporan.data.pt} /> : <Loader />}</Col>
 					</Row>
 				</div>
 			</ContentWrapper>
@@ -62,4 +64,5 @@ class DetailPelaporan extends Component {
 	}
 }
 
-export default DetailPelaporan;
+const mapStateToProps = (state) => ({ user: state.user, token: state.token });
+export default connect(mapStateToProps)(DetailPelaporan);

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

@@ -17,10 +17,7 @@ class Pelaporan 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 pelaporan = await getPelaporan(this.props.token);
 		this.setState({ pelaporan });
 	};
 
@@ -49,5 +46,5 @@ class Pelaporan extends Component {
 	}
 }
 
-const mapStateToProps = (state) => ({ user: state.user });
+const mapStateToProps = (state) => ({ user: state.user, token: state.token });
 export default connect(mapStateToProps)(Pelaporan);

+ 7 - 6
pages/app/pelaporan/new.js

@@ -3,8 +3,8 @@ import Header from "@/components/Main/Header";
 import DetailPT from "@/components/Main/DetailPT";
 import InputData from "@/components/Pelaporan/InputData";
 import Link from "next/link";
-import { getPT } from "@/actions/PT";
-
+import { getOnePT } from "@/actions/PT";
+import { connect } from "react-redux";
 import ContentWrapper from "@/components/Layout/ContentWrapper";
 import { Row, Col, Card, CardHeader, CardBody } from "reactstrap";
 import Loader from "@/components/Common/Loader";
@@ -22,8 +22,8 @@ class PelaporanNew extends Component {
 	};
 
 	componentDidMount = async () => {
-		const { query } = this.props;
-		const pt = await getPT({ id: query.ptId });
+		const { query, token } = this.props;
+		const pt = await getOnePT(token, query.ptId);
 		this.setState({ pt });
 	};
 
@@ -57,7 +57,7 @@ class PelaporanNew extends Component {
 								</CardBody>
 							</Card>
 						</Col>
-						<Col xl="3">{pt?.data ? <DetailPT data={pt.data[0]} /> : <Loader />}</Col>
+						<Col xl="3">{pt?.data ? <DetailPT data={pt.data} /> : <Loader />}</Col>
 					</Row>
 				</div>
 			</ContentWrapper>
@@ -65,4 +65,5 @@ class PelaporanNew extends Component {
 	}
 }
 
-export default PelaporanNew;
+const mapStateToProps = (state) => ({ user: state.user, token: state.token });
+export default connect(mapStateToProps)(PelaporanNew);

+ 12 - 9
pages/app/pelaporan/search.js

@@ -19,8 +19,11 @@ class Search extends Component {
 	};
 
 	componentDidMount = async () => {
-		const dataPembina = await getPembina();
-		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) => {
@@ -51,9 +54,9 @@ class Search extends Component {
 	};
 
 	fetchData = async () => {
-		const pembina = this.props.user.peran[0].peran.id === 2021 ? this.props.user.peran[0].organisasi.id : this.state.selectedOptionMulti.join(",");
+		const pembina = this.props.user.role.id === 2021 ? this.props.user.lembaga.id : this.state.selectedOptionMulti.map((e) => e.value).join(",");
 		const searchValue = document.getElementById("searchInput").value;
-		const jsonData = await getPT({ search: searchValue, pembina });
+		const jsonData = await getPT(this.props.token, { search: searchValue, pembina });
 		this.setState({ data: jsonData.data });
 	};
 
@@ -92,7 +95,7 @@ class Search extends Component {
 											{pt.sk_pendirian} - {pt.website} - {pt.email}
 										</small>
 										<p>{pt.alamat.jalan}</p>
-										{this.props.user.peran[0].peran.id === 2021 ? "" : <p>Pembina: {pt.pembina.nama}</p>}
+										{this.props.user.role.id === 2021 ? "" : <p>Pembina: {pt.pembina.nama}</p>}
 									</div>
 									<div className="ml-auto">
 										<Button color="info" size="sm" onClick={(e) => this.handleClick(e, pt.id)}>
@@ -124,14 +127,14 @@ class Search extends Component {
 					</div>
 				</div>
 				<Row>
-					<Col lg={this.props.user.peran[0].peran.id === 2021 ? 12 : 9}>
+					<Col lg={this.props.user.role.id === 2021 ? 12 : 9}>
 						<div className="form-group mb-4">
 							<input className="form-control mb-2" type="text" id="searchInput" placeholder="Pencarian Nama Perguruan Tinggi" />
 							<div className="d-flex">
 								<button className="btn btn-secondary" type="button" onClick={(e) => this.handleSearchClick()}>
 									Search
 								</button>
-								<div className="ml-auto">{this.props.user.peran[0].peran.id === 2021 && `Pembina: ${this.props.user.peran[0].organisasi.nama}`}</div>
+								<div className="ml-auto">{this.props.user.role.id === 2021 && `Pembina: ${this.props.user.lembaga.nama}`}</div>
 							</div>
 						</div>
 						<div className="card card-default">
@@ -150,7 +153,7 @@ class Search extends Component {
 							</div>
 						</div>
 					</Col>
-					{this.props.user.peran[0].peran.id === 2021 ? (
+					{this.props.user.role.id === 2021 ? (
 						""
 					) : (
 						<Col lg="3">
@@ -171,5 +174,5 @@ class Search extends Component {
 	}
 }
 
-const mapStateToProps = (state) => ({ user: state.user });
+const mapStateToProps = (state) => ({ user: state.user, token: state.token });
 export default connect(mapStateToProps)(Search);

+ 14 - 14
pages/app/pemantauan-perbaikan/detail.js

@@ -4,30 +4,29 @@ import Link from "next/link";
 import Header from "@/components/Main/Header";
 import DetailPT from "@/components/Main/DetailPT";
 import Riwayat from "@/components/DocPerbaikan/Riwayat";
-import { getSanksi } from "@/actions/sanksi";
+import { getOneSanksi } from "@/actions/sanksi";
 import ContentWrapper from "@/components/Layout/ContentWrapper";
 import { Row, Col, Card, CardBody } from "reactstrap";
 import { getPT } from "@/actions/PT";
 import Loader from "@/components/Common/Loader";
+import { connect } from "react-redux";
 
 class PemantauanDokumen extends Component {
 	constructor(props) {
 		super(props);
 		this.state = {
 			sanksi: {},
-			pt: {},
+			pt: null,
 		};
 	}
 
-	static getInitialProps = async ({ query }) => {
-		return { query };
-	};
+	static getInitialProps = async ({ query }) => ({ query });
 
 	componentDidMount = async () => {
-		const { query } = this.props;
-		const sanksi = await getSanksi(query);
-		const pt = await getPT({ id: query.ptId });
-		this.setState({ pt, sanksi });
+		const { query, token } = this.props;
+		const sanksi = await getOneSanksi(token, query.id);
+		const pt = sanksi.data.laporan.pt;
+		this.setState({ sanksi, pt });
 	};
 
 	render() {
@@ -45,13 +44,13 @@ class PemantauanDokumen extends Component {
 						</div>
 					</div>
 					<Row>
-						{sanksi.data && sanksi.data.length ? (
+						{sanksi.data ? (
 							<Col xl="9">
 								<Card className="card-default">
 									<CardBody>
 										<Row>
 											<Col lg={12}>
-												<DetailSanksi data={sanksi.data[0]} />
+												<DetailSanksi data={sanksi.data} />
 											</Col>
 										</Row>
 									</CardBody>
@@ -60,10 +59,10 @@ class PemantauanDokumen extends Component {
 						) : (
 							<Loader />
 						)}
-						<Col xl="3">{pt?.data ? <DetailPT data={pt.data[0]} /> : <Loader />}</Col>
+						<Col xl="3">{pt ? <DetailPT data={pt} /> : <Loader />}</Col>
 					</Row>
 					<Row>
-						<Col>{sanksi.data && sanksi.data.length && <Riwayat data={sanksi.data[0].sanksi.doc_perbaikan} />}</Col>
+						<Col>{sanksi.data && <Riwayat data={sanksi.data.perbaikan} />}</Col>
 					</Row>
 				</div>
 			</ContentWrapper>
@@ -71,4 +70,5 @@ class PemantauanDokumen extends Component {
 	}
 }
 
-export default PemantauanDokumen;
+const mapStateToProps = (state) => ({ user: state.user, token: state.token });
+export default connect(mapStateToProps)(PemantauanDokumen);

+ 4 - 6
pages/app/pemantauan-perbaikan/index.js

@@ -16,10 +16,8 @@ class PemantauanPerbaikan extends Component {
 	}
 
 	componentDidMount = async () => {
-		const { organisasi, peran } = this.props.user.peran[0];
-		const query = { role: peran.id === 2021 ? "lldikti" : "dikti", docPerbaikan: true };
-		if (peran.id === 2021) query.orgId = organisasi.id;
-		const sanksi = await getSanksi(query);
+		const { token } = this.props;
+		const sanksi = await getSanksi(token, { perbaikan: true });
 		this.setState({ sanksi });
 	};
 
@@ -32,12 +30,12 @@ class PemantauanPerbaikan extends Component {
 					<Col lg="4">
 						<CaseProgress />
 					</Col>
-					<Col lg="8">{sanksi?.data ? <TableSanksi listData={sanksi.data} to="/app/pemantauan-perbaikan/detail" linkName="Detail" /> : <Loader />}</Col>
+					<Col lg="8">{sanksi.data ? <TableSanksi listData={sanksi.data} to="/app/pemantauan-perbaikan/detail" linkName="Detail" /> : <Loader />}</Col>
 				</Row>
 			</ContentWrapper>
 		);
 	}
 }
 
-const mapStateToProps = (state) => ({ user: state.user });
+const mapStateToProps = (state) => ({ user: state.user, token: state.token });
 export default connect(mapStateToProps)(PemantauanPerbaikan);

+ 13 - 10
pages/app/pemantauan/index.js

@@ -18,8 +18,11 @@ class Search extends Component {
 	};
 
 	componentDidMount = async () => {
-		const dataPembina = await getPembina();
-		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) => {
@@ -50,9 +53,9 @@ class Search extends Component {
 	};
 
 	fetchData = async () => {
-		const pembina = this.props.user.peran[0].peran.id === 2021 ? this.props.user.peran[0].organisasi.id : this.state.selectedOptionMulti.join(",");
+		const pembina = this.props.user.role.id === 2021 ? null : this.state.selectedOptionMulti.map((e) => e.value).join(",");
 		const searchValue = document.getElementById("searchInput").value;
-		const jsonData = await getPT({ search: searchValue, pembina });
+		const jsonData = await getPT(this.props.token, { search: searchValue, pembina });
 		this.setState({ data: jsonData.data ? jsonData.data : [] });
 	};
 
@@ -75,7 +78,7 @@ class Search extends Component {
 			this.state.data &&
 			this.state.data.map((pt, index) => {
 				return (
-					<tr>
+					<tr key={index}>
 						<td>
 							<label>{index + 1}</label>
 						</td>
@@ -91,7 +94,7 @@ class Search extends Component {
 											{pt.sk_pendirian} - {pt.website} - {pt.email}
 										</small>
 										<p>{pt.alamat.jalan}</p>
-										{this.props.user.peran[0].peran.id === 2021 ? "" : <p>Pembina: {pt.pembina.nama}</p>}
+										{this.props.user?.role.id === 2021 ? "" : <p>Pembina: {pt.pembina.nama}</p>}
 									</div>
 									<div className="ml-auto">
 										<Button color="info" size="sm" onClick={(e) => this.handleClick(e, pt.id)}>
@@ -118,14 +121,14 @@ class Search extends Component {
 					</div>
 				</div>
 				<Row>
-					<Col lg={this.props.user.peran[0].peran.id === 2021 ? 12 : 9}>
+					<Col lg={this.props.user?.role.id === 2021 ? 12 : 9}>
 						<div className="form-group mb-4">
 							<input className="form-control mb-2" type="text" id="searchInput" placeholder="Pencarian Nama Perguruan Tinggi" />
 							<div className="d-flex">
 								<button className="btn btn-secondary" type="button" onClick={(e) => this.handleSearchClick()}>
 									Search
 								</button>
-								<div className="ml-auto">{this.props.user.peran[0].peran.id === 2021 && `Pembina: ${this.props.user.peran[0].organisasi.nama}`}</div>
+								<div className="ml-auto">{this.props.user?.role.id === 2021 && `Pembina: ${this.props.user.lembaga.nama}`}</div>
 							</div>
 						</div>
 						{/* START card */}
@@ -148,7 +151,7 @@ class Search extends Component {
 						</div>
 						{/* END card */}
 					</Col>
-					{this.props.user.peran[0].peran.id === 2021 ? (
+					{this.props.user?.role.id === 2021 ? (
 						""
 					) : (
 						<Col lg="3">
@@ -169,5 +172,5 @@ class Search extends Component {
 	}
 }
 
-const mapStateToProps = (state) => ({ user: state.user });
+const mapStateToProps = (state) => ({ user: state.user, token: state.token });
 export default connect(mapStateToProps)(Search);

+ 9 - 7
pages/app/pemantauan/timeline.js

@@ -5,9 +5,10 @@ import DetailPT from "@/components/Main/DetailPT";
 import { getLog } from "@/actions/log";
 import { Row, Col } from "reactstrap";
 import Timeline from "@/components/Main/Timeline";
-import { getPT } from "@/actions/PT";
+import { getOnePT } from "@/actions/PT";
 import Link from "next/link";
 import Loader from "@/components/Common/Loader";
+import { connect } from "react-redux";
 
 class Pemantauan extends Component {
 	constructor(props) {
@@ -23,10 +24,10 @@ class Pemantauan extends Component {
 	}
 
 	componentDidMount = async () => {
-		const { query } = this.props;
-		const log = await getLog({ ptId: query.ptId });
+		const { query, token } = this.props;
+		const log = await getLog(token, query.ptId);
 		this.setState({ log });
-		const pt = await getPT({ id: query.ptId });
+		const pt = await getOnePT(token, query.ptId);
 		this.setState({ pt });
 	};
 
@@ -37,7 +38,7 @@ class Pemantauan extends Component {
 			<ContentWrapper unwrap>
 				<div className="p-3">
 					<div className="content-heading">
-						<div>Pemantauan {pt?.data && pt.data[0].nama}</div>
+						<div>Pemantauan {pt?.data && pt.data.nama}</div>
 						<div className="ml-auto">
 							<Link href="/app/pemantauan">
 								<button className="btn btn-sm btn-secondary text-sm">&lt; back</button>
@@ -46,7 +47,7 @@ class Pemantauan extends Component {
 					</div>
 					<Row>
 						<Col xl="9">{log?.data ? <Timeline data={log.data} /> : <Loader />}</Col>
-						<Col xl="3">{pt?.data ? <DetailPT data={pt.data[0]} /> : <Loader />}</Col>
+						<Col xl="3">{pt?.data ? <DetailPT data={pt.data} /> : <Loader />}</Col>
 					</Row>
 				</div>
 			</ContentWrapper>
@@ -54,4 +55,5 @@ class Pemantauan extends Component {
 	}
 }
 
-export default Pemantauan;
+const mapStateToProps = (state) => ({ user: state.user, token: state.token });
+export default connect(mapStateToProps)(Pemantauan);

+ 5 - 7
pages/app/pemeriksaan/index.js

@@ -3,7 +3,7 @@ import ContentWrapper from "@/components/Layout/ContentWrapper";
 import { Row, Col } from "reactstrap";
 import { getPelaporan } from "@/actions/pelaporan";
 import CaseProgress from "@/components/Main/CaseProgress";
-import TableLaporan from "@/components/Main/TableLaporan";
+import TableLaporan from "@/components/Pemeriksaan/TableLaporan";
 import { connect } from "react-redux";
 import Loader from "@/components/Common/Loader";
 
@@ -16,10 +16,8 @@ class Pemeriksaan extends Component {
 	}
 
 	componentDidMount = async () => {
-		const { organisasi, peran } = this.props.user.peran[0];
-		const query = { role: peran.id === 2021 ? "lldikti" : "dikti", penjadwalan: true, active: true };
-		if (peran.id === 2021) query.orgId = organisasi.id;
-		const pelaporan = await getPelaporan(query);
+		const { token } = this.props;
+		const pelaporan = await getPelaporan(token, { jadwal: true });
 		this.setState({ pelaporan });
 	};
 
@@ -32,12 +30,12 @@ class Pemeriksaan extends Component {
 					<Col lg="4">
 						<CaseProgress />
 					</Col>
-					<Col lg="8">{pelaporan?.data ? <TableLaporan noBy listData={pelaporan.data} to="/app/pemeriksaan/new" linkName="Evaluasi" status /> : <Loader />}</Col>
+					<Col lg="8">{pelaporan?.data ? <TableLaporan status noBy listData={pelaporan.data} to="/app/pemeriksaan/new" linkName="Evaluasi" /> : <Loader />}</Col>
 				</Row>
 			</ContentWrapper>
 		);
 	}
 }
 
-const mapStateToProps = (state) => ({ user: state.user });
+const mapStateToProps = (state) => ({ user: state.user, token: state.token });
 export default connect(mapStateToProps)(Pemeriksaan);

+ 13 - 11
pages/app/pemeriksaan/new.js

@@ -5,10 +5,11 @@ import Header from "@/components/Main/Header";
 import DetailLaporan from "@/components/Main/DetailLaporan";
 import InputEvaluasi from "@/components/Pemeriksaan/InputEvaluasi";
 import TableRiwayat from "@/components/Pemeriksaan/TableRiwayat";
-import { getPelaporan } from "@/actions/pelaporan";
+import { getOneLaporan } from "@/actions/pelaporan";
 import Link from "next/link";
 import { Row, Col, Card, CardBody, CardHeader, CardTitle } from "reactstrap";
 import Loader from "@/components/Common/Loader";
+import { connect } from "react-redux";
 
 class PemeriksaanNew extends Component {
 	constructor(props) {
@@ -22,13 +23,13 @@ class PemeriksaanNew extends Component {
 	};
 
 	componentDidMount = async () => {
-		const { query } = this.props;
-		const pelaporan = await getPelaporan({ ptId: query.ptId, number: query.number });
+		const { token, query } = this.props;
+		const pelaporan = await getOneLaporan(token, query.id);
 		this.setState({ pelaporan });
 	};
 
 	render() {
-		const { query } = this.props;
+		const { query, token } = this.props;
 		const { pelaporan } = this.state;
 		return (
 			<ContentWrapper unwrap>
@@ -44,13 +45,13 @@ class PemeriksaanNew extends Component {
 					</div>
 					<Row>
 						<Col xl="9">
-							{pelaporan.data && pelaporan.data.length ? (
+							{pelaporan.data ? (
 								<Card className="card-default">
 									<CardBody>
 										<Row>
 											<Col lg={12}>
-												<DetailLaporan data={pelaporan.data[0]} />
-												<InputEvaluasi query={query} />
+												<DetailLaporan data={pelaporan.data} />
+												<InputEvaluasi query={query} token={token} />
 											</Col>
 										</Row>
 									</CardBody>
@@ -59,9 +60,9 @@ class PemeriksaanNew extends Component {
 								<Loader />
 							)}
 						</Col>
-						<Col xl="3">{pelaporan.data && pelaporan.data.length ? <DetailPT data={pelaporan.data[0].pt} /> : <Loader />}</Col>
+						<Col xl="3">{pelaporan.data ? <DetailPT data={pelaporan.data.pt} /> : <Loader />}</Col>
 					</Row>
-					{pelaporan.data && pelaporan.data.length && (
+					{pelaporan.data && (
 						<Row>
 							<Col>
 								<Card className="card-default">
@@ -69,7 +70,7 @@ class PemeriksaanNew extends Component {
 										<CardTitle>Riwayat Evaluasi</CardTitle>
 									</CardHeader>
 									<CardBody>
-										<TableRiwayat data={pelaporan.data[0]} />
+										<TableRiwayat data={pelaporan.data} />
 									</CardBody>
 								</Card>
 							</Col>
@@ -81,4 +82,5 @@ class PemeriksaanNew extends Component {
 	}
 }
 
-export default PemeriksaanNew;
+const mapStateToProps = (state) => ({ user: state.user, token: state.token });
+export default connect(mapStateToProps)(PemeriksaanNew);

+ 134 - 91
pages/app/pencabutan-sanksi/detail.js

@@ -7,13 +7,22 @@ import Header from "@/components/Main/Header";
 import DetailPT from "@/components/Main/DetailPT";
 import PermohonanPT from "@/components/Main/PermohonanPT";
 import Riwayat from "@/components/PencabutanSanksi/Riwayat";
-import { getSanksi } from "@/actions/sanksi";
+import { getOneSanksi } from "@/actions/sanksi";
 import { addJawabanCabutSanksi } from "@/actions/cabutSanksi";
 import ContentWrapper from "@/components/Layout/ContentWrapper";
 import { Row, Col, Card, CardBody, FormGroup, Button, Input, Modal, ModalHeader, ModalBody, ModalFooter } from "reactstrap";
 import { getPT } from "@/actions/PT";
 import Loader from "@/components/Common/Loader";
 import { toast } from "react-toastify";
+import { connect } from "react-redux";
+import { Formik, Form, Field, ErrorMessage } from "formik";
+import * as Yup from "yup";
+
+const jawabanCabutSanksiSchema = Yup.object().shape({
+	status: Yup.string().required("Harap Diisi"),
+	keterangan: Yup.string().max(200).notRequired(),
+	dokumen: Yup.array().notRequired(),
+});
 
 let Dropzone = null;
 class DropzoneWrapper extends Component {
@@ -39,19 +48,18 @@ class JawabanPencabutanSanksi extends Component {
 			files: [],
 			keterangan: "",
 			sanksi: {},
-			pt: {},
+			pt: null,
+			data: {},
 		};
 	}
 
-	static getInitialProps = async ({ query }) => {
-		return { query };
-	};
+	static getInitialProps = async ({ query }) => ({ query });
 
 	componentDidMount = async () => {
-		const { query } = this.props;
-		const sanksi = await getSanksi(query);
-		const pt = await getPT({ id: query.ptId });
-		this.setState({ pt, sanksi });
+		const { query, token } = this.props;
+		const sanksi = await getOneSanksi(token, query.id);
+		const pt = sanksi.data.laporan.pt;
+		this.setState({ sanksi, pt });
 	};
 
 	toggleModal = () => {
@@ -98,26 +106,25 @@ class JawabanPencabutanSanksi extends Component {
 		this.setState({ keterangan: e.target.value });
 	};
 
-	handleSimpan = async (e) => {
-		e.preventDefault();
-		const { selectedOption, keterangan } = this.state;
-		const { noSanksi, ptId } = this.props.query;
+	handleSimpan = async () => {
+		const { data } = this.state;
+		console.log(data);
+		const { token, query } = this.props;
 		const formdata = new FormData();
-		formdata.append("status", selectedOption.value);
-		formdata.append("description", keterangan);
-		if (this.state.files.length > 0) {
-			this.state.files.forEach((e) => {
-				formdata.append("files", e);
+		formdata.append("status", data.status);
+		formdata.append("keterangan", data.keterangan);
+		this.state.files.forEach((e) => {
+			formdata.append("dokumen", e);
+		});
+		const toastid = toast.loading("Please wait...");
+		const added = await addJawabanCabutSanksi(token, query.id, formdata);
+		if (!added) {
+			toast.update(toastid, { render: "All is not good", type: "error", isLoading: false, autoClose: true, closeButton: true });
+		} else {
+			toast.update(toastid, { render: "All is good", type: "success", isLoading: false, autoClose: true, closeButton: true });
+			Router.push({
+				pathname: "/app/pencabutan-sanksi",
 			});
-			const id = toast.loading("Please wait...");
-			const added = await addJawabanCabutSanksi({ noSanksi, ptId }, formdata);
-			// console.log(added);
-			if (added) {
-				toast.update(id, { render: "All is good", type: "success", isLoading: false, autoClose: true, closeButton: true });
-				Router.push({
-					pathname: "/app/pencabutan-sanksi",
-				});
-			}
 		}
 	};
 
@@ -149,69 +156,104 @@ class JawabanPencabutanSanksi extends Component {
 									<CardBody>
 										<Row>
 											<Col lg={12}>
-												<DetailSanksi data={sanksi.data[0]} />
-												<PermohonanPT data={sanksi.data[0].sanksi.cabut_sanksi} />
+												<DetailSanksi data={sanksi.data} />
+												<PermohonanPT data={sanksi.data.pengajuan.cabut_sanksi} />
 												<p className="lead bb">Jawaban</p>
-												<form className="form-horizontal" method="get" action="/" onSubmit={this.onSubmit}>
-													<FormGroup>
-														<label className="row-form-label">Status:</label>
-														<div className="row-md-10">
-															<Select
-																instanceId={selectInstanceId + 1}
-																value={this.state.selectedOption}
-																onChange={this.handleChangeSelect}
-																options={[
-																	{ value: "Diterima", label: "Diterima", className: "State-ACT" },
-																	{ value: "Rekomendasi Perbaikan", label: "Rekomendasi Perbaikan", className: "State-ACT" },
-																]}
-																required
-															/>
-															{/* <span className="form-text">Pilih Jenis Pelanggaran</span> */}
-														</div>
-													</FormGroup>
-													{selectedOption && selectedOption.value === "Rekomendasi Perbaikan" ? (
-														<FormGroup>
-															<label className="row-form-label">Keterangan:</label>
-															<div className="row-md-10">
-																<Input type="textarea" value={this.state.keterangan} onChange={this.setKeterangan} required />
-																{/* <span className="form-text">Deskripsi pelaporan minimum karakter 50 maksimum 200 karakter</span> */}
-															</div>
-														</FormGroup>
-													) : (
-														""
+												<Formik
+													initialValues={{
+														status: "",
+														keterangan: "",
+														dokumen: [],
+													}}
+													validationSchema={jawabanCabutSanksiSchema}
+													onSubmit={async (data) => {
+														this.setState({ data });
+														if (sanksi.data.jawaban?.cabut_sanksi) this.toggleModal();
+														else await this.handleSimpan();
+													}}
+												>
+													{() => (
+														<Form className="form-horizontal">
+															<FormGroup>
+																<label className="row-form-label">Status:</label>
+																<div className="row-md-10">
+																	<Field name="status">
+																		{({ field, form }) => (
+																			<Select
+																				instanceId={selectInstanceId + 1}
+																				value={this.state.selectedOption}
+																				onChange={(e) => {
+																					this.handleChangeSelect(e);
+																					form.setFieldValue(field.name, e.value);
+																				}}
+																				options={[
+																					{ value: "Diterima", label: "Diterima", className: "State-ACT" },
+																					{ value: "Rekomendasi Perbaikan", label: "Rekomendasi Perbaikan", className: "State-ACT" },
+																				]}
+																			/>
+																		)}
+																	</Field>
+																	<ErrorMessage name="status" component="div" className="form-text text-danger" />
+																	{/* <span className="form-text">Pilih Jenis Pelanggaran</span> */}
+																</div>
+															</FormGroup>
+															{selectedOption && selectedOption.value === "Rekomendasi Perbaikan" ? (
+																<FormGroup>
+																	<label className="row-form-label">Keterangan:</label>
+																	<div className="row-md-10">
+																		<Field name="keterangan">{({ field }) => <Input type="textarea" {...field} />}</Field>
+																		<ErrorMessage name="keterangan" component="div" className="form-text text-danger" />
+																		{/* <span className="form-text">Deskripsi pelaporan minimum karakter 50 maksimum 200 karakter</span> */}
+																	</div>
+																</FormGroup>
+															) : (
+																""
+															)}
+															<FormGroup>
+																<label className="row-form-label">Upload Dokumen:</label>
+																<div className="row-md-10">
+																	<Field name="dokumen">
+																		{({ field, form }) => (
+																			<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 {...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>
+																<div className="row-xl-10">
+																	<Button color="primary" type="submit">
+																		Simpan
+																	</Button>
+																</div>
+															</FormGroup>
+														</Form>
 													)}
-													<FormGroup>
-														<label className="row-form-label">Upload Dokumen:</label>
-														<div className="row-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>
-														<div className="row-xl-10">
-															<Button color="primary" onClick={sanksi.data && sanksi.data.length && sanksi.data[0].sanksi.cabut_sanksi.jawaban ? this.toggleModal : this.handleSimpan}>
-																Simpan
-															</Button>
-														</div>
-													</FormGroup>
-												</form>
+												</Formik>
 											</Col>
 										</Row>
 									</CardBody>
@@ -220,12 +262,12 @@ class JawabanPencabutanSanksi extends Component {
 						) : (
 							<Loader />
 						)}
-						<Col xl="3">{pt.data ? <DetailPT data={pt.data[0]} /> : <Loader />}</Col>
+						<Col xl="3">{pt ? <DetailPT data={pt} /> : <Loader />}</Col>
 					</Row>
 					{sanksi.data && (
 						<Row>
 							<Col>
-								<Riwayat data={sanksi.data[0]} />
+								<Riwayat data={sanksi.data.jawaban?.cabut_sanksi} />
 							</Col>
 						</Row>
 					)}
@@ -246,4 +288,5 @@ class JawabanPencabutanSanksi extends Component {
 	}
 }
 
-export default JawabanPencabutanSanksi;
+const mapStateToProps = (state) => ({ user: state.user, token: state.token });
+export default connect(mapStateToProps)(JawabanPencabutanSanksi);

+ 4 - 6
pages/app/pencabutan-sanksi/index.js

@@ -16,10 +16,8 @@ class PencabutanSanksi extends Component {
 	}
 
 	componentDidMount = async () => {
-		const { organisasi, peran } = this.props.user.peran[0];
-		const query = { role: peran.id === 2021 ? "lldikti" : "dikti", cabutSanksi: true };
-		if (peran.id === 2021) query.orgId = organisasi.id;
-		const sanksi = await getSanksi(query);
+		const { token } = this.props;
+		const sanksi = await getSanksi(token, { cabutSanksi: true });
 		this.setState({ sanksi });
 	};
 
@@ -32,12 +30,12 @@ class PencabutanSanksi extends Component {
 					<Col lg="4">
 						<CaseProgress />
 					</Col>
-					<Col lg="8">{sanksi?.data ? <TableSanksi listData={sanksi.data} to="/app/pencabutan-sanksi/detail" linkName="Detail" /> : <Loader />}</Col>
+					<Col lg="8">{sanksi.data ? <TableSanksi listData={sanksi.data} to="/app/pencabutan-sanksi/detail" linkName="Detail" /> : <Loader />}</Col>
 				</Row>
 			</ContentWrapper>
 		);
 	}
 }
 
-const mapStateToProps = (state) => ({ user: state.user });
+const mapStateToProps = (state) => ({ user: state.user, token: state.token });
 export default connect(mapStateToProps)(PencabutanSanksi);

+ 4 - 6
pages/app/penjadwalan/index.js

@@ -3,7 +3,7 @@ import ContentWrapper from "@/components/Layout/ContentWrapper";
 import { Row, Col } from "reactstrap";
 import { getPelaporan } from "@/actions/pelaporan";
 import CaseProgress from "@/components/Main/CaseProgress";
-import TableLaporan from "@/components/Main/TableLaporan";
+import TableLaporan from "@/components/Penjadwalan/TableLaporan";
 import { connect } from "react-redux";
 import Loader from "@/components/Common/Loader";
 
@@ -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);

+ 11 - 9
pages/app/sanksi/detail.js

@@ -7,8 +7,9 @@ import DetailSanksi from "@/components/Main/DetailSanksi";
 import Header from "@/components/Main/Header";
 import Link from "next/link";
 import { getPelaporan } from "@/actions/pelaporan";
-import { createSanksi } from "@/actions/sanksi";
+import { getOneSanksi } from "@/actions/sanksi";
 import Loader from "@/components/Common/Loader";
+import { connect } from "react-redux";
 
 const stepNavitemStyle = {
 	backgroundColor: "#fcfcfc",
@@ -18,7 +19,7 @@ class Detail extends Component {
 	constructor(props) {
 		super(props);
 		this.state = {
-			pelaporan: {},
+			sanksi: {},
 		};
 	}
 
@@ -27,13 +28,13 @@ class Detail extends Component {
 	};
 
 	componentDidMount = async () => {
-		const { query } = this.props;
-		const pelaporan = await getPelaporan({ ptId: query.ptId, number: query.number });
-		this.setState({ pelaporan });
+		const { query, token } = this.props;
+		const sanksi = await getOneSanksi(token, query.id);
+		this.setState({ sanksi });
 	};
 
 	render() {
-		const { pelaporan } = this.state;
+		const { sanksi } = this.state;
 		return (
 			<ContentWrapper unwrap>
 				{/* <Header /> */}
@@ -46,12 +47,12 @@ class Detail extends Component {
 							</Link>
 						</div>
 					</div>
-					{pelaporan?.data ? (
+					{sanksi.data ? (
 						<Card className="card-default">
 							<CardBody>
 								<Row>
 									<Col>
-										<DetailSanksi data={pelaporan.data[0]} />
+										<DetailSanksi data={sanksi.data} />
 									</Col>
 								</Row>
 							</CardBody>
@@ -65,4 +66,5 @@ class Detail extends Component {
 	}
 }
 
-export default Detail;
+const mapStateToProps = (state) => ({ user: state.user, token: state.token });
+export default connect(mapStateToProps)(Detail);

+ 4 - 6
pages/app/sanksi/index.js

@@ -16,10 +16,8 @@ class Sanksi extends Component {
 	}
 
 	componentDidMount = async () => {
-		const { organisasi, peran } = this.props.user.peran[0];
-		const query = { role: peran.id === 2021 ? "lldikti" : "dikti", pemeriksaan: true, active: true };
-		if (peran.id === 2021) query.orgId = organisasi.id;
-		const pelaporan = await getPelaporan(query);
+		const { token } = this.props;
+		const pelaporan = await getPelaporan(token, { evaluasi: true });
 		this.setState({ pelaporan });
 	};
 
@@ -32,12 +30,12 @@ class Sanksi extends Component {
 					<Col lg="4">
 						<CaseProgress />
 					</Col>
-					<Col lg="8">{pelaporan?.data ? <TableLaporan listData={pelaporan.data} /> : <Loader />}</Col>
+					<Col lg="8">{pelaporan.data ? <TableLaporan listData={pelaporan.data} /> : <Loader />}</Col>
 				</Row>
 			</ContentWrapper>
 		);
 	}
 }
 
-const mapStateToProps = (state) => ({ user: state.user });
+const mapStateToProps = (state) => ({ user: state.user, token: state.token });
 export default connect(mapStateToProps)(Sanksi);

+ 31 - 28
pages/app/sanksi/proses.js

@@ -10,9 +10,9 @@ import UploadSurat from "@/components/Sanksi/UploadSurat";
 import Ringkasan from "@/components/Sanksi/Ringkasan";
 import TablePenetapanSanksi from "@/components/Sanksi/TablePenetapanSanksi";
 import Link from "next/link";
-import { getPelaporan } from "@/actions/pelaporan";
+import { getOneLaporan } from "@/actions/pelaporan";
 import { createSanksi } from "@/actions/sanksi";
-import { getPelanggaranId } from "@/actions/pelanggaran";
+import { getPelanggaran } from "@/actions/pelanggaran";
 import { connect } from "react-redux";
 import Loader from "@/components/Common/Loader";
 import { ToastContainer, toast } from "react-toastify";
@@ -29,7 +29,7 @@ class ProsesSanksi extends Component {
 			activeStep: "1",
 			dataUpload: null,
 			dataPelanggaran: {},
-			pelaporan: null,
+			pelaporan: {},
 		};
 	}
 
@@ -38,31 +38,34 @@ class ProsesSanksi extends Component {
 	};
 
 	componentDidMount = async () => {
-		const { query } = this.props;
-		const pelaporan = await getPelaporan({ ptId: query.ptId, number: query.number });
+		const { query, token } = this.props;
+		const { id } = query;
+		const pelaporan = await getOneLaporan(token, id);
 		this.setState({ pelaporan });
 	};
 
 	done = async (e) => {
-		e.preventDefault();
-		const { ptId, number } = this.props.query;
-		const formdata = new FormData();
-		formdata.append("no_sanksi", this.state.dataUpload.nomorSanksi);
-		formdata.append("user", this.props.user._id);
-		formdata.append("description", this.state.dataUpload.keterangan);
-		formdata.append("pelanggaran", this.state.dataPelanggaran.data.map((e) => e._id).join());
-		if (this.state.dataUpload.files.length > 0) {
-			this.state.dataUpload.files.forEach((e) => {
-				formdata.append("files", e);
-			});
-		}
-		const id = toast.loading("Please wait...");
-		const create = await createSanksi({ ptId, number }, formdata);
-		if (create) {
-			toast.update(id, { render: "All is good", type: "success", isLoading: false, autoClose: true, closeButton: true });
+		const toastid = toast.loading("Please wait...");
+		try {
+			e.preventDefault();
+			const { query, token } = this.props;
+			const { id } = query;
+			const formdata = new FormData();
+			formdata.append("no_sanksi", this.state.dataUpload.nomorSanksi);
+			formdata.append("keterangan", this.state.dataUpload.keterangan);
+			formdata.append("pelanggaran_id", this.state.dataPelanggaran.data.map((e) => e._id).join());
+			if (this.state.dataUpload.files.length > 0) {
+				this.state.dataUpload.files.forEach((e) => {
+					formdata.append("dokumen", e);
+				});
+			}
+			await createSanksi(token, id, formdata);
+			toast.update(toastid, { render: "All is good", type: "success", isLoading: false, autoClose: true, closeButton: true });
 			Router.push({
 				pathname: "/app/sanksi",
 			});
+		} catch (error) {
+			toast.update(toastid, { render: "All is not good", type: "error", isLoading: false, autoClose: true, closeButton: true });
 		}
 	};
 
@@ -75,12 +78,12 @@ class ProsesSanksi extends Component {
 	};
 
 	setCheckedData = async (data) => {
-		const result = await getPelanggaranId(data);
+		const { token } = this.props;
+		const result = await getPelanggaran(token, { id: data.join(",") });
 		this.setState({ dataPelanggaran: result });
 	};
 
 	setUploadSuratSanksi = (data) => {
-		// this.dataUpload = data;
 		this.setState({ dataUpload: data });
 	};
 
@@ -165,7 +168,7 @@ class ProsesSanksi extends Component {
 										<TabPane tabId="1">
 											<div className="pt-3 mb-3">
 												<h2>Detail Laporan</h2>
-												{pelaporan?.data ? <DetailLaporan noTitle data={pelaporan.data[0]} /> : <Loader />}
+												{pelaporan.data ? <DetailLaporan noTitle data={pelaporan.data} /> : <Loader />}
 											</div>
 											<hr />
 											<div className="d-flex">
@@ -178,9 +181,9 @@ class ProsesSanksi extends Component {
 											<div className="pt-3 mb-3">
 												<h2>Hasil Evaluasi</h2>
 												<Card className="card-default">
-													{pelaporan?.data ? (
+													{pelaporan.data ? (
 														<CardBody>
-															<TableRiwayat data={pelaporan.data[0]} />
+															<TableRiwayat data={pelaporan.data} />
 														</CardBody>
 													) : (
 														<Loader />
@@ -230,7 +233,7 @@ class ProsesSanksi extends Component {
 										<TabPane tabId="5">
 											<div className="pt-3 mb-3">
 												<h2>Ringkasan</h2>
-												{pelaporan?.data ? <Ringkasan dataLaporan={pelaporan.data[0]} dataPelanggaran={dataPelanggaran.data} dataUpload={dataUpload} /> : <Loader />}
+												{pelaporan.data ? <Ringkasan dataLaporan={pelaporan.data} dataPelanggaran={dataPelanggaran.data} dataUpload={dataUpload} /> : <Loader />}
 											</div>
 											<hr />
 											<div className="d-flex">
@@ -253,5 +256,5 @@ class ProsesSanksi extends Component {
 	}
 }
 
-const mapStateToProps = (state) => ({ user: state.user });
+const mapStateToProps = (state) => ({ user: state.user, token: state.token });
 export default connect(mapStateToProps)(ProsesSanksi);

+ 195 - 159
pages/laporan/new/index.js

@@ -1,9 +1,9 @@
 import React, { Component } from "react";
 import Router from "next/router";
 import BasePage from "@/components/Layout/BasePage";
-import { getPT } from "@/actions/PT";
-import { getPelanggaran } from "@/actions/pelanggaran";
-import { createPelaporan } from "@/actions/pelaporan";
+import { ptPublic } from "@/actions/PT";
+import { getPelanggaranPublic } from "@/actions/pelanggaran";
+import { createLaporanPublic } from "@/actions/pelaporan";
 import { notifLaporanBaru } from "@/actions/notifikasi";
 import Select from "react-select";
 import AsyncSelect from "react-select/async";
@@ -12,12 +12,29 @@ import Link from "next/link";
 import ContentWrapper from "@/components/Layout/ContentWrapper";
 import { createPublicUser } from "@/actions/user";
 import { ToastContainer, toast } from "react-toastify";
+import { Formik, Form, Field, ErrorMessage } from "formik";
+import * as Yup from "yup";
 
 import "react-toastify/dist/ReactToastify.css";
 
+const laporanSchema = Yup.object().shape({
+	no_laporan: Yup.string().required("Harap Diisi"),
+	no_hp: Yup.number().required("Harap Diisi"),
+	nama: Yup.string().required(),
+	setuju: Yup.boolean().isTrue(),
+	alamat: Yup.string().min(3).max(200).required(),
+	keterangan: Yup.string().min(3).max(200).required(),
+	email: Yup.string().email().required(),
+	pelanggaran_id: Yup.array().min(1).required(),
+	pt_id: Yup.string().required(),
+	foto: Yup.array().min(1).required("Harus ada"),
+	dokumen: Yup.mixed().nullable().notRequired(),
+	is_private: Yup.boolean().notRequired(),
+});
+
 const loadOptions = (inputValue, callback) => {
 	setTimeout(async () => {
-		const pt = await getPT({ search: inputValue });
+		const pt = await ptPublic({ search: inputValue });
 		const data = pt.data.map((e) => ({ value: e.id, label: e.nama, className: "State-ACT" }));
 		callback(data);
 	}, 1000);
@@ -64,7 +81,7 @@ class App extends Component {
 	static getInitialProps = ({ pathname }) => ({ pathname });
 
 	componentDidMount = async () => {
-		const pelanggaran = await getPelanggaran();
+		const pelanggaran = await getPelanggaranPublic();
 		this.setState({ pelanggaran });
 	};
 
@@ -96,70 +113,36 @@ class App extends Component {
 		return inputValue;
 	};
 
-	handleKirim = async (e) => {
-		e.preventDefault();
-		const id = toast.loading("Please wait...");
-		const { nama } = this.state;
-		const user_id = await this.createUser();
-
-		if (user_id) {
-			const laporan = await this.createLaporan(user_id, nama);
-			if (laporan) {
-				toast.update(id, { render: "All is good", type: "success", isLoading: false });
-				await this.sendNotif();
-				Router.push("/laporan/new");
-			}
-			return;
-		}
-		toast.update(id, { render: "All is not good", type: "error", isLoading: false });
-
-		// validasi
-		// if (nama === '')  msgError.push({nama: 'Wajib diisi'})
-	};
-
-	sendNotif = async () => {
-		const { nama, no_hp, selectedPerguruanTinggi, keteranganLaporan, pelaporanNumber, selectedJenis } = this.state;
-		const description = `${selectedJenis.map((e) => e.label).join(", ")} dengan keterangan ${keteranganLaporan}`;
-		// const description = keteranganLaporan;
-		return await notifLaporanBaru({ nama, pt_name: selectedPerguruanTinggi.label, description, no_laporan: pelaporanNumber.toString() });
-	};
-
-	createUser = async () => {
-		const { nama, alamat, no_hp, email, fileIdentitas, isPrivate } = this.state;
-		const dataUser = new FormData();
-		dataUser.append("nama", nama);
-		dataUser.append("no_hp", no_hp);
-		dataUser.append("email", email);
-		dataUser.append("alamat", alamat);
-		dataUser.append("files", fileIdentitas);
-		dataUser.append("isPrivate", isPrivate);
-		const userPublic = await createPublicUser(dataUser);
-		if (userPublic.success) return userPublic.created._id;
-		return false;
-	};
-
-	createLaporan = async (user_id, nama) => {
-		const { pelaporanNumber, selectedPerguruanTinggi, keteranganLaporan, selectedJenis, files } = this.state;
+	handleKirim = async (data, { resetForm }) => {
 		const formdata = new FormData();
-		formdata.append("number", pelaporanNumber);
-		formdata.append("pt_id", selectedPerguruanTinggi.value);
-		formdata.append("user_id", user_id);
-		formdata.append("description", keteranganLaporan);
-		formdata.append("is_public", true);
-		formdata.append("nama", nama);
-		formdata.append("pelanggaran", selectedJenis.map((e) => e.value).join());
-		if (files.length > 0) {
-			Array.from(files).forEach((e) => {
-				formdata.append("files", e);
+		formdata.append("no_laporan", data.no_laporan);
+		formdata.append("pt_id", data.pt_id);
+		formdata.append("keterangan", data.keterangan);
+		formdata.append("is_private", data.is_private);
+		formdata.append("nama", data.nama);
+		formdata.append("alamat", data.alamat);
+		formdata.append("no_hp", data.no_hp);
+		formdata.append("email", data.email);
+		formdata.append("pelanggaran_id", data.pelanggaran_id.join(","));
+		formdata.append("foto", data.foto[0]);
+		if (data.dokumen.length > 0) {
+			Array.from(data.dokumen).forEach((e) => {
+				formdata.append("dokumen", e);
 			});
 		}
-		const create = await createPelaporan(formdata);
-		if (create.success) return create;
-		return false;
+
+		const toastid = toast.loading("Please wait...");
+		const success = await createLaporanPublic(formdata);
+		if (!success) {
+			toast.update(toastid, { render: "All is not good", type: "error", isLoading: false, autoClose: true, closeButton: true });
+		} else {
+			toast.update(toastid, { render: "All is good", type: "success", isLoading: false, autoClose: true, closeButton: true });
+			Router.push("/laporan/new");
+		}
 	};
 
 	render() {
-		const { selectedJenis, pelanggaran, confirm, isPrivate } = this.state;
+		const { selectedJenis, pelanggaran } = this.state;
 		return (
 			<div>
 				<ToastContainer />
@@ -185,104 +168,157 @@ class App extends Component {
 						<Col lg={8} className="block-center d-block ">
 							<Card className="card-default">
 								<CardBody>
-									<form className="form-horizontal" method="post" onSubmit={this.onSubmit}>
-										<p className="lead bb">Identitas Pelapor</p>
-										<FormGroup row>
-											<div className="col-xl-10">
-												<div className="checkbox c-checkbox">
-													<label>
-														<Input type="checkbox" onChange={(e) => this.setState({ isPrivate: !isPrivate })} defaultChecked="" />
-														<span className="fa fa-check"></span>Klik jika ingin merahasiakan identitas anda
-													</label>
+									<Formik
+										initialValues={{
+											nama: "",
+											no_hp: "",
+											email: "",
+											alamat: "",
+											foto: [],
+											no_laporan: moment(new Date()).format("DDMM") + "" + Math.floor(Math.random() * 1000000),
+											pt_id: "",
+											pelanggaran_id: "",
+											keterangan: "",
+											dokumen: [],
+											setuju: false,
+											is_private: false,
+										}}
+										validationSchema={laporanSchema}
+										onSubmit={this.handleKirim}
+									>
+										<Form className="form-horizontal">
+											<p className="lead bb">Identitas Pelapor</p>
+											<FormGroup row>
+												<div className="col-xl-10">
+													<div className="checkbox c-checkbox">
+														<label>
+															<Field name="is_private">{({ field }) => <Input type="checkbox" {...field} />}</Field>
+															<span className="fa fa-check"></span>Klik jika ingin merahasiakan identitas anda
+														</label>
+													</div>
+												</div>
+											</FormGroup>
+											<FormGroup row>
+												<label className="col-md-2 col-form-label">Nama Pelapor</label>
+												<div className="col-md-10">
+													<Field name="nama">{({ field }) => <Input type="text" {...field} />}</Field>
+													<ErrorMessage name="nama" component="div" className="form-text text-danger" />
+												</div>
+											</FormGroup>
+											<FormGroup row>
+												<label className="col-md-2 col-form-label">Nomor yang dapat dihubungi</label>
+												<div className="col-md-10">
+													<Field name="no_hp">{({ field }) => <Input type="tel" {...field} />}</Field>
+													<ErrorMessage name="no_hp" component="div" className="form-text text-danger" />
+												</div>
+											</FormGroup>
+											<FormGroup row>
+												<label className="col-md-2 col-form-label">Email</label>
+												<div className="col-md-10">
+													<Field name="email">{({ field }) => <Input type="email" {...field} />}</Field>
+													<ErrorMessage name="email" component="div" className="form-text text-danger" />
+												</div>
+											</FormGroup>
+											<FormGroup row>
+												<label className="col-md-2 col-form-label">Alamat</label>
+												<div className="col-md-10">
+													<Field name="alamat">{({ field }) => <Input type="textarea" {...field} />}</Field>
+													<ErrorMessage name="alamat" component="div" className="form-text text-danger" />
+												</div>
+											</FormGroup>
+											<FormGroup row>
+												<label className="col-md-2 col-form-label">Foto Kartu Identitas</label>
+												<div className="col-md-10">
+													<Field name="foto">{({ field, form }) => <Input type="file" onChange={(e) => form.setFieldValue(field.name, Array.from(e.currentTarget.files))} />}</Field>
+													<ErrorMessage name="foto" component="div" className="form-text text-danger" />
+												</div>
+											</FormGroup>
+											<p className="lead bb">Detail Laporan</p>
+											<FormGroup row>
+												<label className="col-md-2 col-form-label">Nomor Pelaporan</label>
+												<div className="col-md-10">
+													<Field name="no_laporan">{({ field }) => <Input disabled type="text" {...field} />}</Field>
+													<ErrorMessage name="no_laporan" component="div" className="form-text text-danger" />
+												</div>
+											</FormGroup>
+											<FormGroup row>
+												<label className="col-md-2 col-form-label">Perguruan Tinggi yang Dilaporkan</label>
+												<div className="col-md-10">
+													<Field name="pt_id">
+														{({ field, form }) => (
+															<AsyncSelect
+																cacheOptions
+																loadOptions={loadOptions}
+																defaultOptions
+																onChange={(e) => {
+																	this.handleChangeSelectPerguruanTinggi(e);
+																	form.setFieldValue(field.name, e.value);
+																}}
+																onInputChange={this.handleInputChange}
+															/>
+														)}
+													</Field>
+													<ErrorMessage name="pt_id" component="div" className="form-text text-danger" />
+												</div>
+											</FormGroup>
+											<FormGroup row>
+												<label className="col-md-2 col-form-label">Jenis Pelanggaran</label>
+												<div className="col-md-10">
+													<Field name="pelanggaran_id">
+														{({ field, form }) => (
+															<Select
+																instanceId={selectInstanceId + 1}
+																isMulti
+																value={selectedJenis}
+																onChange={(e) => {
+																	this.handleChangeSelectJenis(e);
+																	form.setFieldValue(
+																		field.name,
+																		e.map((e) => e.value)
+																	);
+																}}
+																options={pelanggaran.data ? this.optionsJenisPelanggaran(pelanggaran) : []}
+																required
+															/>
+														)}
+													</Field>
+													<ErrorMessage name="pelanggaran_id" 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" {...field} />}</Field>
+													<ErrorMessage name="keterangan" component="div" className="form-text text-danger" />
+												</div>
+											</FormGroup>
+											<FormGroup row>
+												<label className="col-md-2 col-form-label">File Pendukung</label>
+												<div className="col-md-10">
+													<Field name="dokumen">{({ field, form }) => <Input type="file" multiple onChange={(e) => form.setFieldValue(field.name, Array.from(e.currentTarget.files))} />}</Field>
+													<ErrorMessage name="dokumen" component="div" className="form-text text-danger" />
+												</div>
+											</FormGroup>
+											<FormGroup row>
+												<div className="col-xl-10">
+													<div className="checkbox c-checkbox">
+														<label>
+															<Field name="setuju">{({ field }) => <Input type="checkbox" {...field} />}</Field>
+															<span className="fa fa-check"></span>Klik jika data yang anda laporkan sudah benar
+															<ErrorMessage name="setuju" component="div" className="form-text text-danger" />
+														</label>
+													</div>
 												</div>
-											</div>
-										</FormGroup>
-										<FormGroup row>
-											<label className="col-md-2 col-form-label">Nama Pelapor</label>
-											<div className="col-md-10">
-												<Input type="text" value={this.state.nama} onChange={(e) => this.setState({ nama: e.target.value })} required />
-											</div>
-										</FormGroup>
-										<FormGroup row>
-											<label className="col-md-2 col-form-label">Nomor yang dapat dihubungi</label>
-											<div className="col-md-10">
-												<Input type="text" value={this.state.no_hp} onChange={(e) => this.setState({ no_hp: e.target.value })} required />
-											</div>
-										</FormGroup>
-										<FormGroup row>
-											<label className="col-md-2 col-form-label">Email</label>
-											<div className="col-md-10">
-												<Input type="email" value={this.state.email} onChange={(e) => this.setState({ email: e.target.value })} required />
-											</div>
-										</FormGroup>
-										<FormGroup row>
-											<label className="col-md-2 col-form-label">Alamat</label>
-											<div className="col-md-10">
-												<Input type="textarea" value={this.state.alamat} onChange={(e) => this.setState({ alamat: e.target.value })} required />
-											</div>
-										</FormGroup>
-										<FormGroup row>
-											<label className="col-md-2 col-form-label">Foto Kartu Identitas</label>
-											<div className="col-md-10">
-												<Input type="file" onChange={(e) => this.setState({ fileIdentitas: e.target.files[0] })} />
-											</div>
-										</FormGroup>
-										<p className="lead bb">Detail Laporan</p>
-										<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} />
-											</div>
-										</FormGroup>
-										<FormGroup row>
-											<label className="col-md-2 col-form-label">Perguruan Tinggi yang Dilaporkan</label>
-											<div className="col-md-10">
-												<AsyncSelect cacheOptions loadOptions={loadOptions} defaultOptions onChange={this.handleChangeSelectPerguruanTinggi} onInputChange={this.handleInputChange} required />
-											</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={selectedJenis}
-													onChange={this.handleChangeSelectJenis}
-													options={pelanggaran.data ? this.optionsJenisPelanggaran(pelanggaran) : []}
-													required
-												/>
-											</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 />
-											</div>
-										</FormGroup>
-										<FormGroup row>
-											<label className="col-md-2 col-form-label">File Pendukung</label>
-											<div className="col-md-10">
-												<Input type="file" multiple onChange={(e) => this.setState({ files: e.target.files })} />
-											</div>
-										</FormGroup>
-										<FormGroup row>
-											<div className="col-xl-10">
-												<div className="checkbox c-checkbox">
-													<label>
-														<Input type="checkbox" onChange={(e) => this.setState({ confirm: !confirm })} defaultChecked="" />
-														<span className="fa fa-check"></span>Klik jika data yang anda laporkan sudah benar
-													</label>
+											</FormGroup>
+											<FormGroup row>
+												<div className="col-12 col-lg-2">
+													<Button color="info" block type="submit">
+														Kirim Laporan
+													</Button>
 												</div>
-											</div>
-										</FormGroup>
-										<FormGroup row>
-											<div className="col-12 col-lg-2">
-												<Button color="info" block type="submit" onClick={this.handleKirim}>
-													Kirim Laporan
-												</Button>
-											</div>
-										</FormGroup>
-									</form>
+											</FormGroup>
+										</Form>
+									</Formik>
 								</CardBody>
 							</Card>
 						</Col>

+ 50 - 34
pages/pemantauan.js

@@ -7,6 +7,8 @@ import { getPelaporanPublic } from "@/actions/pelaporan";
 import DetailLaporan from "@/components/Public/DetailLaporan";
 import { getLogPublic } from "@/actions/log";
 import Timeline from "@/components/Main/Timeline";
+import { Formik, Form, Field, ErrorMessage } from "formik";
+import * as Yup from "yup";
 
 const menu = [
 	{
@@ -22,6 +24,11 @@ const menu = [
 		path: "/pemantauan",
 	},
 ];
+
+const pemantauanSchema = Yup.object().shape({
+	no_laporan: Yup.string().required("Harap Diisi"),
+	no_hp: Yup.number().required("Harap Diisi"),
+});
 class App extends Component {
 	constructor(props) {
 		super(props);
@@ -30,8 +37,8 @@ class App extends Component {
 			no_laporan: "",
 			no_hp: "",
 			msgError: [],
-			laporan: {},
-			log: {},
+			laporan: null,
+			log: null,
 		};
 	}
 
@@ -43,13 +50,11 @@ class App extends Component {
 		});
 	};
 
-	handleLihatPemantaun = async (e) => {
-		e.preventDefault();
-		const { no_hp, no_laporan } = this.state;
-		const laporan = await getPelaporanPublic({ number: no_laporan, noHp: no_hp });
-		const log = await getLogPublic({ ptId: laporan.data[0].pt_id, laporanId: laporan.data[0]._id });
-		this.setState({ laporan });
-		this.setState({ log });
+	handleLihatPemantaun = async (data) => {
+		const { no_hp, no_laporan } = data;
+		const log = await getLogPublic({ no_hp, no_laporan });
+		this.setState({ laporan: log.data.laporan });
+		this.setState({ log: log.data.pemantauan });
 	};
 
 	render() {
@@ -78,37 +83,48 @@ class App extends Component {
 						<Col lg={8} className="block-center d-block ">
 							<Card className="card-default">
 								<CardBody>
-									<form className="form-horizontal" method="post" onSubmit={this.onSubmit}>
-										<p className="lead bb">Pemantauan</p>
-										<FormGroup row>
-											<label className="col-md-2 col-form-label">Nomor Laporan</label>
-											<div className="col-md-10">
-												<Input type="text" value={this.state.no_laporan} onChange={(e) => this.setState({ no_laporan: e.target.value })} required />
-											</div>
-										</FormGroup>
-										<FormGroup row>
-											<label className="col-md-2 col-form-label">Nomor yang dapat dihubungi</label>
-											<div className="col-md-10">
-												<Input type="text" value={this.state.no_hp} onChange={(e) => this.setState({ no_hp: e.target.value })} required />
-											</div>
-										</FormGroup>
-										<FormGroup row>
-											<div className="col-12 col-lg-2">
-												<Button color="info" block type="submit" onClick={this.handleLihatPemantaun}>
-													Lihat Pemantauan
-												</Button>
-											</div>
-										</FormGroup>
-									</form>
+									<Formik
+										initialValues={{
+											no_laporan: "",
+											no_hp: "",
+										}}
+										validationSchema={pemantauanSchema}
+										onSubmit={this.handleLihatPemantaun}
+									>
+										<Form className="form-horizontal">
+											<p className="lead bb">Pemantauan</p>
+											<FormGroup row>
+												<label className="col-md-2 col-form-label">Nomor Laporan</label>
+												<div className="col-md-10">
+													<Field name="no_laporan">{({ field }) => <Input type="text" {...field} />}</Field>
+													<ErrorMessage name="no_laporan" component="div" className="form-text text-danger" />
+												</div>
+											</FormGroup>
+											<FormGroup row>
+												<label className="col-md-2 col-form-label">Nomor yang dapat dihubungi</label>
+												<div className="col-md-10">
+													<Field name="no_hp">{({ field }) => <Input type="tel" {...field} />}</Field>
+													<ErrorMessage name="no_hp" component="div" className="form-text text-danger" />
+												</div>
+											</FormGroup>
+											<FormGroup row>
+												<div className="col-12 col-lg-2">
+													<Button color="info" block type="submit">
+														Lihat Pemantauan
+													</Button>
+												</div>
+											</FormGroup>
+										</Form>
+									</Formik>
 								</CardBody>
 							</Card>
 							<Card className="card-default">
 								<CardBody>
-									{laporan.data && log.data ? (
+									{laporan && log ? (
 										<>
-											<DetailLaporan data={laporan.data[0]} />
+											<DetailLaporan data={laporan} />
 											<p className="lead bb">Pemantauan</p>
-											<Timeline data={log.data} noFile />{" "}
+											<Timeline data={log} noFile />{" "}
 										</>
 									) : (
 										"Tidak Ada Laporan"

+ 265 - 0
pages/pt/dokumen-perbaikan/detail.js

@@ -0,0 +1,265 @@
+import React, { Component } from "react";
+import Router from "next/router";
+import { getOneSanksi } from "@/actions/sanksi";
+import { addDocPerbaikan } from "@/actions/docPerbaikan";
+import Link from "next/link";
+import Header from "@/components/Main/Header";
+import DetailPT from "@/components/Main/DetailPT";
+import DetailSanksi from "@/components/Main/DetailSanksi";
+import Riwayat from "@/components/PT/DocPerbaikan/Riwayat";
+import ContentWrapper from "@/components/Layout/ContentWrapper";
+import { Row, Col, Card, CardBody, FormGroup, Button, Input } from "reactstrap";
+import { connect } from "react-redux";
+import { notifDocPerbaikan } from "@/actions/notifikasi";
+import Loader from "@/components/Common/Loader";
+import { toast } from "react-toastify";
+import { Formik, Form, Field, ErrorMessage } from "formik";
+import * as Yup from "yup";
+
+const perbaikanSchema = Yup.object().shape({
+	keterangan: Yup.string().min(3).max(200).required("Harap diisi"),
+	dokumen: Yup.array().min(1).required("Harap diisi"),
+});
+
+let Dropzone = null;
+class DropzoneWrapper extends Component {
+	state = {
+		isClient: false,
+	};
+	componentDidMount = () => {
+		Dropzone = require("react-dropzone").default;
+		this.setState({ isClient: true });
+	};
+	render() {
+		return Dropzone ? <Dropzone {...this.props}>{this.props.children}</Dropzone> : null;
+	}
+}
+
+class DetailPerbaikanDoc extends Component {
+	constructor(props) {
+		super(props);
+		this.state = {
+			files: [],
+			sanksi: {},
+			keterangan: "",
+			pt: null,
+		};
+	}
+
+	static getInitialProps = ({ query }) => ({ query });
+
+	componentDidMount = async () => {
+		const { token, query } = this.props;
+		const sanksi = await getOneSanksi(token, query.id);
+		this.setState({ sanksi, pt: sanksi.data.laporan.pt });
+	};
+
+	componentShouldUpdate(nextProps, nextState) {
+		return nextState.sanksi !== this.state.sanksi;
+	}
+
+	onDrop = (files) => {
+		this.setState({
+			files: files.map((file) =>
+				Object.assign(file, {
+					preview: URL.createObjectURL(file),
+				})
+			),
+			stat: "Added " + files.length + " file(s)",
+		});
+	};
+
+	uploadFiles = (e) => {
+		e.preventDefault();
+		e.stopPropagation();
+		this.setState({
+			stat: this.state.files.length ? "Dropzone ready to upload " + this.state.files.length + " file(s)" : "No files added.",
+		});
+	};
+
+	clearFiles = (e) => {
+		e.preventDefault();
+		e.stopPropagation();
+		this.setState({
+			stat: this.state.files.length ? this.state.files.length + " file(s) cleared." : "No files to clear.",
+		});
+		this.setState({
+			files: [],
+		});
+	};
+
+	handleKirim = async (data, { resetForm }) => {
+		const { token, query } = this.props;
+		const { id } = query;
+		const formdata = new FormData();
+		formdata.append("keterangan", data.keterangan);
+		this.state.files.forEach((e) => {
+			formdata.append("dokumen", e);
+		});
+		const toastid = toast.loading("Please wait...");
+		const added = await addDocPerbaikan(token, id, formdata);
+		if (!added) {
+			toast.update(toastid, { render: "All is not good", type: "error", isLoading: false, autoClose: true, closeButton: true });
+		} else {
+			toast.update(toastid, { render: "All is good", type: "success", isLoading: false, autoClose: true, closeButton: true });
+			const sanksi = await getOneSanksi(token, id);
+			this.setState({ sanksi, files: [] });
+			resetForm();
+			// Router.push({
+			// 	pathname: "/app/pt/dokumen-perbaikan",
+			// });
+		}
+	};
+
+	render() {
+		const { files, sanksi, pt } = this.state;
+
+		const thumbs = files.map((file, index) => (
+			<Col md={3} key={index}>
+				<img className="img-fluid mb-2" src={file.preview} alt="Item" />
+			</Col>
+		));
+
+		return (
+			<ContentWrapper unwrap>
+				{pt && <Header data={pt} />}
+				<div className="p-3">
+					<div className="content-heading">
+						<div>Dokumen Perbaikan</div>
+						<div className="ml-auto">
+							<Link href="/pt/dokumen-perbaikan">
+								<button className="btn btn-sm btn-secondary text-sm">&lt; back</button>
+							</Link>
+						</div>
+					</div>
+					<Row>
+						{sanksi.data ? (
+							<Col xl="9">
+								<Card className="card-default">
+									<CardBody>
+										<Row>
+											<Col lg={12}>
+												<DetailSanksi data={sanksi.data} />
+												<p className="lead bb">Dokumen Perbaikan</p>
+												<Formik
+													initialValues={{
+														keterangan: "",
+														dokumen: [],
+													}}
+													validationSchema={perbaikanSchema}
+													onSubmit={this.handleKirim}
+												>
+													{() => (
+														<Form className="form-horizontal">
+															<FormGroup>
+																<label className="row-form-label">Keterangan Jawaban:</label>
+																<div className="row-md-10">
+																	<Field name="keterangan">{({ field }) => <Input type="textarea" {...field} />}</Field>
+																	<ErrorMessage name="keterangan" component="div" className="form-text text-danger" />
+																	{/* <span className="form-text">Deskripsi pelaporan minimum karakter 50 maksimum 200 karakter</span> */}
+																</div>
+															</FormGroup>
+															<FormGroup>
+																<label className="row-form-label">Upload Dokumen:</label>
+																<div className="row-md-10">
+																	<Field name="dokumen">
+																		{({ field, form }) => (
+																			<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 {...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 color="primary" type="submit">
+																		Kirim
+																	</Button>
+																</div>
+															</FormGroup>
+														</Form>
+													)}
+												</Formik>
+												{/* <form className="form-horizontal" method="get" action="/" onSubmit={this.onSubmit}>
+													
+													<FormGroup>
+														<label className="row-form-label">Upload Dokumen:</label>
+														<div className="row-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>
+														<div className="row-xl-10">
+															<Button color="primary" onClick={this.handleKirim} type="submit">
+																Kirim
+															</Button>
+														</div>
+													</FormGroup>
+												</form> */}
+											</Col>
+										</Row>
+									</CardBody>
+								</Card>
+							</Col>
+						) : (
+							<Loader />
+						)}
+						<Col xl="3">{pt && <DetailPT data={pt} />}</Col>
+					</Row>
+					{sanksi.data && (
+						<Row>
+							<Col>
+								<Riwayat data={sanksi.data.perbaikan || []} />
+							</Col>
+						</Row>
+					)}
+				</div>
+			</ContentWrapper>
+		);
+	}
+}
+
+const mapStateToProps = (state) => ({ user: state.user, token: state.token });
+export default connect(mapStateToProps)(DetailPerbaikanDoc);

+ 37 - 0
pages/pt/dokumen-perbaikan/index.js

@@ -0,0 +1,37 @@
+import React, { Component } from "react";
+import ContentWrapper from "@/components/Layout/ContentWrapper";
+import { Row, Col } from "reactstrap";
+import { getSanksi } from "@/actions/sanksi";
+import TableSanksi from "@/components/PT/TableSanksi";
+import { connect } from "react-redux";
+import Loader from "@/components/Common/Loader";
+
+class Pelaporan extends Component {
+	constructor(props) {
+		super(props);
+		this.state = {
+			sanksi: {},
+		};
+	}
+
+	componentDidMount = async () => {
+		const { token } = this.props;
+		const sanksi = await getSanksi(token, { perbaikan: true });
+		this.setState({ sanksi });
+	};
+
+	render() {
+		const { sanksi } = this.state;
+		return (
+			<ContentWrapper>
+				<div className="content-heading">Dokumen Perbaikan</div>
+				<Row>
+					<Col lg={12}>{sanksi.data?.length ? <TableSanksi listData={sanksi.data} to="/pt/dokumen-perbaikan/detail" linkName="Detail" /> : sanksi.data ? "" : <Loader />}</Col>
+				</Row>
+			</ContentWrapper>
+		);
+	}
+}
+
+const mapStateToProps = (state) => ({ user: state.user, token: state.token });
+export default connect(mapStateToProps)(Pelaporan);

+ 72 - 0
pages/pt/jawaban-banding/detail.js

@@ -0,0 +1,72 @@
+import React, { Component } from "react";
+import ContentWrapper from "@/components/Layout/ContentWrapper";
+import { getOneSanksi } from "@/actions/sanksi";
+import Header from "@/components/Main/Header";
+import DetailPT from "@/components/Main/DetailPT";
+import DetailSanksi from "@/components/Main/DetailSanksi";
+import DetailJawaban from "@/components/PT/JawabanBanding/DetailJawaban";
+import Link from "next/link";
+import { Row, Col, Card, CardBody, Button } from "reactstrap";
+import { connect } from "react-redux";
+import { withRouter } from "next/router";
+import Loader from "@/components/Common/Loader";
+
+class JawabanBanding extends Component {
+	constructor(props) {
+		super(props);
+		this.state = {
+			modal: false,
+			sanksi: {},
+			pt: null,
+		};
+	}
+
+	static getInitialProps = ({ query }) => ({ query });
+
+	componentDidMount = async () => {
+		const { token, query } = this.props;
+		const sanksi = await getOneSanksi(token, query.id);
+		this.setState({ sanksi, pt: sanksi.data.laporan.pt });
+	};
+
+	render() {
+		const { sanksi, pt } = this.state;
+		return (
+			<ContentWrapper unwrap>
+				{pt && <Header data={pt} />}
+				<div className="p-3">
+					<div className="content-heading">
+						<div>Jawaban Atas Permohonan Banding</div>
+						<div className="ml-auto">
+							<Link href="/pt/jawaban-banding">
+								<button className="btn btn-sm btn-secondary text-sm">&lt; back</button>
+							</Link>
+						</div>
+					</div>
+					<Row>
+						{sanksi.data ? (
+							<Col xl="9">
+								<Card className="card-default">
+									<CardBody>
+										<Row>
+											<Col lg={12}>
+												<DetailSanksi data={sanksi.data} />
+												<DetailJawaban data={sanksi.data.jawaban.banding} />
+											</Col>
+										</Row>
+									</CardBody>
+								</Card>
+							</Col>
+						) : (
+							<Loader />
+						)}
+						<Col xl="3">{pt && <DetailPT data={pt} />}</Col>
+					</Row>
+				</div>
+			</ContentWrapper>
+		);
+	}
+}
+
+const mapStateToProps = (state) => ({ user: state.user, token: state.token });
+export default connect(mapStateToProps)(JawabanBanding);

+ 37 - 0
pages/pt/jawaban-banding/index.js

@@ -0,0 +1,37 @@
+import React, { Component } from "react";
+import ContentWrapper from "@/components/Layout/ContentWrapper";
+import { Row, Col } from "reactstrap";
+import { getSanksi } from "@/actions/sanksi";
+import TableSanksi from "@/components/PT/JawabanBanding/TableSanksiJawaban";
+import { connect } from "react-redux";
+import Loader from "@/components/Common/Loader";
+
+class JawabanKeberatan extends Component {
+	constructor(props) {
+		super(props);
+		this.state = {
+			sanksi: {},
+		};
+	}
+
+	componentDidMount = async () => {
+		const { token } = this.props;
+		const sanksi = await getSanksi(token, { banding: true });
+		this.setState({ sanksi });
+	};
+
+	render() {
+		const { sanksi } = this.state;
+		return (
+			<ContentWrapper>
+				<div className="content-heading">Jawaban Atas Permohonan Banding</div>
+				<Row>
+					<Col lg={12}>{sanksi.data?.length ? <TableSanksi listData={sanksi.data} to="/pt/jawaban-banding/detail" linkName="Detail" /> : sanksi.data ? "" : <Loader />}</Col>
+				</Row>
+			</ContentWrapper>
+		);
+	}
+}
+
+const mapStateToProps = (state) => ({ user: state.user, token: state.token });
+export default connect(mapStateToProps)(JawabanKeberatan);

+ 102 - 0
pages/pt/jawaban-keberatan/detail.js

@@ -0,0 +1,102 @@
+import React, { Component } from "react";
+import ContentWrapper from "@/components/Layout/ContentWrapper";
+import { getOneSanksi } from "@/actions/sanksi";
+import Header from "@/components/Main/Header";
+import DetailPT from "@/components/Main/DetailPT";
+import DetailSanksi from "@/components/Main/DetailSanksi";
+import DetailJawaban from "@/components/PT/JawabanKeberatan/DetailJawaban";
+import ModalPermohonan from "@/components/PT/JawabanKeberatan/ModalPermohonan";
+import Riwayat from "@/components/PT/JawabanKeberatan/Riwayat";
+import Link from "next/link";
+import { Row, Col, Card, CardBody, Button } from "reactstrap";
+import { connect } from "react-redux";
+import { withRouter } from "next/router";
+import Loader from "@/components/Common/Loader";
+
+class JawabanKeberatan extends Component {
+	constructor(props) {
+		super(props);
+		this.state = {
+			modal: false,
+			sanksi: {},
+			pt: null,
+		};
+	}
+
+	static getInitialProps = ({ query }) => ({ query });
+
+	componentDidMount = async () => {
+		const { token, query } = this.props;
+		const sanksi = await getOneSanksi(token, query.id);
+		this.setState({ sanksi, pt: sanksi.data.laporan.pt });
+	};
+
+	toggleModal = (value = true) => {
+		if (!value) {
+			this.setState({ modal: false });
+		} else {
+			this.setState({ modal: !this.state.modal });
+		}
+	};
+
+	render() {
+		const { sanksi, pt } = this.state;
+		return (
+			<ContentWrapper unwrap>
+				{sanksi.data && <ModalPermohonan toggleModal={this.toggleModal} modal={this.state.modal} query={this.props.router.query} data={sanksi.data} />}
+				{pt && <Header data={pt} />}
+				<div className="p-3">
+					<div className="content-heading">
+						<div>Jawaban Atas Permohonan Keberatan</div>
+						<div className="ml-auto">
+							<Link href="/pt/jawaban-keberatan">
+								<button className="btn btn-sm btn-secondary text-sm">&lt; back</button>
+							</Link>
+						</div>
+					</div>
+					<Row>
+						{sanksi.data ? (
+							<Col xl="9">
+								<Card className="card-default">
+									<CardBody>
+										<Row>
+											<Col lg={12}>
+												<DetailSanksi data={sanksi.data} />
+												<DetailJawaban data={sanksi.data.jawaban.keberatan} />
+												{sanksi.data.jawaban.keberatan.status !== "Membatalkan Keputusan" &&
+													(new Date(sanksi.data.batas_waktu.banding).getTime() > Date.now() ? (
+														<>
+															<p>
+																Setelah membaca jawaban permohonan keberatan atas pengenaan sanksi, jika Perguruan Tinggi bermaksud mengajukan permohonan banding kepada atasan pemberi sanksi maka dapat
+																menekan tombol di bawah ini paling lambat {moment(sanksi.data.batas_waktu.banding).format("DD MMMM YYYY")}
+															</p>
+															<Button color="primary" onClick={this.toggleModal} disabled={sanksi.data.pengajuan.banding || false}>
+																Ajukan Banding
+															</Button>
+														</>
+													) : (
+														<p>Pengajuan ditutup</p>
+													))}
+											</Col>
+										</Row>
+									</CardBody>
+								</Card>
+							</Col>
+						) : (
+							<Loader />
+						)}
+						<Col xl="3">{pt && <DetailPT data={pt} />}</Col>
+					</Row>
+					{sanksi.data && (
+						<Row>
+							<Col>{sanksi.data.jawaban.keberatan.status !== "Membatalkan Keputusan" && <Riwayat data={sanksi.data.pengajuan?.banding ? sanksi.data.pengajuan.banding : null} />}</Col>
+						</Row>
+					)}
+				</div>
+			</ContentWrapper>
+		);
+	}
+}
+
+const mapStateToProps = (state) => ({ user: state.user, token: state.token });
+export default connect(mapStateToProps)(withRouter(JawabanKeberatan));

+ 37 - 0
pages/pt/jawaban-keberatan/index.js

@@ -0,0 +1,37 @@
+import React, { Component } from "react";
+import ContentWrapper from "@/components/Layout/ContentWrapper";
+import { Row, Col } from "reactstrap";
+import { getSanksi } from "@/actions/sanksi";
+import TableSanksi from "@/components/PT/JawabanKeberatan/TableSanksiJawaban";
+import { connect } from "react-redux";
+import Loader from "@/components/Common/Loader";
+
+class JawabanKeberatan extends Component {
+	constructor(props) {
+		super(props);
+		this.state = {
+			sanksi: {},
+		};
+	}
+
+	componentDidMount = async () => {
+		const { token } = this.props;
+		const sanksi = await getSanksi(token, { keberatan: true });
+		this.setState({ sanksi });
+	};
+
+	render() {
+		const { sanksi } = this.state;
+		return (
+			<ContentWrapper>
+				<div className="content-heading">Jawaban Atas Permohonan Keberatan</div>
+				<Row>
+					<Col lg={12}>{sanksi.data?.length ? <TableSanksi listData={sanksi.data} to="/pt/jawaban-keberatan/detail" linkName="Detail" /> : sanksi.data ? "" : <Loader />} </Col>
+				</Row>
+			</ContentWrapper>
+		);
+	}
+}
+
+const mapStateToProps = (state) => ({ user: state.user, token: state.token });
+export default connect(mapStateToProps)(JawabanKeberatan);

+ 121 - 0
pages/pt/jawaban-pencabutan-sanksi/detail.js

@@ -0,0 +1,121 @@
+import React, { Component } from "react";
+import Link from "next/link";
+import { getOneSanksi } from "@/actions/sanksi";
+import Header from "@/components/Main/Header";
+import DetailPT from "@/components/Main/DetailPT";
+import DetailSanksi from "@/components/Main/DetailSanksi";
+import ContentWrapper from "@/components/Layout/ContentWrapper";
+import Scrollable from "@/components/Common/Scrollable";
+import { Row, Col, Card, CardBody, FormGroup, Button } from "reactstrap";
+import { connect } from "react-redux";
+import Loader from "@/components/Common/Loader";
+import { API_URL } from "@/env";
+class DetailJawabanPencabutanSanksi extends Component {
+	constructor(props) {
+		super(props);
+		this.state = {
+			sanksi: {},
+			pt: null,
+		};
+	}
+
+	static getInitialProps = ({ query }) => ({ query });
+
+	componentDidMount = async () => {
+		const { token, query } = this.props;
+		const sanksi = await getOneSanksi(token, query.id);
+		this.setState({ sanksi, pt: sanksi.data.laporan.pt });
+	};
+
+	render() {
+		const { sanksi, pt } = this.state;
+		const jawaban = sanksi.data?.jawaban.cabut_sanksi || null;
+		return (
+			<ContentWrapper unwrap>
+				{pt && <Header data={pt} />}
+				<div className="p-3">
+					<div className="content-heading">
+						<div>Jawaban Permohonan Pencabutan Sanksi</div>
+						<div className="ml-auto">
+							<Link href="/pt/jawaban-pencabutan-sanksi">
+								<button className="btn btn-sm btn-secondary text-sm">&lt; back</button>
+							</Link>
+						</div>
+					</div>
+					<Row>
+						{sanksi.data ? (
+							<Col xl="9">
+								<Card className="card-default">
+									<CardBody>
+										<Row>
+											<Col lg={12}>
+												<DetailSanksi data={sanksi.data} />
+												<p className="lead bb">Jawaban Permohonan Pencabutan Sanksi</p>
+												<form className="form-horizontal">
+													<FormGroup>
+														<label md="4">Jawaban:</label>
+														<div md="8">
+															<h3>{jawaban && jawaban.status}</h3>
+														</div>
+													</FormGroup>
+													{jawaban && jawaban.keterangan && (
+														<FormGroup>
+															<label md="4">Keterangan:</label>
+															<div md="8">
+																<p>{jawaban.keterangan}</p>
+															</div>
+														</FormGroup>
+													)}
+													<FormGroup>
+														<label md="4">Dokumen Jawaban:</label>
+														<div md="8">
+															<Scrollable height="120px" className="list-group">
+																<table className="table table-bordered bg-transparent">
+																	<tbody>
+																		{jawaban &&
+																			jawaban.dokumen.map((e) => (
+																				<tr>
+																					<td>
+																						<em className="fa-lg far fa-file-code"></em>
+																					</td>
+																					<td>
+																						<a className="text-muted" href={e.path} target="_blank" download={e.judul}>
+																							{e.judul}
+																						</a>
+																					</td>
+																				</tr>
+																			))}
+																	</tbody>
+																</table>
+															</Scrollable>
+														</div>
+													</FormGroup>
+												</form>
+												{jawaban && jawaban.status === "Rekomendasi Perbaikan" && (
+													<Link
+														href={{
+															pathname: "/pt/dokumen-perbaikan/detail",
+															query: { id: sanksi.data._id },
+														}}
+													>
+														<Button color="primary">Perbaiki Dokumen</Button>
+													</Link>
+												)}
+											</Col>
+										</Row>
+									</CardBody>
+								</Card>
+							</Col>
+						) : (
+							<Loader />
+						)}
+						<Col xl="3">{pt && <DetailPT data={pt} />}</Col>
+					</Row>
+				</div>
+			</ContentWrapper>
+		);
+	}
+}
+
+const mapStateToProps = (state) => ({ user: state.user, token: state.token });
+export default connect(mapStateToProps)(DetailJawabanPencabutanSanksi);

+ 37 - 0
pages/pt/jawaban-pencabutan-sanksi/index.js

@@ -0,0 +1,37 @@
+import React, { Component } from "react";
+import ContentWrapper from "@/components/Layout/ContentWrapper";
+import { Row, Col } from "reactstrap";
+import { getSanksi } from "@/actions/sanksi";
+import TableSanksi from "@/components/PT/JawabanPencabutanSanksi/TableSanksiJawaban";
+import { connect } from "react-redux";
+import Loader from "@/components/Common/Loader";
+
+class JawabanCabutSanksi extends Component {
+	constructor(props) {
+		super(props);
+		this.state = {
+			sanksi: {},
+		};
+	}
+
+	componentDidMount = async () => {
+		const { token } = this.props;
+		const sanksi = await getSanksi(token, { cabutSanksi: true });
+		this.setState({ sanksi });
+	};
+
+	render() {
+		const { sanksi } = this.state;
+		return (
+			<ContentWrapper>
+				<div className="content-heading">Jawaban Permohonan Pencabutan Sanksi</div>
+				<Row>
+					<Col lg={12}>{sanksi.data?.length ? <TableSanksi listData={sanksi.data} to="/pt/jawaban-pencabutan-sanksi/detail" linkName="Detail" /> : sanksi.data ? "" : <Loader />}</Col>
+				</Row>
+			</ContentWrapper>
+		);
+	}
+}
+
+const mapStateToProps = (state) => ({ user: state.user, token: state.token });
+export default connect(mapStateToProps)(JawabanCabutSanksi);

+ 99 - 0
pages/pt/keberatan/detail.js

@@ -0,0 +1,99 @@
+import React, { Component } from "react";
+import ContentWrapper from "@/components/Layout/ContentWrapper";
+import { getOneSanksi } from "@/actions/sanksi";
+import Header from "@/components/Main/Header";
+import DetailPT from "@/components/Main/DetailPT";
+import DetailSanksi from "@/components/Main/DetailSanksi";
+import Riwayat from "@/components/PT/Keberatan/Riwayat";
+import ModalPermohonan from "@/components/PT/Keberatan/ModalPermohonan";
+import Link from "next/link";
+import moment from "moment";
+import { Row, Col, Card, CardBody, Button } from "reactstrap";
+import { connect } from "react-redux";
+import { withRouter } from "next/router";
+import Loader from "@/components/Common/Loader";
+
+class Keberatan extends Component {
+	state = {
+		modal: false,
+		sanksi: {},
+		pt: null,
+	};
+
+	static getInitialProps = ({ query }) => ({ query });
+
+	componentDidMount = async () => {
+		const { token, query } = this.props;
+		const sanksi = await getOneSanksi(token, query.id);
+		this.setState({ sanksi, pt: sanksi.data.laporan.pt });
+	};
+
+	toggleModal = (value = true) => {
+		if (!value) {
+			this.setState({ modal: false });
+		} else {
+			this.setState({ modal: !this.state.modal });
+		}
+	};
+
+	render() {
+		const { sanksi, pt } = this.state;
+		return (
+			<ContentWrapper unwrap>
+				{sanksi.data && <ModalPermohonan toggleModal={this.toggleModal} modal={this.state.modal} query={this.props.query} data={sanksi.data} />}
+				{pt && <Header data={pt} />}
+				<div className="p-3">
+					<div className="content-heading">
+						<div>Permohonan Keberatan</div>
+						<div className="ml-auto">
+							<Link href="/pt/keberatan">
+								<button className="btn btn-sm btn-secondary text-sm">&lt; back</button>
+							</Link>
+						</div>
+					</div>
+					<Row>
+						{sanksi.data ? (
+							<Col xl="9">
+								<Card className="card-default">
+									<CardBody>
+										<Row>
+											<Col lg={12}>
+												<DetailSanksi data={sanksi.data} />
+												{new Date(sanksi.data.batas_waktu.keberatan).getTime() > Date.now() ? (
+													<>
+														<p>
+															Setelah membaca surat keputusan sanksi tersebut, jika Perguruan Tinggi bermaksud mengajukan permohonan keberatan maka dapat menekan tombol di bawah ini paling lambat{" "}
+															{moment(sanksi.data.batas_waktu.keberatan).format("DD MMMM YYYY")}
+														</p>
+														<Button color="primary" onClick={this.toggleModal} disabled={sanksi.data.pengajuan?.keberatan || false}>
+															Ajukan Permohonan Keberatan
+														</Button>
+													</>
+												) : (
+													<p>Pengajuan ditutup</p>
+												)}
+											</Col>
+										</Row>
+									</CardBody>
+								</Card>
+							</Col>
+						) : (
+							<Loader />
+						)}
+						<Col xl="3">{pt && <DetailPT data={pt} />}</Col>
+					</Row>
+					{sanksi.data && (
+						<Row>
+							<Col>
+								<Riwayat data={sanksi.data?.pengajuan?.keberatan ? sanksi.data.pengajuan.keberatan : null} />
+							</Col>
+						</Row>
+					)}
+				</div>
+			</ContentWrapper>
+		);
+	}
+}
+
+const mapStateToProps = (state) => ({ user: state.user, token: state.token });
+export default connect(mapStateToProps)(Keberatan);

+ 36 - 0
pages/pt/keberatan/index.js

@@ -0,0 +1,36 @@
+import React, { Component } from "react";
+import ContentWrapper from "@/components/Layout/ContentWrapper";
+import { Row, Col } from "reactstrap";
+import { getSanksi } from "@/actions/sanksi";
+import TableSanksi from "@/components/PT/TableSanksi";
+import { connect } from "react-redux";
+import Loader from "@/components/Common/Loader";
+class Keberatan extends Component {
+	constructor(props) {
+		super(props);
+		this.state = {
+			sanksi: {},
+		};
+	}
+
+	componentDidMount = async () => {
+		const { token } = this.props;
+		const sanksi = await getSanksi(token);
+		this.setState({ sanksi });
+	};
+
+	render() {
+		const { sanksi } = this.state;
+		return (
+			<ContentWrapper>
+				<div className="content-heading">Permohonan Keberatan</div>
+				<Row>
+					<Col lg={12}>{sanksi.data?.length ? <TableSanksi listData={sanksi.data} to="/pt/keberatan/detail" linkName="Detail" /> : sanksi.data ? "Tidak ada Sanksi" : <Loader />}</Col>
+				</Row>
+			</ContentWrapper>
+		);
+	}
+}
+
+const mapStateToProps = (state) => ({ user: state.user, token: state.token });
+export default connect(mapStateToProps)(Keberatan);

+ 51 - 0
pages/pt/pemantauan.js

@@ -0,0 +1,51 @@
+import React, { Component } from "react";
+import ContentWrapper from "@/components/Layout/ContentWrapper";
+import Header from "@/components/Main/Header";
+import DetailPT from "@/components/Main/DetailPT";
+import { getLogPT } from "@/actions/log";
+import { getPT } from "@/actions/PT";
+import { Row, Col } from "reactstrap";
+import Timeline from "@/components/Main/Timeline";
+import { connect } from "react-redux";
+import Loader from "@/components/Common/Loader";
+
+class Pemantauan extends Component {
+	constructor(props) {
+		super(props);
+		this.state = {
+			log: [],
+			pt: {},
+		};
+	}
+
+	componentDidMount = async () => {
+		const { token } = this.props;
+		const log = await getLogPT(token);
+		const pt = await getPT(token);
+		this.setState({ log, pt });
+	};
+
+	render() {
+		const { log, pt } = this.state;
+		return (
+			<ContentWrapper unwrap>
+				{pt?.data ? <Header data={pt.data} /> : <Loader />}
+				<div className="p-3">
+					<Row>
+						{log.data ? (
+							<Col xl="9">
+								<Timeline data={log.data} />
+							</Col>
+						) : (
+							<Loader />
+						)}
+						<Col xl="3">{pt.data && <DetailPT data={pt.data} />}</Col>
+					</Row>
+				</div>
+			</ContentWrapper>
+		);
+	}
+}
+
+const mapStateToProps = (state) => ({ user: state.user, token: state.token });
+export default connect(mapStateToProps)(Pemantauan);

+ 211 - 0
pages/pt/pencabutan-sanksi/detail.js

@@ -0,0 +1,211 @@
+import React, { Component } from "react";
+import Router from "next/router";
+import Link from "next/link";
+import { getOneSanksi } from "@/actions/sanksi";
+import Header from "@/components/Main/Header";
+import DetailPT from "@/components/Main/DetailPT";
+import DetailSanksi from "@/components/Main/DetailSanksi";
+import Riwayat from "@/components/PencabutanSanksi/Riwayat";
+import ContentWrapper from "@/components/Layout/ContentWrapper";
+import { Row, Col, Card, CardBody, FormGroup, Button } from "reactstrap";
+import { addCabutSanksi } from "@/actions/cabutSanksi";
+import { connect } from "react-redux";
+import Loader from "@/components/Common/Loader";
+import { toast } from "react-toastify";
+import { Formik, Form, Field, ErrorMessage } from "formik";
+import * as Yup from "yup";
+
+const docSchema = Yup.object().shape({
+	dokumen: Yup.array().min(1).required("Required"),
+});
+let Dropzone = null;
+class DropzoneWrapper extends Component {
+	state = {
+		isClient: false,
+	};
+	componentDidMount = () => {
+		Dropzone = require("react-dropzone").default;
+		this.setState({ isClient: true });
+	};
+	render() {
+		return Dropzone ? <Dropzone {...this.props}>{this.props.children}</Dropzone> : null;
+	}
+}
+
+class DetailPencabutanSanksi extends Component {
+	constructor(props) {
+		super(props);
+		this.state = {
+			files: [],
+			sanksi: {},
+			pt: null,
+			error: null,
+		};
+	}
+
+	static async getInitialProps({ query }) {
+		return { query };
+	}
+
+	componentDidMount = async () => {
+		const { token, query } = this.props;
+		const sanksi = await getOneSanksi(token, query.id);
+		this.setState({ sanksi, pt: sanksi.data.laporan.pt });
+	};
+
+	onDrop = (files) => {
+		this.setState({
+			files: files.map((file) =>
+				Object.assign(file, {
+					preview: URL.createObjectURL(file),
+				})
+			),
+			stat: "Added " + files.length + " file(s)",
+		});
+	};
+
+	uploadFiles = (e) => {
+		e.preventDefault();
+		e.stopPropagation();
+		this.setState({
+			stat: this.state.files.length ? "Dropzone ready to upload " + this.state.files.length + " file(s)" : "No files added.",
+		});
+	};
+
+	clearFiles = (e) => {
+		e.preventDefault();
+		e.stopPropagation();
+		this.setState({
+			stat: this.state.files.length ? this.state.files.length + " file(s) cleared." : "No files to clear.",
+		});
+		this.setState({
+			files: [],
+		});
+	};
+
+	handleKirim = async (data) => {
+		const { user, query, token } = this.props;
+		const formdata = new FormData();
+		data.dokumen.forEach((e) => {
+			formdata.append("dokumen", e);
+		});
+		const id = toast.loading("Please wait...");
+		const added = await addCabutSanksi(token, query.id, formdata);
+		if (!added) {
+			toast.update(id, { render: "All is not good", type: "error", isLoading: false, autoClose: true, closeButton: true });
+		} else {
+			toast.update(id, { render: "All is good", type: "success", isLoading: false, autoClose: true, closeButton: true });
+			Router.push({
+				pathname: "/pt/jawaban-pencabutan-sanksi",
+			});
+		}
+	};
+
+	render() {
+		const { files, sanksi, pt } = this.state;
+
+		const thumbs = files.map((file, index) => (
+			<Col md={3} key={index}>
+				<img className="img-fluid mb-2" src={file.preview} alt="Item" />
+			</Col>
+		));
+		return (
+			<ContentWrapper unwrap>
+				{pt && <Header data={pt} />}
+				<div className="p-3">
+					<div className="content-heading">
+						<div>Permohonan Pencabutan Sanksi</div>
+						<div className="ml-auto">
+							<Link href="/pt/pencabutan-sanksi">
+								<button className="btn btn-sm btn-secondary text-sm">&lt; back</button>
+							</Link>
+						</div>
+					</div>
+					<Row>
+						{sanksi.data ? (
+							<Col xl="9">
+								<Card className="card-default">
+									<CardBody>
+										<Row>
+											<Col lg={12}>
+												<DetailSanksi data={sanksi.data} />
+												<p className="lead bb">Permohonan Pencabutan Sanksi</p>
+												<Formik
+													initialValues={{
+														dokumen: [],
+													}}
+													validationSchema={docSchema}
+													onSubmit={this.handleKirim}
+												>
+													{() => (
+														<Form className="form-horizontal">
+															<FormGroup>
+																<label className="row-form-label">Upload Dokumen:</label>
+																<div className="row-md-10">
+																	<Field name="dokumen">
+																		{({ field, form }) => (
+																			<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 {...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>
+																<div className="row-xl-10">
+																	<Button color="primary" disabled={sanksi.data?.pengajuan?.cabut_sanksi || false} type="submit">
+																		Kirim
+																	</Button>
+																</div>
+															</FormGroup>
+														</Form>
+													)}
+												</Formik>
+											</Col>
+										</Row>
+									</CardBody>
+								</Card>
+							</Col>
+						) : (
+							<Loader />
+						)}
+						<Col xl="3">{pt && <DetailPT data={pt} />}</Col>
+					</Row>
+					{sanksi.data && (
+						<Row>
+							<Col>
+								<Riwayat data={sanksi.data?.pengajuan?.cabut_sanksi || null} />
+							</Col>
+						</Row>
+					)}
+				</div>
+			</ContentWrapper>
+		);
+	}
+}
+
+const mapStateToProps = (state) => ({ user: state.user, token: state.token });
+export default connect(mapStateToProps)(DetailPencabutanSanksi);

+ 37 - 0
pages/pt/pencabutan-sanksi/index.js

@@ -0,0 +1,37 @@
+import React, { Component } from "react";
+import ContentWrapper from "@/components/Layout/ContentWrapper";
+import { Row, Col } from "reactstrap";
+import { getSanksi } from "@/actions/sanksi";
+import TableSanksi from "@/components/PT/TableSanksi";
+import { connect } from "react-redux";
+import Loader from "@/components/Common/Loader";
+
+class PencabutanSanksi extends Component {
+	constructor(props) {
+		super(props);
+		this.state = {
+			sanksi: {},
+		};
+	}
+
+	componentDidMount = async () => {
+		const { token } = this.props;
+		const sanksi = await getSanksi(token);
+		this.setState({ sanksi });
+	};
+
+	render() {
+		const { sanksi } = this.state;
+		return (
+			<ContentWrapper>
+				<div className="content-heading">Permohonan Pencabutan Sanksi</div>
+				<Row>
+					<Col lg={12}>{sanksi.data?.length ? <TableSanksi listData={sanksi.data} to="/pt/pencabutan-sanksi/detail" linkName="Detail" /> : sanksi.data ? "Tidak ada Sanksi" : <Loader />}</Col>
+				</Row>
+			</ContentWrapper>
+		);
+	}
+}
+
+const mapStateToProps = (state) => ({ user: state.user, token: state.token });
+export default connect(mapStateToProps)(PencabutanSanksi);

+ 11 - 0
store/actions/token.js

@@ -0,0 +1,11 @@
+export const LOGIN_SUCCESS = "LOGIN_SUCCESS";
+export const LOGIN_FAIL = "LOGIN_FAIL";
+
+
+export const login = (username, password) => (dispatch) => {
+	let data = dataUser.filter((value) => value.username === username && value.password === password);
+	if (!data.length) dispatch({ type: LOGIN_FAIL });
+	data = data[0];
+	localStorage.setItem("userInfo", JSON.stringify(data));
+	dispatch({ type: LOGIN_SUCCESS, payload: data });
+};

+ 0 - 1
store/actions/user.js

@@ -1,7 +1,6 @@
 export const LOGIN_SUCCESS = "LOGIN_SUCCESS";
 export const LOGIN_FAIL = "LOGIN_FAIL";
 
-import dataUser from "@/json/dataUser";
 
 export const login = (username, password) => (dispatch) => {
 	let data = dataUser.filter((value) => value.username === username && value.password === password);

+ 2 - 0
store/reducers/reducers.js

@@ -7,6 +7,7 @@ import { pelaporanListReducer, pelaporanCreateReducer } from "./pelaporan.reduce
 import { penjadwalanCreateReducer } from "./penjadwalan.reducers.js";
 import { userReducer } from "./user.reducer";
 import { ptReducer } from "./pt.reducer";
+import { tokenReducer } from "./token.reducer";
 
 export default combineReducers({
 	settings: settingsReducer,
@@ -17,4 +18,5 @@ export default combineReducers({
 	penjadwalanAdd: penjadwalanCreateReducer,
 	user: userReducer,
 	pt: ptReducer,
+	token: tokenReducer,
 });

+ 8 - 0
store/reducers/token.reducer.js

@@ -0,0 +1,8 @@
+export const tokenReducer = (state = {}, action) => {
+	switch (action.type) {
+		case "SET_TOKEN":
+			return (state = action.payload);
+		default:
+			return state;
+	}
+};