diff --git a/src/views/SimproV2/Kanban/Column.js b/src/views/SimproV2/Kanban/Column.js
new file mode 100644
index 0000000..8ff0b73
--- /dev/null
+++ b/src/views/SimproV2/Kanban/Column.js
@@ -0,0 +1,130 @@
+import React, { Fragment } from 'react'
+import './Kanban.css';
+import { Card, Row, Col, Button, Avatar, Tooltip, Popover, Divider, Space, Menu, Dropdown } from 'antd';
+import Task from './Task';
+import { Droppable } from 'react-beautiful-dnd';
+import styled from 'styled-components'
+import {
+ AntDesignOutlined,
+ UserOutlined,
+ EditOutlined,
+ DeleteOutlined,
+ SettingOutlined,
+ EllipsisOutlined,
+ MoreOutlined
+ } from '@ant-design/icons';
+
+const Container = styled.div`
+ margin: 8px;
+ border: 1px solid lightgrey;
+ border-radius: 2px;
+ width: 220px;
+
+ display: flex;
+ flex-direction: column;
+`
+
+const TaskList = styled.div`
+ transition: background-color 0.2s ease;
+ background-color: ${props =>
+ props.isDraggingOver ? 'skyblue' : 'white'}
+ flex-grow: 1;
+ min-height: 100px;
+`
+
+const Column = ({
+ column,
+ tasks,
+ index,
+ handleOpenDialogActivity,
+ handleOpenDialogChild,
+ handleDelete,
+ handleEditBoard,
+ loadingCard,
+ handleDeleteCard,
+ handleEditCard
+}) => {
+
+
+ const menu = (column) => (
+
+ )
+ }
+
+
+ return (
+ <>
+ handleCancel()} open={openDialogActivity}>
+
+
+
+
+
+
+
+
+
+
+
+ {renderForm()}
+
+
+
+
+ >
+ )
+
+}
+
+export default DialogFormActivity;
diff --git a/src/views/SimproV2/Kanban/DialogFormBoard.js b/src/views/SimproV2/Kanban/DialogFormBoard.js
new file mode 100644
index 0000000..000b840
--- /dev/null
+++ b/src/views/SimproV2/Kanban/DialogFormBoard.js
@@ -0,0 +1,176 @@
+import React, { useEffect, useState } from 'react'
+import {
+ Modal, ModalHeader, ModalBody, ModalFooter,
+ Form, FormGroup, Label, Input, Col, Row, FormFeedback
+} from 'reactstrap';
+import { DatePicker, Tooltip, Select, Drawer, Divider, Layout, Button, Space } from 'antd';
+import 'antd/dist/antd.css';
+import {
+ CloseOutlined
+} from '@ant-design/icons';
+const { Option } = Select
+const { Header, Footer, Sider, Content } = Layout;
+
+
+const DialogFormBoard = ({ openDialogBoard, closeDialogBoard, toggleDialogBoard, typeDialogBoard, dataEditBoard, proyek_id, version_gantt_id }) => {
+ const [id, setId] = useState(0)
+ const [nameBoard, setNameBoard] = useState('')
+ const [statusProgress, setStatusProgress] = useState('none')
+ const [headerColor, setHeaderColor] = useState('#000000')
+ const [bodyColor, setBodyColor] = useState('#ffffff')
+
+ // validation
+ const [nameBoardVal, setNameBoardVal] = useState(false)
+
+
+ useEffect(() => {
+
+ setNameBoard('')
+ setStatusProgress('')
+ setHeaderColor('#000000')
+ setBodyColor('#ffffff')
+
+ if (typeDialogBoard === "Edit") {
+ setId(dataEditBoard.id)
+ setNameBoard(dataEditBoard.name_board)
+ setHeaderColor(dataEditBoard.header_color)
+ setBodyColor(dataEditBoard.body_color)
+ setStatusProgress(dataEditBoard.status_progress)
+ } else {
+ setId(0)
+ }
+ }, [dataEditBoard, openDialogBoard])
+
+ const handleCancel = () => {
+ closeDialogBoard('cancel', 'none')
+ }
+
+ const handleSave = () => {
+ let data = '';
+
+ if (!nameBoard || nameBoard === "") {
+ alert("nameBoard cannot be empty!");
+ return false;
+ }
+ if (!statusProgress || statusProgress === "") {
+ alert("statusProgress cannot be empty!");
+ return false;
+ }
+
+ if (typeDialogBoard === "Save") {
+ data = {
+ name_board: nameBoard,
+ status_progress: statusProgress,
+ header_color: headerColor,
+ body_color: bodyColor,
+ proyek_id: proyek_id,
+ version_gantt_id: version_gantt_id
+ }
+ closeDialogBoard('save', data);
+ } else if (typeDialogBoard === "Edit") {
+ data = {
+ id: id,
+ name_board: nameBoard,
+ status_progress: statusProgress,
+ header_color: headerColor,
+ body_color: bodyColor,
+ proyek_id: proyek_id,
+ version_gantt_id: version_gantt_id
+ }
+ closeDialogBoard('edit', data);
+ }
+
+ setNameBoard('')
+ setStatusProgress('')
+ setHeaderColor('#000000')
+ setBodyColor('#ffffff')
+ }
+
+ const onChangeStatusProgress = (val) => {
+ setStatusProgress(val)
+ };
+
+ const renderForm = () => {
+ return (
+
+ )
+ }
+
+
+ return (
+ <>
+ handleCancel()} open={openDialogBoard}>
+
+
+
+
+
+
+
+
+
+
+
+ {renderForm()}
+
+
+
+
+ >
+ )
+
+}
+
+export default DialogFormBoard;
diff --git a/src/views/SimproV2/Kanban/DialogFormChild.js b/src/views/SimproV2/Kanban/DialogFormChild.js
new file mode 100644
index 0000000..b820fcc
--- /dev/null
+++ b/src/views/SimproV2/Kanban/DialogFormChild.js
@@ -0,0 +1,69 @@
+import React, { useEffect, useState } from 'react'
+import {
+ Modal, ModalHeader, ModalBody, ModalFooter,
+ Button, Form, FormGroup, Label, Input, Col, Row
+} from 'reactstrap';
+import { DatePicker, Tooltip, Select } from 'antd';
+import { formatRupiah, formatNumber } from '../../../const/CustomFunc'
+import moment from 'moment';
+import 'antd/dist/antd.css';
+const { Option } = Select
+
+const DialogFormChild = ({ openDialogChild, closeDialogChild, toggleDialogChild, typeDialogChild }) => {
+ const [activity, setActivity] = useState('')
+ const [name, setName] = useState('')
+
+ useEffect(() => {
+
+ }, [openDialogChild])
+
+ const handleCancel = () => {
+ closeDialogChild('cancel', 'none')
+ }
+
+ const handleSave = () => {
+ let data = '';
+ if (typeDialogChild === "Save") {
+ data = {
+ name,
+ }
+ closeDialogChild('save', data);
+ }
+
+ setActivity('')
+ }
+
+ const renderForm = () => {
+ return (
+
+ )
+ }
+
+
+ return (
+ <>
+
+ {typeDialogChild == "Edit" ? `Edit` : "Add"} Child
+
+ {renderForm()}
+
+
+ {' '}
+
+
+
+ >
+ )
+
+}
+
+export default DialogFormChild;
diff --git a/src/views/SimproV2/Kanban/DialogFormReport.js b/src/views/SimproV2/Kanban/DialogFormReport.js
new file mode 100644
index 0000000..295e6e9
--- /dev/null
+++ b/src/views/SimproV2/Kanban/DialogFormReport.js
@@ -0,0 +1,438 @@
+import React, { useEffect, useState } from 'react'
+import {
+ Modal, ModalHeader, ModalBody, ModalFooter,
+ Form, FormGroup, Label, Input, Col, Row
+} from 'reactstrap';
+import { DatePicker, Table, Drawer, Divider, Tooltip, Button, Space, Progress } from 'antd';
+import {
+ CloseOutlined,
+ MinusOutlined,
+ PlusOutlined
+} from '@ant-design/icons';
+import {
+ NotificationManager
+} from "react-notifications";
+import SweetAlert from 'react-bootstrap-sweetalert';
+import { formatRupiah, formatNumber } from '../../../const/CustomFunc'
+import moment from 'moment';
+import Select from 'react-select';
+import 'antd/dist/antd.css';
+import axios from "../../../const/interceptorApi";
+import { BASE_SIMPRO_LUMEN } from '../../../const/ApiConst';
+// const { Option } = Select
+
+const DialogFormReport = ({
+ activityProject,
+ openDialogReport,
+ closeDialogReport,
+ toggleDialogActivity,
+ typeDialogReport,
+ proyek_id,
+ version_gantt_id,
+ idBoard,
+ dataHr,
+ dataEditCard,
+ userToActivityDelete,
+ userToActivityAdd
+}) => {
+ const [id, setId] = useState('')
+ const [idDelete, setIdDelete] = useState('')
+ const [alertDelete, setAlertDelete] = useState(false)
+ const [text, setText] = useState('')
+ const [description, setDescription] = useState('')
+ const [startDate, setStartDate] = useState('')
+ const [reportDate, setReportDate] = useState('')
+ const [progress, setProgress] = useState(0)
+ const [volumePlan, setVolumePlan] = useState(0)
+ const [volumeActual, setVolumeActual] = useState(0)
+ const [nativeeditorStatus, setNativeeditorStatus] = useState('')
+ const [hr, setHr] = useState(null);
+ const [hrTemporary, setHrTemporary] = useState();
+ const [hrTemporaryAdd, setHrTemporaryAdd] = useState([]);
+ const [IdDeleteHrTemporary, setIdDeleteHrTemporary] = useState([]);
+ const [reportActivity, setReportActivity] = useState([])
+ const token = localStorage.getItem("token");
+
+ const HEADER = {
+ headers: {
+ "Content-Type": "application/json",
+ Authorization: `Bearer ${token}`,
+ },
+ };
+ useEffect(() => {
+
+ setText('')
+ setStartDate('')
+ setProgress(0)
+ setVolumePlan(0)
+ setHr('')
+ setHrTemporaryAdd([])
+ setHrTemporary([])
+ setIdDeleteHrTemporary([])
+ if (dataEditCard.id) {
+ handleGetReportActivity(dataEditCard.id)
+ }
+
+ if (typeDialogReport === "Edit") {
+
+ setId(dataEditCard.id)
+ setText(dataEditCard.activity)
+ setProgress(dataEditCard.persentase_progress)
+ setStartDate(moment(dataEditCard.start_date))
+ dataEditCard.assign_hr.map((item) => {
+ item.value = item.id_hr
+ item.label = item.name
+ })
+ setVolumePlan(dataEditCard.jumlah_pekerjaan)
+ setHr(dataEditCard.assign_hr)
+ setHrTemporary(dataEditCard.assign_hr)
+
+ } else {
+
+ setId(0)
+
+ }
+
+ dataHr.map((item) => {
+ item.value = item.id
+ item.label = item.name
+ })
+
+ }, [openDialogReport])
+
+ useEffect(() => {
+ if (dataEditCard.id) {
+ handleGetReportActivity(dataEditCard.id)
+ }
+ }, [idDelete])
+
+ const handleCancel = () => {
+ closeDialogReport('cancel', 'none')
+ }
+
+ const handleReport = () => {
+ console.log('Button Triggered')
+ }
+
+ const handleGetReportActivity = async (id) => {
+ const payload = {
+ columns: [
+ {
+ name: "activity_id",
+ logic_operator: "=",
+ value: id,
+ operator: "AND",
+ }
+ ],
+ };
+
+ const result = await axios
+ .post(`${BASE_SIMPRO_LUMEN}/report-activity/search`, payload, HEADER)
+ .then((res) => res)
+ .catch((error) => error.response);
+
+ if (result && result.data && result.data.code == 200) {
+ let dataRes = result.data.data || [];
+ dataRes.map((item) => {
+ let formattedDate = moment(item.report_date, "YYYY-MM-DD HH:mm:ssZ").format('YYYY-MM-DD');
+ item.report_date = formattedDate;
+
+ })
+ setReportActivity(dataRes)
+ } else {
+ NotificationManager.error("Gagal Mengambil Data!!", "Failed");
+ }
+ }
+
+ const handleDeleteReport = (id) => {
+ setIdDelete(id)
+ setAlertDelete(true)
+ }
+
+ const cancelDelete = () => {
+ setAlertDelete(false)
+ }
+
+ const onConfirmDelete = async () => {
+ let url = `${BASE_SIMPRO_LUMEN}/report-activity/delete/${idDelete}`;
+ setAlertDelete(false)
+ const result = await axios.delete(url, HEADER)
+ .then(res => res)
+ .catch((error) => error.response);
+
+ if (result && result.data && result.data.code === 200) {
+ setIdDelete('')
+ closeDialogReport("Delete")
+ NotificationManager.success(`Data berhasil dihapus!`, 'Success!!');
+ } else {
+ setIdDelete('')
+ NotificationManager.error(`Data gagal dihapus!}`, 'Failed!!');
+ }
+ }
+
+ const handleSave = () => {
+ let data = '';
+
+ if (!reportDate || reportDate === "") {
+ alert("reportDate cannot be empty!");
+ return false;
+ }
+ if (!hr || hr === "") {
+ alert("hr cannot be empty!");
+ return false;
+ }
+
+ if (typeDialogReport === "Save") {
+
+ data = {
+ "activity_id": dataEditCard.id,
+ "report_date": reportDate,
+ "jumlah_pekerjaan": volumeActual,
+ "description": description
+ }
+ setReportDate('');
+ setVolumeActual(0);
+ setHr(null);
+ setDescription('');
+ closeDialogReport('Save', data, hr);
+ handleCancel()
+ } else if (typeDialogReport === "Edit") {
+
+ hrTemporaryAdd.map((item) => {
+ let dataSaveHr = {
+ "user_id": item.id,
+ "role_proyek_id": item.proyek_role,
+ "version_gantt_id": version_gantt_id,
+ "proyek_id": proyek_id,
+ "activity_id": id
+ }
+ userToActivityAdd(dataSaveHr)
+ })
+
+
+ IdDeleteHrTemporary.map((item) => {
+
+ userToActivityDelete(item)
+
+ })
+
+ setHrTemporaryAdd([])
+ setIdDeleteHrTemporary([])
+ setHrTemporary([])
+
+ progress == 100 ?
+ data = {
+ "text": text,
+ "start_date": startDate,
+ "jumlah_pekerjaan": volumePlan,
+ "proyek_id": proyek_id,
+ "version_gantt_id": version_gantt_id,
+ "board_id": idBoard
+ }
+ :
+ data = {
+ "text": text,
+ "start_date": startDate,
+ "jumlah_pekerjaan": volumePlan,
+ "proyek_id": proyek_id,
+ "version_gantt_id": version_gantt_id,
+ "board_id": idBoard
+ }
+
+ closeDialogReport('edit', data, hr, id);
+
+ }
+
+ }
+
+ const onChangeHr = (newValue, actionMeta) => {
+ if (typeDialogReport === "Edit") {
+ if (actionMeta.action === 'select-option') {
+ const item = actionMeta.option
+ // console.log("actionMeta", item);
+ // let dataSaveHr = {
+ // "user_id": item.id,
+ // "role_proyek_id": item.proyek_role,
+ // "version_gantt_id": version_gantt_id,
+ // "proyek_id": proyek_id,
+ // "activity_id": id
+ // }
+ // userToActivityAdd(dataSaveHr)
+ setHrTemporaryAdd([...hrTemporaryAdd, item])
+ } else if (actionMeta.action === 'remove-value') {
+ const id = actionMeta.removedValue.id
+ // userToActivityDelete(id)
+ for (let k in hrTemporary) {
+ if (hrTemporary[k].id == id) {
+ setIdDeleteHrTemporary([...IdDeleteHrTemporary, id])
+ }
+ }
+
+ }
+ }
+
+ setHr(newValue)
+ };
+
+ const increase = () => {
+ let newPercent = progress + 10;
+ if (newPercent > 100) {
+ newPercent = 100;
+ }
+ setProgress(newPercent);
+
+ };
+
+ const decline = () => {
+ let newPercent = progress - 10;
+ if (newPercent < 0) {
+ newPercent = 0;
+ }
+ setProgress(newPercent);
+ };
+
+ const columns = [
+ {
+ title: 'Created By',
+ dataIndex: 'created_by',
+ key: 'created_by',
+ },
+ {
+ title: 'Report Date',
+ dataIndex: 'report_date',
+ key: 'report_date',
+ },
+ {
+ title: 'Actual',
+ dataIndex: 'job_count_report',
+ key: 'job_count_report',
+ render: (text) => Math.round(text),
+ },
+ {
+ title: 'Action',
+ dataIndex: 'id',
+ key: 'actions',
+ render: (id) => (
+
+
+
+ ),
+ },
+ ];
+
+ const renderForm = () => {
+ return (
+
+ )
+ }
+
+
+ return (
+ <>
+ handleCancel()} open={openDialogReport}>
+ cancelDelete()}
+ focusCancelBtn
+ >
+ Delete this data
+
+
+
+
+
+
+
+
+
+
+
+
+ {renderForm()}
+
+
+
+
+ >
+ )
+
+}
+
+export default DialogFormReport;
diff --git a/src/views/SimproV2/Kanban/Kanban.css b/src/views/SimproV2/Kanban/Kanban.css
new file mode 100644
index 0000000..61a0e59
--- /dev/null
+++ b/src/views/SimproV2/Kanban/Kanban.css
@@ -0,0 +1,136 @@
+.App {
+ text-align: center;
+}
+
+.App-header {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ font-size: calc(10px + 1.5vmin);
+}
+
+.App-link {
+ color: #61dafb;
+}
+
+@keyframes App-logo-spin {
+ from {
+ transform: rotate(0deg);
+ }
+ to {
+ transform: rotate(360deg);
+ }
+}
+
+.characters {
+ list-style: none;
+ padding-left: 0;
+}
+
+.characters li {
+ display: flex;
+ align-items: center;
+ border: solid 2px #d0d0d0;
+ border-radius: .2em;
+ padding: .5em .8em .5em .5em;
+ margin-bottom: 1em;
+}
+
+/* .characters p {
+ max-width: none;
+ font-weight: bold;
+ margin: 0;
+} */
+
+.characters-thumb {
+ overflow: hidden;
+ flex-shrink: 0;
+ width: 2em;
+ height: 2em;
+ background-color: #e8e8e8;
+ padding: .5em;
+ margin-right: .5em;
+}
+
+.characters-thumb img {
+ display: block;
+ width: 100%;
+ height: auto;
+}
+
+
+/* body */
+.body {
+ background-color: #f4f4f4;
+ border-radius: 10px;
+ padding: 20px;
+}
+
+.board {
+ width: 19rem;
+ height: 3rem;
+ padding: 5px;
+ background-color: #fff;
+ border-radius: 8px;
+ /* border: 0.5px solid silver; */
+ text-align: left;
+}
+
+.board-add{
+ width: 15rem;
+ height: 3rem;
+ padding: 5px;
+ background-color: #fff;
+ border-radius: 8px;
+ /* border: 0.5px solid silver; */
+ text-align: left;
+ cursor: pointer;
+ background-color: #f4f4f4;
+}
+
+.board-add:hover {
+ background-color: rgb(255, 255, 255);
+}
+
+.title-board-add {
+ font-weight: normal;
+ margin-top: 5px;
+ margin-left: 10px;
+ /* font-size: 20px; */
+ color: silver;
+}
+
+.title-board {
+ font-weight: normal;
+ margin-top: 5px;
+ margin-left: 10px;
+ /* font-size: 20px; */
+ /* color: rgb(81, 80, 80); */
+}
+
+.board .title-option {
+ float: right;
+}
+
+.board {
+ border-radius: 8px;
+ height: 3rem;
+ width: 270px;
+}
+
+.cards {
+ margin-bottom: 10px;
+ border-radius: 8px;
+}
+/* border-top: 5px solid #65D3B3; */
+
+.add-card {
+ color: #f4f4f4;
+ cursor: pointer;
+ /* visibility: hidden; */
+}
+
+.destination:hover .add-card{
+ color: silver;
+}
\ No newline at end of file
diff --git a/src/views/SimproV2/Kanban/Task.js b/src/views/SimproV2/Kanban/Task.js
new file mode 100644
index 0000000..c5fdf74
--- /dev/null
+++ b/src/views/SimproV2/Kanban/Task.js
@@ -0,0 +1,206 @@
+import React, {Fragment, useState} from 'react'
+import './Kanban.css';
+import { Draggable, DragDropContext, Droppable } from 'react-beautiful-dnd';
+import styled from 'styled-components'
+import moment from 'moment';
+import DialogCard from './DialogCard';
+import { Card, Row, Col, Button, Avatar, Tooltip, Popover, Progress, Dropdown, Menu, Space } from 'antd';
+import {
+ AntDesignOutlined,
+ UserOutlined,
+ EditOutlined,
+ DeleteOutlined,
+ EllipsisOutlined,
+ DownOutlined,
+ MoreOutlined,
+ SmileOutlined
+ } from '@ant-design/icons';
+
+const Container = styled.div`
+ border: 1px solid lightgrey;
+ border-radius: 2px;
+ padding: 8px;
+ margin-bottom: 8px;
+ transition: background-color 0.2s ease;
+ background-color: ${props =>
+ props.isDragDisabled
+ ? 'lightgrey'
+ : props.isDragging
+ ? 'lightgreen'
+ : 'white'};
+`
+
+const gridStyle = {
+ // width: '25%',
+ // textAlign: 'center',
+};
+
+
+const Task = ({
+ task,
+ index,
+ stateKanban,
+ bodyColor,
+ loadingCard,
+ handleDeleteCard,
+ handleEditCard
+}) => {
+ // const Task = ({ task, index, children, stateKanban , handleOpenDialogChild , handleDelete}) => {
+ const isDragDisabled = false
+
+ const [openDialogChild, setOpenDialogChild] = useState(false)
+ const [typeDialogChild, setTypeDialogChild] = useState('Save')
+ const [clickOpenModalChild, setClickOpenModalChild] = useState(false)
+
+
+ function onDragEnd(result) {
+ const { destination, source, draggableId } = result
+
+
+ if (!destination) {
+ return
+ }
+
+ if (
+ destination.droppableId === source.droppableId &&
+ destination.index === source.index
+ ) {
+ return
+ }
+
+ const start = source.droppableId;
+ const finish = destination.droppableId;
+
+ if (start === finish) {
+ const newTaskIds = stateKanban.columns[0].tasks[0].children;
+ let newObj = stateKanban.columns[0].tasks[0].children.find(x => x.id === draggableId);
+ newTaskIds.splice(source.index, 1)
+ newTaskIds.splice(destination.index, 0, newObj)
+ return
+ }
+
+
+ }
+
+
+ const handleCloseDialogChild = (type, data) => {
+ if (type === "save") {
+ // saveItem(data);
+ }
+ setOpenDialogChild(false)
+ }
+
+ const toggleAddDialogChild = () => {
+ setOpenDialogChild(!openDialogChild)
+ }
+
+ const handleOpenDialogChild = (type) => {
+ setOpenDialogChild(true)
+ setTypeDialogChild(type)
+ }
+
+ const menu = (task) => (
+