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) => ( + + status is {column.status_progress} + + ), + }, + { + key: '1', + label: ( + handleEditBoard(column)} > + Edit + + ), + }, + { + key: '2', + label: ( + handleDelete(column.id)} > + Hapus + + ), + }, + ]} + /> + ); + + return ( + +
+
+ {column.name_board} + {/* handleEditBoard(column)}> */} + {/* handleDelete(column.id)}> */} +
+ + e.preventDefault()}> + + + +
+
+
+ +
+ { tasks.length === 0 && +

handleOpenDialogActivity("Save", column.id)}>+ Add Activity

+ } + + {(provided, snapshot) => ( + + {tasks.map((task, index) => ( + + ))} + {provided.placeholder} + + )} + + { tasks.length > 0 && +

handleOpenDialogActivity("Save", column.id)}>+ Add Activity

+ } +
+ + ) +} + +export default Column; diff --git a/src/views/SimproV2/Kanban/DialogCard.js b/src/views/SimproV2/Kanban/DialogCard.js new file mode 100644 index 0000000..76355ce --- /dev/null +++ b/src/views/SimproV2/Kanban/DialogCard.js @@ -0,0 +1,72 @@ +import React, { useEffect, useState } from 'react' +import { + ModalHeader, ModalBody, ModalFooter, + Button, Form, FormGroup, Label, Input, Col, Row +} from 'reactstrap'; +import { DatePicker, Tooltip, Select, Modal } from 'antd'; +import { formatRupiah, formatNumber } from '../../../const/CustomFunc' +import moment from 'moment'; +import 'antd/dist/antd.css'; +const { Option } = Select + +const DialogCard = ({ + openDialogChild, + closeDialogChild, + toggleDialogChild, + typeDialogChild, + task, + }) => { + + useEffect(() => { + + }, [openDialogChild]) + + const handleCancel = () => { + closeDialogChild('cancel', 'none') + } + + return ( + <> + {/* + + + + + {' '} + + + */} + + Close + + ]} + > + + Activity + {task.activity} + + + Start Date + {moment(task.start_date).format('DD-MM-YYYY HH:mm')} + + + End Date + {moment(task.end_date).format('DD-MM-YYYY HH:mm')} + + + Bobot Planning + {task.bobot_planning} + + + Presentase Progress + {task.persentase_progress} + + + + ) + +} + +export default DialogCard; diff --git a/src/views/SimproV2/Kanban/DialogFormActivity.js b/src/views/SimproV2/Kanban/DialogFormActivity.js new file mode 100644 index 0000000..a8e9680 --- /dev/null +++ b/src/views/SimproV2/Kanban/DialogFormActivity.js @@ -0,0 +1,347 @@ +import React, { useEffect, useState } from 'react' +import { + Modal, ModalHeader, ModalBody, ModalFooter, + Form, FormGroup, Label, Input, Col, Row +} from 'reactstrap'; +import { DatePicker, Tooltip, Drawer, Divider, Layout, Button, Space, Progress } from 'antd'; +import { + CloseOutlined, + MinusOutlined, + PlusOutlined +} from '@ant-design/icons'; +import { formatRupiah, formatNumber } from '../../../const/CustomFunc' +import moment from 'moment'; +import Select from 'react-select'; +import axios from "../../../const/interceptorApi"; +import { BASE_SIMPRO_LUMEN } from '../../../const/ApiConst'; +import { + NotificationManager +} from "react-notifications"; +import 'antd/dist/antd.css'; +// const { Option } = Select + +const DialogFormActivity = ({ + activityProject, + openDialogActivity, + closeDialogActivity, + handleOpenDialogReport, + toggleDialogActivity, + typeDialogActivity, + proyek_id, + version_gantt_id, + idBoard, + dataHr, + dataEditCard, + userToActivityDelete, + userToActivityAdd +}) => { + const [id, setId] = useState('') + const [text, setText] = useState('') + const [startDate, setStartDate] = useState('') + const [progress, setProgress] = useState(0) + const [volumePlan, setVolumePlan] = useState(0) + const [endDate, setEndDate] = useState('') + const [nativeeditorStatus, setNativeeditorStatus] = useState('') + const [hr, setHr] = useState(null); + const [hrTemporary, setHrTemporary] = useState(); + const [hrTemporaryAdd, setHrTemporaryAdd] = useState([]); + const [IdDeleteHrTemporary, setIdDeleteHrTemporary] = useState([]); + + useEffect(() => { + + setText('') + setStartDate('') + setProgress(0) + setEndDate('') + setVolumePlan(0) + setHr('') + setHrTemporaryAdd([]) + setHrTemporary([]) + setIdDeleteHrTemporary([]) + + if (typeDialogActivity === "Edit") { + + setId(dataEditCard.id) + setText(dataEditCard.activity) + setProgress(dataEditCard.persentase_progress) + setStartDate(moment(dataEditCard.start_date)) + setEndDate(moment(dataEditCard.end_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 + }) + + }, [openDialogActivity]) + + const handleCancel = () => { + closeDialogActivity('cancel', 'none') + setVolumePlan(0) + } + + + const handleSave = () => { + let data = ''; + + if (!text || text === "") { + alert("text cannot be empty!"); + return false; + } + if (!startDate || startDate === "") { + alert("startDate cannot be empty!"); + return false; + } + if (!endDate || endDate === "") { + alert("endDate cannot be empty!"); + return false; + } + if (!hr || hr === "") { + alert("hr cannot be empty!"); + return false; + } + + if (typeDialogActivity === "Save") { + + data = { + "text" : text, + "start_date": startDate, + "jumlah_pekerjaan" : volumePlan, + "end_date" : endDate, + "proyek_id" : proyek_id, + "version_gantt_id": version_gantt_id, + "board_id" : idBoard, + "parent_id" : activityProject + } + closeDialogActivity('save', data, hr, id); + } else if (typeDialogActivity === "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, + "end_date": endDate, + "proyek_id": proyek_id, + "version_gantt_id": version_gantt_id, + "board_id": idBoard, + "progress": parseFloat(progress / 100).toFixed(2) + } + : + data = { + "text": text, + "start_date": startDate, + "jumlah_pekerjaan": volumePlan, + "end_date": endDate, + "proyek_id": proyek_id, + "version_gantt_id": version_gantt_id, + "board_id": idBoard, + "progress": parseFloat(progress / 100).toFixed(2) + } + + closeDialogActivity('edit', data, hr, id); + + } + + } + + const onChangeHr = (newValue, actionMeta) => { + if (typeDialogActivity === "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 renderForm = () => { + return ( +
+ + + + + setText(e.target.value)} /> + + + + + + { + dateItem.set({hour:0,minute:0,second:0}) + setStartDate(dateItem)}} + /> + + + + + + { + dateItem.set({hour:23,minute:59,second:59}) + setEndDate(dateItem)}} + /> + + + {/* + + + setDuration(e.target.value)}/> + + */} + + + + {/* setProgress(e.target.value)}/> */} + + + + + + + setVolumePlan(e.target.value)} /> + + + + + + {/* */} + setNameBoard(e.target.value)} /> + {/* setNameBoard(e.target.value)} invalid={nameBoardVal} /> + + Required! + */} + + + + + + + + {/* setStatusProgress(e.target.value)}/> */} + + + + + + + + + setHeaderColor(e.target.value)} /> + + + + + + setBodyColor(e.target.value)} /> + + + +
+ ) + } + + + 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 ( +
+ + + + + setName(e.target.value)}/> + + + +
+ ) + } + + + 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 ( +
+ + + + + { + dateItem.set({ hour: 0, minute: 0, second: 0 }) + setReportDate(dateItem) + }} + /> + + + {/* + + + setDuration(e.target.value)}/> + + */} + + + + setVolumeActual(e.target.value)} /> + + + + + + {/* */} + setDescription(e.target.value)} /> + + +
+ {reportActivity.length > 0 ? ( + + ) : null} + + + + ) + } + + + 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) => ( + handleEditCard(task)}> + Edit + + ), + }, + { + key: '2', + label: ( + handleDeleteCard(task.id)}> + Hapus + + ), + }, + ]} + /> + ); + + + + return ( + + toggleAddDialogChild} + typeDialogChild={typeDialogChild} + clickOpenModalChild={clickOpenModalChild} + task={task} + /> + + {(provided, snapshot) => ( + handleEditCard(task)} + > +
+ +
{task.activity}
+
+
+ +
+
+
+ + { + task.assign_hr.map( (item, index) => { + if (index < 8) { + return + } size="small" + /> + + } else if(index == 8) { + return + {/* + {["1","1","1","1","1","1","1","1"].length - 3} */} + + {task.assign_hr.length - 8} + + } + + }) + } + +
+
+

{moment(task.start_date).format('DD-MM-YYYY HH:mm')}

+
+
+
+ )} +
+
+ ) +} + +export default Task; diff --git a/src/views/SimproV2/Kanban/index.js b/src/views/SimproV2/Kanban/index.js new file mode 100644 index 0000000..de647c2 --- /dev/null +++ b/src/views/SimproV2/Kanban/index.js @@ -0,0 +1,912 @@ +import React, { Fragment, useEffect, useMemo, useState } from 'react'; +import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd'; +import { + Row, + Col, + Button, + Tooltip, + Popover, + Select +} from 'antd'; +import { Card, CardBody, CardHeader, Input } from "reactstrap"; + +import './Kanban.css'; +import Column from './Column'; +import styled from 'styled-components' +import { + PlusOutlined +} from '@ant-design/icons'; +import { BASE_SIMPRO_LUMEN, VERSION_GANTT_SEARCH, KANBAN_BOARD_LIST, KANBAN_BOARD_ADD, KANBAN_BOARD_DELETE, KANBAN_BOARD_EDIT, KANBAN_CARD_LIST, KANBAN_CARD_ADD, KANBAN_CARD_EDIT, USER_TO_ACTIVITY_ADD, HUMAN_RESOURCE_LIST, USER_TO_ACTIVITY_DELETE, PROYEK_SEARCH } from '../../../const/ApiConst'; +import axios from "../../../const/interceptorApi"; +import { + NotificationContainer, + NotificationManager +} from "react-notifications"; +import { array } from 'prop-types'; +import DialogFormBoard from './DialogFormBoard'; +import DialogFormActivity from './DialogFormActivity'; +import DialogFormChild from './DialogFormChild'; +import DialogFormReport from './DialogFormReport'; +import SweetAlert from 'react-bootstrap-sweetalert'; +import { useParams, useHistory } from "react-router-dom"; +import moment from 'moment'; +const Container = styled.div` + display: flex; +` + +const { Option } = Select; + +const finalSpaceCharacters = [ + { + id: 'gary', + name: 'Gary Goodspeed', + thumb: '/images/gary.png' + }, + { + id: 'garyd', + name: 'Gary Goodspeedg', + thumb: '/images/gary.png' + }, +] + +const initialData = { + columns: [ + + ], +} + +let hierarchy = []; +hierarchy.push(JSON.parse(localStorage.getItem("hierarchy"))); +const role_id = localStorage.getItem("role_id"); + +const Kanban = ({ params, ...props }) => { + let history = useHistory(); + const token = localStorage.getItem("token"); + const sessionLogin = localStorage.getItem("user_id"); + + const HEADER = { + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${token}`, + }, + }; + + const { proyek_id, version_gantt_id } = useParams(); + const [characters, updateCharacters] = useState(finalSpaceCharacters); + const [stateKanban, setStateKanban] = useState(initialData); + const [kanbanBoard, setKanbanBoard] = useState([]); + const [kanbanCard, setKanbanCard] = useState([]); + const [alertDelete, setAlertDelete] = useState(false) + const [alertDeleteCard, setAlertDeleteCard] = useState(false) + const [idDelete, setIdDelete] = useState(0) + const [idDeleteCard, setIdDeleteCard] = useState(0) + const [project, setProject] = useState(!proyek_id ? '' : proyek_id) + const [versionGantt, setVersionGantt] = useState(!version_gantt_id ? 0 : version_gantt_id) + // + const [openDialogBoard, setOpenDialogBoard] = useState(false) + const [typeDialogBoard, setTypeDialogBoard] = useState('') + const [clickOpenModalBoard, setClickOpenModalBoard] = useState(false) + const [openDialogActivity, setOpenDialogActivity] = useState(false) + const [openDialogReport, setOpenDialogReport] = useState(false) + const [typeDialogActivity, setTypeDialogActivity] = useState('Save') + // report + + const [typeDialogReport, setTypeDialogReport] = useState('Save') + const [clickOpenModalActivity, setClickOpenModalActivity] = useState(false) + const [openDialogChild, setOpenDialogChild] = useState(false) + const [typeDialogChild, setTypeDialogChild] = useState('Save') + const [clickOpenModalChild, setClickOpenModalChild] = useState(false) + const [open, setOpen] = useState(false); + const [dataEditBoard, setDataEditBoard] = useState([]) + const [dataEditCard, setDataEditCard] = useState([]) + const [idBoard, setIdBoard] = useState(0) + const [loadingCard, setLoadingCard] = useState(false) + const [dataHr, setDataHr] = useState([]); + const [dataProyek, setDataProyek] = useState([]) + const [dataVersionGantt, setDataVersionGantt] = useState([]) + const [optionProyek, setOptionProyek] = useState([]) + const [optionGantt, setOptionGantt] = useState([]) + const [activityProject, setActivityProject] = useState(null) + + useEffect(() => { + handleGetDataHr() + }, [project]) + + useEffect(() => { + getDataProyek() + if (project && versionGantt) { + getDataGantt(parseInt(project)) + getActivityProject() + getDataKanbanBoard() + } + }, [project, versionGantt]) + + const getActivityProject = async () => { + const url = `${BASE_SIMPRO_LUMEN}/activity/${versionGantt}/${project}/get` + const result = await axios + .get(url, HEADER) + .then(res => res) + .catch((error) => error.response) + + if (result && result.data.data.length > 0) { + result.data.data.map((item) => { + if (item.type_activity == "project") { + setActivityProject(item.id) + } + }) + } + } + + const getDataKanbanBoard = async () => { + const url = `${KANBAN_BOARD_LIST}?start=0&length=-1&orderby=id&asc=true&column=version_gantt_id&value=${versionGantt}&logic==`; + const result = await axios + .get(url, HEADER) + .then(res => res) + .catch((error) => error.response); + + if (!result || result.data.data.length == 0) { + + const data = [ + { + name_board: 'TODO LIST', + status_progress: 'open', + header_color: "#9EE5FF", + body_color: "#112A46", + proyek_id: project, + version_gantt_id: versionGantt + }, + { + name_board: 'DOING', + status_progress: 'on-progress', + header_color: "#D9D4FD", + body_color: "#112A46", + proyek_id: project, + version_gantt_id: versionGantt + }, + { + name_board: 'TESTING', + status_progress: 'none', + header_color: "#FAD4FC", + body_color: "#112A46", + proyek_id: project, + version_gantt_id: versionGantt + }, + { + name_board: 'DONE', + status_progress: 'done', + header_color: "#ADE7BF", + body_color: "#112A46", + proyek_id: project, + version_gantt_id: versionGantt + } + ]; + for (let i = 0; i < data.length; i++) { + const formData = data[i]; + const result = await axios.post(KANBAN_BOARD_ADD, formData, HEADER) + .then(res => res) + .catch((error) => error.response); + if (result && result.data && result.data.code === 200) { + getDataKanbanBoard() + NotificationManager.success(`Data berhasil ditambah`, 'Success!!'); + } else { + NotificationManager.error(`${result.data.message}`, 'Failed!!'); + } + } + } + if (result && result.data && result.data.code == 200) { + let countColumns = 0; + const results = result.data.data; + for (let i = results.length - 1; i >= 0; i--) { + const res = results[i]; + res.key = res.id.toString() + res.tasks = [] + await getDataKanbanCard(res.id).then(function (task) { + if (task !== false) { + task.map((e) => { + e.id = e.id.toString() + e.persentase_progress = e.persentase_progress ? e.persentase_progress : 0; + res.tasks.push(e); + }); + } + }) + countColumns += 1; + + if (result.data.data.length === countColumns) { + let newState = { + ...stateKanban, + columns: [ + ...result.data.data, + ] + } + + setStateKanban(newState) + + + } + + setLoadingCard(false) + + }; + + } else { + NotificationManager.error('Gagal Mengambil Data!!', 'Failed'); + } + + } + + const getDataProyek = async () => { + let start = 0; + + const payload = { + columns: [ + { + name: "created_by", + logic_operator: "IN", + value: hierarchy, + operator: "AND" + } + ], + joins: [ + { + name: "m_users", + column_join: "pm_id", + column_results: ["name", "username"], + }, + { + name: "m_type_proyek", + column_join: "type_proyek_id", + column_results: ["name", "description"], + }, + ], + orders: { columns: ["id"], ascending: false }, + }; + + if (parseInt(role_id) !== 1) { + payload["columns"] = [ + { name: "id", logic_operator: "=", value: project, operator: "AND" }, + ]; + } + + const result = await axios + .post(PROYEK_SEARCH, payload, HEADER) + .then((res) => res) + .catch((error) => error.response); + + if (result && result.data && result.data.code == 200) { + let dataRes = result.data.data || []; + let dataTemp = [] + dataRes.map(n => { + if (n.id > 0 && n.nama.trim() !== "") { + let obj = { + key: n.id, + value: n.id, + label: n.nama.trim() + } + dataTemp.push(obj) + } + }) + setOptionProyek(dataTemp); + setDataProyek(dataRes); + } else { + NotificationManager.error("Gagal Mengambil Data!!", "Failed"); + } + }; + + const getDataGantt = async (id) => { + const payload = { + columns: [ + { + name: "proyek_id", + logic_operator: "=", + value: id, + operator: "AND", + }, + ], + }; + const result = await axios + .post(VERSION_GANTT_SEARCH, payload, HEADER) + .then((res) => res) + .catch((error) => error.response); + + if (result && result.status == 200) { + let dataRes = result.data.data || []; + let dataTemp = [] + dataRes.map(n => { + if (n.id > 0 && n.name_version.trim() !== "") { + let obj = { + key: n.id, + value: n.id, + label: n.name_version + } + dataTemp.push(obj) + } + }) + setOptionGantt(dataTemp); + setDataVersionGantt(dataRes); + + } else { + NotificationManager.error( + `Data gantt project gagal terload silahkan coba lagi!`, + "Failed!!" + ); + } + }; + + const getDataKanbanCard = async (id) => { + const url = `${KANBAN_CARD_LIST}/${project}/${versionGantt}/${id}?session=${sessionLogin}`; + const result = await axios + .get(url, HEADER) + .then(res => res) + .catch((error) => error.response); + + + if (result.data.status === "empty") { + return false + } else { + return result.data.data + } + + } + + + function handleOnDragEnd(result) { + if (!result.destination) return; + + const items = Array.from(characters); + const [reorderedItem] = items.splice(result.source.index, 1); + items.splice(result.destination.index, 0, reorderedItem); + + updateCharacters(items); + } + + const onChangeProject = (val) => { + setProject(val); + getDataGantt(val); + setVersionGantt(''); + }; + + const onChangeGantt = async (val) => { + setVersionGantt(val); + if (project) { + history.push(`/kanban/${project}/${val}`); + } + }; + + 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 data = { + "kanban_board_id": destination.droppableId, + } + + setLoadingCard(true) + + editActivity(data, draggableId) + + } + + } + + const saveBoard = async (data) => { + const formData = data + + const result = await axios.post(KANBAN_BOARD_ADD, formData, HEADER) + .then(res => res) + .catch((error) => error.response); + if (result && result.data && result.data.code === 200) { + NotificationManager.success(`Data berhasil ditambah`, 'Success!!'); + getDataKanbanBoard() + } else { + NotificationManager.error('Status Open Progress Done Hanya 1', 'Failed!!'); + } + } + + const editBoard = async (data) => { + let urlEdit = KANBAN_BOARD_EDIT(data.id) + + const formData = data + const result = await axios.put(urlEdit, formData, HEADER) + .then(res => res) + .catch((error) => error.response); + if (result && result.data && result.data.code === 200) { + getDataKanbanBoard() + NotificationManager.success(`Data berhasil diedit`, 'Success!!'); + } else { + NotificationManager.error(`Data gagal di edit`, `Failed!!`); + } + } + + // Board + + const handleCloseDialogBoard = (type, data) => { + if (type === "save") { + saveBoard(data); + } else if (type === "edit") { + editBoard(data); + } + setOpenDialogBoard(false) + } + + const toggleDialogBoard = () => { + setOpenDialogBoard(!openDialogBoard) + } + + const handleOpenDialogBoard = (type) => { + setOpenDialogBoard(true) + setTypeDialogBoard(type) + } + + const saveReport = async (data, dataHrSelect) => { + setLoadingCard(true) + var formData = new FormData(); + formData.set('report_date', moment(data.report_date).format("YYYY-MM-DD")); + formData.set('job_count_report', data.jumlah_pekerjaan); + formData.set('user_id', dataHrSelect.id); + formData.set('gantt', true); + formData.set('activity_id', data.activity_id); + formData.set('description', data.description); + const result = await axios + .post(`${BASE_SIMPRO_LUMEN}/report-activity/add`, formData, HEADER) + .then(res => res) + .catch((error) => error.response); + if (result && result.data && result.data.code === 200) { + getDataKanbanBoard() + NotificationManager.success(`Data berhasil ditambah`, 'Success!!'); + } else { + setLoadingCard(false) + NotificationManager.error(`${result.data.message}`, 'Failed!!'); + } + } + + const saveCard = async (data, dataHrSelect) => { + setLoadingCard(true) + + const formData = data + const result = await axios.post(KANBAN_CARD_ADD, formData, HEADER) + .then(res => res) + .catch((error) => error.response); + + if (result && result.data && result.data.code === 200) { + dataHrSelect.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": result.data.tid + } + userToActivityAdd(dataSaveHr) + }) + NotificationManager.success(`Data berhasil ditambah`, 'Success!!'); + } else { + setLoadingCard(false) + NotificationManager.error(`${result.data.message}`, 'Failed!!'); + } + } + + const editCard = async (data, id) => { + setLoadingCard(true) + const url = `${KANBAN_CARD_ADD}/${id}` + const formData = data + const result = await axios.put(url, formData, HEADER) + .then(res => res) + .catch((error) => error.response); + if (result && result.data && result.data.code === 200) { + getDataKanbanBoard() + NotificationManager.success(`Data berhasil diubah`, 'Success!!'); + } else { + NotificationManager.error(`${result.data.message}`, 'Failed!!'); + } + } + + const userToActivityAdd = async (data) => { + setLoadingCard(true) + + const formData = data + const result = await axios.post(USER_TO_ACTIVITY_ADD, formData, HEADER) + .then(res => res) + .catch((error) => error.response); + + if (result && result.data && result.data.code === 200) { + getDataKanbanBoard() + NotificationManager.success(`Data berhasil ditambah`, 'Success!!'); + } else { + setLoadingCard(false) + NotificationManager.error(`${result.data.message}`, 'Failed!!'); + } + } + + const userToActivityDelete = async (id) => { + setLoadingCard(true) + + const url = USER_TO_ACTIVITY_DELETE(id) + const result = await axios.delete(url, HEADER) + .then(res => res) + .catch((error) => error.response); + + if (result && result.data && result.data.code === 200) { + getDataKanbanBoard() + NotificationManager.success(`Data berhasil dihapus`, 'Success!!'); + } else { + setLoadingCard(false) + NotificationManager.error(`${result.data.message}`, 'Failed!!'); + } + } + // Activity + + const handleCloseDialogActivity = (type, data, dataHrSelect, id) => { + if (type === "save") { + saveCard(data, dataHrSelect) + } else if (type == "edit") { + editCard(data, id) + } + setOpenDialogActivity(false) + } + + const toggleAddDialogActivity = () => { + setOpenDialogActivity(!openDialogActivity) + } + + const handleOpenDialogActivity = (type, idBoard) => { + setOpenDialogActivity(true) + setTypeDialogActivity(type) + setIdBoard(idBoard) + } + + // Report + + const handleCloseDialogReport = (type, data, dataHrSelect) => { + if (type === "Save") { + saveReport(data, dataHrSelect) + } else if(type === "Delete") { + setLoadingCard(true); + getDataKanbanBoard(); + } else { + setOpenDialogReport(false) + } + } + + const handleOpenDialogReport = (type) => { + setOpenDialogReport(true) + setTypeDialogReport(type) + } + + const editActivity = async (data, id) => { + let urlEdit = KANBAN_CARD_EDIT(id) + + const formData = data + const result = await axios.put(urlEdit, formData, HEADER) + .then(res => res) + .catch((error) => error.response); + + + + if (result && result.data && result.data.code === 200) { + getDataKanbanBoard() + NotificationManager.success(`Data berhasil diedit`, 'Success!!'); + } else { + NotificationManager.error(`Data gagal di edit`, `Failed!!`); + } + } + + // Child + + const handleCloseDialogChild = (type, data) => { + if (type === "save") { + // saveItem(data); + } + setOpenDialogChild(false) + } + + const toggleAddDialogChild = () => { + setOpenDialogChild(!openDialogChild) + } + + const handleOpenDialogChild = (type) => { + setOpenDialogChild(true) + setTypeDialogChild(type) + } + + const onConfirmDelete = async () => { + let url = KANBAN_BOARD_DELETE(idDelete); + + const result = await axios.delete(url, HEADER) + .then(res => res) + .catch((error) => error.response); + + if (result && result.data && result.data.code === 200) { + getDataKanbanBoard() + setIdDelete(0) + setAlertDelete(false) + NotificationManager.success(`Data berhasil dihapus!`, 'Success!!'); + } else { + setIdDelete(0) + setAlertDelete(false) + NotificationManager.error(`Data gagal dihapus!}`, 'Failed!!'); + } + } + + const cancelDelete = () => { + setAlertDelete(false) + } + + const handleDelete = async (id) => { + await setAlertDelete(true) + setIdDelete(id) + + } + + const handleEditBoard = (data) => { + setDataEditBoard(data) + handleOpenDialogBoard('Edit'); + } + + const handleGetDataHr = async () => { + let urlList = `${HUMAN_RESOURCE_LIST}/select?idProyek=${project}&sessionLogin=${sessionLogin}` + const result = await axios + .get(urlList, HEADER) + .then((res) => res) + .catch((err) => err.response); + + + if (result.status === 200) { + setDataHr(result.data) + } + + }; + + const handleDeleteCard = async (id) => { + await setAlertDeleteCard(true) + setIdDeleteCard(id) + } + + const onConfirmDeleteCard = async () => { + setAlertDeleteCard(false) + setLoadingCard(true) + + const url = `${KANBAN_CARD_ADD}/${idDeleteCard}` + + const result = await axios.delete(url, HEADER) + .then(res => res) + .catch((error) => error.response); + + if (result && result.data && result.data.code === 200) { + setIdDeleteCard(0) + getDataKanbanBoard() + NotificationManager.success(`Data berhasil dihapus!`, 'Success!!'); + } else { + setIdDeleteCard(0) + NotificationManager.error(`Data gagal dihapus!}`, 'Failed!!'); + } + } + + const cancelDeleteCard = () => { + setAlertDeleteCard(false) + } + + const handleEditCard = (data) => { + setDataEditCard(data) + handleOpenDialogActivity('Edit'); + } + const setupSelectVersionGantt = () => { + return ( + <> + {dataVersionGantt.map((val, index) => { + return ( + + ) + })} + + ) + } + + const setupSelectDataProyek = () => { + return ( + <> + {dataProyek.map((val, index) => { + return ( + + ) + })} + + ) + } + + const RenderStateKanban = useMemo(() => { + return (<> + { + stateKanban.columns.map((column) => { + + return + }) + } + ) + }, [stateKanban, loadingCard]) + + const RenderDialogFormActivity = useMemo( + () => ( + toggleAddDialogActivity} + typeDialogActivity={typeDialogActivity} + clickOpenModalActivity={clickOpenModalActivity} + proyek_id={project} + version_gantt_id={versionGantt} + idBoard={idBoard} + dataHr={dataHr} + dataEditCard={dataEditCard} + userToActivityAdd={userToActivityAdd} + userToActivityDelete={userToActivityDelete} + /> + ), + [openDialogActivity] + ); + const RenderDialogFormReport = useMemo( + () => ( + + ), + [openDialogReport] + ); + return ( + + + cancelDelete()} + focusCancelBtn + > + Delete this data + + cancelDeleteCard()} + focusCancelBtn + > + Delete this data + + toggleDialogBoard} + typeDialogBoard={typeDialogBoard} + clickOpenModalBoard={clickOpenModalBoard} + dataEditBoard={dataEditBoard} + proyek_id={project} + version_gantt_id={versionGantt} + /> + {RenderDialogFormActivity} + {RenderDialogFormReport} + toggleAddDialogChild} + typeDialogChild={typeDialogChild} + clickOpenModalChild={clickOpenModalChild} + /> +
+ + +

Kanban

+ +
+ + + + + + + {project != "" && versionGantt != 0 ? ( + <> + + +
handleOpenDialogBoard("Save")} > +
+
+ + Add Board +
+
+
+ + + + + {RenderStateKanban} + + + + ) : ( +

Please select Project and Gantt Option

+ )} + + + ); +} + +export default Kanban;