andifebri 3 년 전
부모
커밋
f3801ac21e

+ 197 - 0
pages/app/pt/dokumen-perbaikan/detail.js

@@ -0,0 +1,197 @@
+import React, { Component } from "react";
+import Router from "next/router";
+import { getSanksi } 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";
+
+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: "",
+		};
+	}
+
+	static getInitialProps = ({ query }) => ({ query });
+
+	componentDidMount = async () => {
+		const { user } = this.props;
+		const { noSanksi } = this.props.query;
+		const sanksi = await getSanksi({ noSanksi, ptId: user.peran[0].organisasi.id });
+		this.setState({ 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 (e) => {
+		e.preventDefault();
+		const { keterangan, sanksi } = this.state;
+		const { noSanksi } = this.props.query;
+		const { user } = this.props;
+		const org_id = user.peran[0].organisasi.id;
+		const formdata = new FormData();
+		formdata.append("description", keterangan);
+		if (this.state.files.length > 0) {
+			this.state.files.forEach((e) => {
+				formdata.append("files", e);
+			});
+			const id = toast.loading("Please wait...");
+			const added = await addDocPerbaikan({ noSanksi, ptId: org_id }, formdata);
+			if (added) {
+				await notifDocPerbaikan({ lembaga: sanksi.data[0].sanksi.user.lembaga, pt_name: user.peran[0].organisasi.nama, no_sanksi: sanksi.data[0].sanksi.no_sanksi });
+				toast.update(id, { render: "All is good", type: "success", isLoading: false, autoClose: true, closeButton: true });
+				Router.push({
+					pathname: "/app/pt/dokumen-perbaikan",
+				});
+			}
+		}
+	};
+
+	render() {
+		const { files, sanksi } = 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>
+				<Header data={this.props.pt[0]} />
+				<div className="p-3">
+					<div className="content-heading">
+						<div>Dokumen Perbaikan</div>
+						<div className="ml-auto">
+							<Link href="/app/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[0]} />
+												<p className="lead bb">Dokumen Perbaikan</p>
+												<form className="form-horizontal" method="get" action="/" onSubmit={this.onSubmit}>
+													<FormGroup>
+														<label className="row-form-label">Keterangan:</label>
+														<div className="row-md-10">
+															<Input type="textarea" value={this.state.keterangan} onChange={(e) => this.setState({ keterangan: e.target.value })} required />
+														</div>
+													</FormGroup>
+													<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">{this.props.pt && <DetailPT data={this.props.pt[0]} />}</Col>
+					</Row>
+					{sanksi?.data && (
+						<Row>
+							<Col>
+								<Riwayat data={sanksi.data[0].sanksi.doc_perbaikan} />
+							</Col>
+						</Row>
+					)}
+				</div>
+			</ContentWrapper>
+		);
+	}
+}
+
+const mapStateToProps = (state) => ({ user: state.user, pt: state.pt });
+export default connect(mapStateToProps)(DetailPerbaikanDoc);

+ 38 - 0
pages/app/pt/dokumen-perbaikan/index.js

@@ -0,0 +1,38 @@
+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 { user } = this.props;
+		const org_id = user.peran[0].organisasi.id;
+		const sanksi = await getSanksi({ ptId: org_id });
+		this.setState({ sanksi });
+	};
+
+	render() {
+		const { sanksi } = this.state;
+		return (
+			<ContentWrapper>
+				<div className="content-heading">Dokumen Perbaikan</div>
+				<Row>
+					<Col lg={12}>{sanksi.data && sanksi.data.length > 0 ? <TableSanksi listData={sanksi.data} to="/app/pt/dokumen-perbaikan/detail" linkName="Detail" /> : sanksi.data ? 'Tidak ada Sanksi' : <Loader />}</Col>
+				</Row>
+			</ContentWrapper>
+		);
+	}
+}
+
+const mapStateToProps = (state) => ({ user: state.user });
+export default connect(mapStateToProps)(Pelaporan);

+ 70 - 0
pages/app/pt/jawaban-banding/detail.js

@@ -0,0 +1,70 @@
+import React, { Component } from "react";
+import ContentWrapper from "@/components/Layout/ContentWrapper";
+import { getSanksi } 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: {},
+		};
+	}
+
+	componentDidMount = async () => {
+		const { user } = this.props;
+		const { noSanksi } = this.props.router.query;
+		const sanksi = await getSanksi({ noSanksi, ptId: user.peran[0].organisasi.id });
+		this.setState({ sanksi });
+	};
+
+	render() {
+		const { sanksi } = this.state;
+		return (
+			<ContentWrapper unwrap>
+				<Header data={this.props.pt[0]} />
+				<div className="p-3">
+					<div className="content-heading">
+						<div>Jawaban Atas Permohonan Banding</div>
+						<div className="ml-auto">
+							<Link href="/app/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[0]} />
+												<DetailJawaban data={sanksi.data[0]} />
+											</Col>
+										</Row>
+									</CardBody>
+								</Card>
+							</Col>
+						) : (
+							<Loader />
+						)}
+						<Col xl="3">{this.props.pt && <DetailPT data={this.props.pt[0]} />}</Col>
+					</Row>
+				</div>
+			</ContentWrapper>
+		);
+	}
+}
+
+const mapStateToProps = (state) => ({ user: state.user, pt: state.pt });
+export default connect(mapStateToProps)(withRouter(JawabanBanding));

+ 38 - 0
pages/app/pt/jawaban-banding/index.js

@@ -0,0 +1,38 @@
+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 { user } = this.props;
+		const org_id = user.peran[0].organisasi.id;
+		const sanksi = await getSanksi({ banding: true, ptId: org_id });
+		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 && sanksi.data.length ? <TableSanksi listData={sanksi.data} to="/app/pt/jawaban-banding/detail" linkName="Detail" /> : sanksi.data ? '' : <Loader />}</Col>
+				</Row>
+			</ContentWrapper>
+		);
+	}
+}
+
+const mapStateToProps = (state) => ({ user: state.user });
+export default connect(mapStateToProps)(JawabanKeberatan);

+ 100 - 0
pages/app/pt/jawaban-keberatan/detail.js

@@ -0,0 +1,100 @@
+import React, { Component } from "react";
+import ContentWrapper from "@/components/Layout/ContentWrapper";
+import { getSanksi } 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: {},
+		};
+	}
+
+	componentDidMount = async () => {
+		const { user } = this.props;
+		const { noSanksi } = this.props.router.query;
+		const sanksi = await getSanksi({ noSanksi, ptId: user.peran[0].organisasi.id });
+		this.setState({ sanksi });
+	};
+
+	toggleModal = (value = true) => {
+		if (!value) {
+			this.setState({ modal: false });
+		} else {
+			this.setState({ modal: !this.state.modal });
+		}
+	};
+
+	render() {
+		const { sanksi } = this.state;
+		return (
+			<ContentWrapper unwrap>
+				{sanksi?.data && <ModalPermohonan toggleModal={this.toggleModal} modal={this.state.modal} query={this.props.router.query} data={sanksi.data[0]} />}
+				<Header data={this.props.pt[0]} />
+				<div className="p-3">
+					<div className="content-heading">
+						<div>Jawaban Atas Permohonan Keberatan</div>
+						<div className="ml-auto">
+							<Link href="/app/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[0]} />
+												<DetailJawaban data={sanksi.data[0]} />
+												{sanksi.data[0].sanksi.keberatan.jawaban.status !== "Membatalkan Keputusan" &&
+													(new Date(sanksi.data[0].sanksi.close_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[0].sanksi.close_banding).format("DD MMMM YYYY")}
+															</p>
+															<Button color="primary" onClick={this.toggleModal} disabled={sanksi.data[0].sanksi.banding || false}>
+																Ajukan Banding
+															</Button>
+														</>
+													) : (
+														<p>Pengajuan ditutup</p>
+													))}
+											</Col>
+										</Row>
+									</CardBody>
+								</Card>
+							</Col>
+						) : (
+							<Loader />
+						)}
+						<Col xl="3">{this.props.pt && <DetailPT data={this.props.pt[0]} />}</Col>
+					</Row>
+					{sanksi?.data && (
+						<Row>
+							<Col>{sanksi.data[0].sanksi.keberatan.jawaban.status !== "Membatalkan Keputusan" && <Riwayat data={sanksi.data[0]} />}</Col>
+						</Row>
+					)}
+				</div>
+			</ContentWrapper>
+		);
+	}
+}
+
+const mapStateToProps = (state) => ({ user: state.user, pt: state.pt });
+export default connect(mapStateToProps)(withRouter(JawabanKeberatan));

+ 38 - 0
pages/app/pt/jawaban-keberatan/index.js

@@ -0,0 +1,38 @@
+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 { user } = this.props;
+		const org_id = user.peran[0].organisasi.id;
+		const sanksi = await getSanksi({ keberatan: true, ptId: org_id });
+		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 && sanksi.data.length ? <TableSanksi listData={sanksi.data} to="/app/pt/jawaban-keberatan/detail" linkName="Detail" /> : sanksi.data ? '' : <Loader />} </Col>
+				</Row>
+			</ContentWrapper>
+		);
+	}
+}
+
+const mapStateToProps = (state) => ({ user: state.user });
+export default connect(mapStateToProps)(JawabanKeberatan);

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

@@ -0,0 +1,122 @@
+import React, { Component } from "react";
+import Link from "next/link";
+import { getSanksi } 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: {},
+		};
+	}
+
+	static async getInitialProps({ query }) {
+		return { query };
+	}
+
+	componentDidMount = async () => {
+		const { user } = this.props;
+		const { noSanksi } = this.props.query;
+		const sanksi = await getSanksi({ noSanksi, ptId: user.peran[0].organisasi.id });
+		console.log(sanksi);
+		this.setState({ sanksi });
+	};
+
+	render() {
+		const { sanksi } = this.state;
+		const { jawaban } = (sanksi.data && sanksi.data[0].sanksi.cabut_sanksi) || {};
+		return (
+			<ContentWrapper unwrap>
+				<Header data={this.props.pt[0]} />
+				<div className="p-3">
+					<div className="content-heading">
+						<div>Jawaban Permohonan Pencabutan Sanksi</div>
+						<div className="ml-auto">
+							<Link href="/app/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[0]} />
+												<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>
+													<FormGroup>
+														<label md="4">Keterangan:</label>
+														<div md="8">
+															<p>{jawaban && jawaban.description}</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.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>
+														</div>
+													</FormGroup>
+												</form>
+												{jawaban && jawaban.status === "Rekomendasi Perbaikan" && (
+													<Link
+														href={{
+															pathname: "/app/pt/dokumen-perbaikan/detail",
+															query: { noSanksi: sanksi.data[0].sanksi.no_sanksi },
+														}}
+													>
+														<Button color="primary">Perbaiki Dokumen</Button>
+													</Link>
+												)}
+											</Col>
+										</Row>
+									</CardBody>
+								</Card>
+							</Col>
+						) : (
+							<Loader />
+						)}
+						<Col xl="3">{this.props.pt && <DetailPT data={this.props.pt[0]} />}</Col>
+					</Row>
+				</div>
+			</ContentWrapper>
+		);
+	}
+}
+
+const mapStateToProps = (state) => ({ user: state.user, pt: state.pt });
+export default connect(mapStateToProps)(DetailJawabanPencabutanSanksi);

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

@@ -0,0 +1,38 @@
+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 { user } = this.props;
+		const org_id = user.peran[0].organisasi.id;
+		const sanksi = await getSanksi({ cabutSanksi: true, ptId: org_id });
+		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 && sanksi.data.length ? <TableSanksi listData={sanksi.data} to="/app/pt/jawaban-pencabutan-sanksi/detail" linkName="Detail" /> : sanksi.data ? '' : <Loader />}</Col>
+				</Row>
+			</ContentWrapper>
+		);
+	}
+}
+
+const mapStateToProps = (state) => ({ user: state.user });
+export default connect(mapStateToProps)(JawabanCabutSanksi);

+ 97 - 0
pages/app/pt/keberatan/detail.js

@@ -0,0 +1,97 @@
+import React, { Component } from "react";
+import ContentWrapper from "@/components/Layout/ContentWrapper";
+import { getSanksi } 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: {},
+	};
+
+	componentDidMount = async () => {
+		const { user } = this.props;
+		const { noSanksi } = this.props.router.query;
+		const sanksi = await getSanksi({ noSanksi, ptId: user.peran[0].organisasi.id });
+		this.setState({ sanksi });
+	};
+
+	toggleModal = (value = true) => {
+		if (!value) {
+			this.setState({ modal: false });
+		} else {
+			this.setState({ modal: !this.state.modal });
+		}
+	};
+
+	render() {
+		const { sanksi } = this.state;
+		return (
+			<ContentWrapper unwrap>
+				{sanksi?.data && <ModalPermohonan toggleModal={this.toggleModal} modal={this.state.modal} query={this.props.router.query} data={sanksi.data[0]} />}
+				<Header data={this.props.pt[0]} />
+				<div className="p-3">
+					<div className="content-heading">
+						<div>Permohonan Keberatan</div>
+						<div className="ml-auto">
+							<Link href="/app/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[0]} />
+												{new Date(sanksi.data[0].sanksi.close_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[0].sanksi.close_keberatan).format("DD MMMM YYYY")}
+														</p>
+														<Button color="primary" onClick={this.toggleModal} disabled={sanksi.data[0].sanksi.keberatan || false}>
+															Ajukan Permohonan Keberatan
+														</Button>
+													</>
+												) : (
+													<p>Pengajuan ditutup</p>
+												)}
+											</Col>
+										</Row>
+									</CardBody>
+								</Card>
+							</Col>
+						) : (
+							<Loader />
+						)}
+						<Col xl="3">{this.props.pt && <DetailPT data={this.props.pt[0]} />}</Col>
+					</Row>
+					{sanksi?.data && (
+						<Row>
+							<Col>
+								<Riwayat data={sanksi.data[0]} />
+							</Col>
+						</Row>
+					)}
+				</div>
+			</ContentWrapper>
+		);
+	}
+}
+
+const mapStateToProps = (state) => ({ user: state.user, pt: state.pt });
+export default connect(mapStateToProps)(withRouter(Keberatan));

+ 37 - 0
pages/app/pt/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/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 { user } = this.props;
+		const org_id = user.peran[0].organisasi.id;
+		const sanksi = await getSanksi({ ptId: org_id });
+		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="/app/pt/keberatan/detail" linkName="Detail" /> : sanksi.data ? 'Tidak ada Sanksi' : <Loader />}</Col>
+				</Row>
+			</ContentWrapper>
+		);
+	}
+}
+
+const mapStateToProps = (state) => ({ user: state.user });
+export default connect(mapStateToProps)(Keberatan);

+ 51 - 0
pages/app/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 { getLog } from "@/actions/log";
+import { getPelaporan } from "@/actions/pelaporan";
+import { Row, Col } from "reactstrap";
+import Timeline from "@/components/PT/Timeline";
+import { connect } from "react-redux";
+import Loader from "@/components/Common/Loader";
+
+class Pemantauan extends Component {
+	constructor(props) {
+		super(props);
+		this.state = {
+			log: [],
+			pelaporan: {},
+		};
+	}
+
+	componentDidMount = async () => {
+		const { user } = this.props;
+		const log = await getLog({ ptId: user.peran[0].organisasi.id, isPT: true });
+		const pelaporan = await getPelaporan({ ptId: user.peran[0].organisasi.id });
+		this.setState({ log, pelaporan });
+	};
+
+	render() {
+		const { log, pelaporan } = this.state;
+		return (
+			<ContentWrapper unwrap>
+				<Header data={this.props.pt[0]} />
+				<div className="p-3">
+					<Row>
+						{log && pelaporan.data ? (
+							<Col xl="9">
+								<Timeline data={log} dataPelaporan={pelaporan.data[0]} />
+							</Col>
+						) : (
+							<Loader />
+						)}
+						<Col xl="3">{this.props.pt && <DetailPT data={this.props.pt[0]} />}</Col>
+					</Row>
+				</div>
+			</ContentWrapper>
+		);
+	}
+}
+
+const mapStateToProps = (state) => ({ user: state.user, pt: state.pt });
+export default connect(mapStateToProps)(Pemantauan);

+ 186 - 0
pages/app/pt/pencabutan-sanksi/detail.js

@@ -0,0 +1,186 @@
+import React, { Component } from "react";
+import Router from "next/router";
+import Link from "next/link";
+import { getSanksi } 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/CabutSanksi/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";
+
+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: [],
+		};
+	}
+
+	static async getInitialProps({ query }) {
+		return { query };
+	}
+
+	componentDidMount = async () => {
+		const { user } = this.props;
+		const { noSanksi } = this.props.query;
+		const sanksi = await getSanksi({ noSanksi, ptId: user.peran[0].organisasi.id });
+		this.setState({ 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 (e) => {
+		e.preventDefault();
+		const { noSanksi } = this.props.query;
+		const { user } = this.props;
+		const org_id = user.peran[0].organisasi.id;
+		const formdata = new FormData();
+		if (this.state.files.length > 0) {
+			this.state.files.forEach((e) => {
+				formdata.append("files", e);
+			});
+			const id = toast.loading("Please wait...");
+			const added = await addCabutSanksi({ noSanksi, ptId: org_id }, formdata);
+			if (added) {
+				toast.update(id, { render: "All is good", type: "success", isLoading: false, autoClose: true, closeButton: true });
+				Router.push({
+					pathname: "/app/pt/pencabutan-sanksi",
+				});
+			}
+		}
+	};
+
+	render() {
+		const { files, sanksi } = 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>
+				<Header data={this.props.pt[0]} />
+				<div className="p-3">
+					<div className="content-heading">
+						<div>Permohonan Pencabutan Sanksi</div>
+						<div className="ml-auto">
+							<Link href="/app/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[0]} />
+												<p className="lead bb">Permohonan Pencabutan Sanksi</p>
+												<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} disabled={sanksi.data[0].sanksi.cabut_sanksi || false} type="submit">
+																Kirim
+															</Button>
+														</div>
+													</FormGroup>
+												</form>
+											</Col>
+										</Row>
+									</CardBody>
+								</Card>
+							</Col>
+						) : (
+							<Loader />
+						)}
+						<Col xl="3">{this.props.pt && <DetailPT data={this.props.pt[0]} />}</Col>
+					</Row>
+					{sanksi?.data && (
+						<Row>
+							<Col>
+								<Riwayat data={sanksi.data[0]} />
+							</Col>
+						</Row>
+					)}
+				</div>
+			</ContentWrapper>
+		);
+	}
+}
+
+const mapStateToProps = (state) => ({ user: state.user, pt: state.pt });
+export default connect(mapStateToProps)(DetailPencabutanSanksi);

+ 38 - 0
pages/app/pt/pencabutan-sanksi/index.js

@@ -0,0 +1,38 @@
+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 { user } = this.props;
+		const org_id = user.peran[0].organisasi.id;
+		const sanksi = await getSanksi({ ptId: org_id });
+		this.setState({ sanksi });
+	};
+
+	render() {
+		const { sanksi } = this.state;
+		return (
+			<ContentWrapper>
+				<div className="content-heading">Permohonan Pencabutan Sanksi</div>
+				<Row>
+					<Col lg={12}>{sanksi.data && sanksi.data.length ? <TableSanksi listData={sanksi.data} to="/app/pt/pencabutan-sanksi/detail" linkName="Detail" /> : sanksi.data ? 'Tidak ada Sanksi' : <Loader />}</Col>
+				</Row>
+			</ContentWrapper>
+		);
+	}
+}
+
+const mapStateToProps = (state) => ({ user: state.user });
+export default connect(mapStateToProps)(PencabutanSanksi);

BIN
public/static/img/icon-menu-penjadwalan.png


BIN
public/static/img/icon-menupemantauan.png


BIN
public/static/img/icon-pelaporan.png