Browse Source

Merge pull request 'add kanban. limits storage and user' (#53) from dev-wahyun into staging

Reviewed-on: ibnu/generic-ospro-frontend#53
pull/1/head
farhantock 10 months ago
parent
commit
e17af1b21f
  1. 40
      src/const/ApiConst.js
  2. 6
      src/routes.js
  3. 2
      src/views/ControlMonitoringGantt/index.js
  4. 14
      src/views/Master/MasterCompany/index.js
  5. 3
      src/views/Master/Proyek/index.js
  6. 3
      src/views/SimproV2/Closing/index.js
  7. 2
      src/views/SimproV2/CreatedProyek/AsignCustProject.js
  8. 22
      src/views/SimproV2/CreatedProyek/DialogDocument.js
  9. 9
      src/views/SimproV2/CreatedProyek/FormDocument.js
  10. 6
      src/views/SimproV2/CreatedProyek/index.js
  11. 130
      src/views/SimproV2/Kanban/Column.js
  12. 72
      src/views/SimproV2/Kanban/DialogCard.js
  13. 347
      src/views/SimproV2/Kanban/DialogFormActivity.js
  14. 176
      src/views/SimproV2/Kanban/DialogFormBoard.js
  15. 69
      src/views/SimproV2/Kanban/DialogFormChild.js
  16. 438
      src/views/SimproV2/Kanban/DialogFormReport.js
  17. 136
      src/views/SimproV2/Kanban/Kanban.css
  18. 206
      src/views/SimproV2/Kanban/Task.js
  19. 912
      src/views/SimproV2/Kanban/index.js
  20. 4
      src/views/SimproV2/ResourceWorker/DialogForm.js

40
src/const/ApiConst.js

@ -122,6 +122,9 @@ export let BASE_OSPRO = "http://localhost:8444/generic-ospro-backend";
export let BASE_SIMPRO_LUMEN = `${BASE_OSPRO}/api`;
export let BASE_SIMPRO_LUMEN_IMAGE = `${BASE_OSPRO}/assets/image`;
export let BASE_SIMPRO_LUMEN_FILE = `${BASE_OSPRO}/assets/file/project`;
export let BASE_SIMPRO_LUMEN_FILE_COMPANY = (file, company_name)=>{
return `${BASE_OSPRO}/assets/${company_name}/file/project/${file}`;
}
// switch (APP_MODE) {
// case 'KIT':
@ -281,6 +284,10 @@ export const USER_DELETE = (id) => {
return `${BASE_SIMPRO_LUMEN}/human-resource/delete/${id}`;
};
export const HUMAN_RESOURCE_LIST = `${BASE_SIMPRO_LUMEN}/human-resource`
export const USER_TO_ACTIVITY_ADD = `${BASE_SIMPRO_LUMEN}/user-to-activity/add`
export const USER_TO_ACTIVITY_DELETE = (id) => { return `${BASE_SIMPRO_LUMEN}/user-to-activity/delete/${id}` }
export const ABSENSI_ADD = `${BASE_SIMPRO_LUMEN}/permit/add`;
export const ABSENSI_SEARCH = `${BASE_SIMPRO_LUMEN}/permit/search`;
export const ABSENSI_EDIT = (id) => {
@ -307,11 +314,11 @@ export const DOCUMENT_SEARCH = `${BASE_SIMPRO_LUMEN}/document-project/search`;
export const DOCUMENT_EDIT = (id) => {
return `${BASE_SIMPRO_LUMEN}/document-project/update/${id}`;
};
export const DOCUMENT_DELETE = (id) => {
return `${BASE_SIMPRO_LUMEN}/document-project/delete/${id}`;
export const DOCUMENT_DELETE = (id, company_id) => {
return `${BASE_SIMPRO_LUMEN}/document-project/delete/${id}/${company_id}`;
};
export const DOCUMENT_DOWNLOAD = (id) => {
return `${BASE_SIMPRO_LUMEN}/document-project/download/${id}`;
export const DOCUMENT_DOWNLOAD = (id, company_id) => {
return `${BASE_SIMPRO_LUMEN}/document-project/download/${id}/${company_id}`;
};
export const ROLE_ADD = `${BASE_SIMPRO_LUMEN}/role/add`;
@ -423,8 +430,8 @@ export const PROYEK_GET_ID = (id) => {
export const PROYEK_EDIT = (id) => {
return `${BASE_SIMPRO_LUMEN}/project/update/${id}`;
};
export const PROYEK_DELETE = (id) => {
return `${BASE_SIMPRO_LUMEN}/project/delete/${id}`;
export const PROYEK_DELETE = (id, company_id) => {
return `${BASE_SIMPRO_LUMEN}/project/delete/${id}/${company_id}`;
};
export const PROJECT_ROLE_ADD = `${BASE_SIMPRO_LUMEN}/project-role/add`;
@ -576,6 +583,15 @@ export const DIVISI_DELETE = (id) => {
};
export const DIVISI_LIST = `${BASE_SIMPRO_LUMEN}/divisi/list`;
export const KANBAN_BOARD_LIST = `${BASE_SIMPRO_LUMEN}/kanban-board/list`
export const KANBAN_BOARD_ADD = `${BASE_SIMPRO_LUMEN}/kanban-board/add`
export const KANBAN_BOARD_EDIT = (id) => { return `${BASE_SIMPRO_LUMEN}/kanban-board/update/${id}` }
export const KANBAN_BOARD_DELETE = (id) => { return `${BASE_SIMPRO_LUMEN}/kanban-board/delete/${id}` }
export const KANBAN_CARD_LIST = `${BASE_SIMPRO_LUMEN}/kanban-card/get-data`
export const KANBAN_CARD_ADD = `${BASE_SIMPRO_LUMEN}/task`
export const KANBAN_CARD_EDIT = (id) => { return `${BASE_SIMPRO_LUMEN}/kanban-card/update/${id}` }
export const SHIFT_ADD = `${BASE_SIMPRO_LUMEN}/shift/add`;
export const SHIFT_SEARCH = `${BASE_SIMPRO_LUMEN}/shift/search`;
export const SHIFT_EDIT = (id) => {
@ -700,8 +716,8 @@ export const ASSIGN_HR_PROJECT_SEARCH = `${BASE_SIMPRO_LUMEN}/user-to-proyek/sea
export const ASSIGN_HR_PROJECT_EDIT = (id) => {
return `${BASE_SIMPRO_LUMEN}/user-to-proyek/update/${id}`;
};
export const ASSIGN_HR_PROJECT_DELETE = (id) => {
return `${BASE_SIMPRO_LUMEN}/user-to-proyek/delete/${id}`;
export const ASSIGN_HR_PROJECT_DELETE = (id, company_id) => {
return `${BASE_SIMPRO_LUMEN}/user-to-proyek/delete/${id}/${company_id}`;
};
export const ASSIGN_HR_PROJECT_LIST = `${BASE_SIMPRO_LUMEN}/user-to-proyek/list`;
@ -772,11 +788,11 @@ export const WAYPOINT_SEARCH = `${BASE_SIMPRO_LUMEN}/waypoint/search`;
export const IMAGE_UPLOAD = `${BASE_SIMPRO_LUMEN}/image/upload`;
export const IMAGE_MULTIPLE_UPLOAD = `${BASE_SIMPRO_LUMEN}/image/multiple-upload`;
export const IMAGE_MULTIPLE_DELETE = (id, category) => {
return `${BASE_SIMPRO_LUMEN}/image/multiple-delete/${id}/${category}`;
export const IMAGE_MULTIPLE_DELETE = (id, category, company_id) => {
return `${BASE_SIMPRO_LUMEN}/image/multiple-delete/${id}/${category}/${company_id}`;
}
export const IMAGE_DELETE = (id, category) => {
return `${BASE_SIMPRO_LUMEN}/image/delete/${id}/${category}`;
export const IMAGE_DELETE = (id, category, company_id) => {
return `${BASE_SIMPRO_LUMEN}/image/delete/${id}/${category}/${company_id}`;
}

6
src/routes.js

@ -47,6 +47,7 @@ const Shift = React.lazy(() => import('./views/SimproV2/Shift'));
const TestGantt = React.lazy(() => import('./views/testgantt'));
const UserAdmin = React.lazy(() => import('./views/Master/UserAdmin'));
const UserShift = React.lazy(() => import('./views/SimproV2/UserShift'));
const Kanban = React.lazy(() => import('./views/SimproV2/Kanban'));
// const DashboardProject = React.lazy(() => import('./views/DashboardProject'));
const DashboardBOD = React.lazy(() => import('./views/Dashboard/DashboardBOD'));
const DashboardCustomer = React.lazy(() => import('./views/Dashboard/DashboardCustomer'));
@ -71,7 +72,6 @@ const routes = [
{ path: '/human-resource', exact: true, name: 'Human Resource', component: ResourceWorker },
{ path: '/material-resource', exact: true, name: 'Material Resource', component: ResourceMaterial },
{ path: '/tools-resource', exact: true, name: 'Tools Resource', component: ResourceTools },
{ path: '/control-monitoring', exact: true, name: 'Control Monitoring', component: ControlMonitoring },
{ path: '/control-monitoring-gantt', exact: true, name: 'Control Monitoring Gantt', component: ControlMonitoringGantt },
{ path: '/presensi-resource', exact: true, name: 'Presensi Resource', component: Presensi },
@ -79,9 +79,9 @@ const routes = [
{ path: '/laporan-k3', exact: true, name: 'Laporan K3', component: K3 },
{ path: '/broadcast', exact: true, name: 'Broadcast', component: Broadcast },
{ path: '/panic-button', exact: true, name: 'Tombol Darurat', component: PanicButton },
{ path: '/kanban', exact: true, name: 'Kanban', component: Kanban },
{ path: '/kanban/:proyek_id/:version_gantt_id', exact: true, name: 'Kanban', component: Kanban },
{ path: '/closing', exact: true, name: 'Closing', component: Closing },
{ path: '/menu', exact: true, name: 'Menu', component: Menu },
{ path: '/roles', exact: true, name: 'Roles', component: Roles },
{ path: '/project-role', exact: true, name: 'Project Role', component: ProjectRole },

2
src/views/ControlMonitoringGantt/index.js

@ -2,7 +2,7 @@ import React, { useEffect, useMemo, useState } from 'react';
import Timeline from 'react-calendar-timeline'
import 'react-calendar-timeline/lib/Timeline.css'
import moment from 'moment';
import { BASE_SIMPRO, PROYEK_ADD, PROYEK_SEARCH_DETAIL, GANTT_CONTROL_MONITORING_SEARCH, PROYEK_EDIT, PROYEK_DELETE } from '../../const/ApiConst';
import { BASE_SIMPRO, PROYEK_ADD, GANTT_CONTROL_MONITORING_SEARCH } from '../../const/ApiConst';
import axios from 'axios';
import { NotificationContainer, NotificationManager } from 'react-notifications';
import { Button } from 'antd';

14
src/views/Master/MasterCompany/index.js

@ -53,7 +53,7 @@ const MasterCompany = ({ params }) => {
const [loading, setLoading] = useState(true);
const pageName = params.name;
const { t } = useTranslation();
const configApp = JSON.parse(window.localStorage.getItem('configApp'));
useEffect(() => {
setLoading(true)
getDataCompany()
@ -354,6 +354,7 @@ const MasterCompany = ({ params }) => {
formData.append('ref_id', id);
formData.append('category', 'company_logo_header');
formData.append('files', data);
formData.append('company_name',configApp.company_name);
await axios
.post(IMAGE_UPLOAD, formData, HEADER_MULTIPART)
@ -367,6 +368,7 @@ const MasterCompany = ({ params }) => {
formData.append('ref_id', id);
formData.append('category', 'company_logo_login');
formData.append('files', data);
formData.append('company_name',configApp.company_name);
await axios
.post(IMAGE_UPLOAD, formData, HEADER_MULTIPART)
@ -380,6 +382,7 @@ const MasterCompany = ({ params }) => {
formData.append('ref_id', id);
formData.append('category', 'company_favicon');
formData.append('files', data);
formData.append('company_name',configApp.company_name);
await axios
.post(IMAGE_UPLOAD, formData, HEADER_MULTIPART)
@ -389,6 +392,7 @@ const MasterCompany = ({ params }) => {
};
const saveSliderLogin = async (data) => {
data.company_name = configApp.company_name;
await axios
.post(IMAGE_MULTIPLE_UPLOAD, data, HEADER_MULTIPART)
.then(res => res)
@ -398,7 +402,7 @@ const MasterCompany = ({ params }) => {
// Delete Image Function
const deleteImageHeader = async (id) => {
const URL = IMAGE_DELETE(id, 'company_logo_header');
const URL = IMAGE_DELETE(id, 'company_logo_header', company_id);
await axios
.delete(URL, HEADER)
.then(res => res)
@ -407,7 +411,7 @@ const MasterCompany = ({ params }) => {
};
const deleteImageLogin = async (id) => {
const URL = IMAGE_DELETE(id, 'company_logo_login');
const URL = IMAGE_DELETE(id, 'company_logo_login', company_id);
await axios
.delete(URL, HEADER)
.then(res => res)
@ -416,7 +420,7 @@ const MasterCompany = ({ params }) => {
};
const deleteImageFavicon = async (id) => {
const URL = IMAGE_DELETE(id, 'company_favicon');
const URL = IMAGE_DELETE(id, 'company_favicon', company_id);
await axios
.delete(URL, HEADER)
.then(res => res)
@ -425,7 +429,7 @@ const MasterCompany = ({ params }) => {
};
const deleteImageSlider = async (id) => {
const URL = IMAGE_MULTIPLE_DELETE(id, 'company_slider_login');
const URL = IMAGE_MULTIPLE_DELETE(id, 'company_slider_login', company_id);
await axios
.delete(URL, HEADER)
.then(res => res)

3
src/views/Master/Proyek/index.js

@ -48,6 +48,7 @@ const data = [
const url = "";
const proyek_id = localStorage.getItem('proyek_id');
const role_id = localStorage.getItem('role_id');
const company_id = window.localStorage.getItem('company_id');
const IndexRole = ({ params }) => {
const token = localStorage.getItem("token")
@ -214,7 +215,7 @@ const IndexRole = ({ params }) => {
}
const onConfirmDelete = async () => {
let urlDel = PROYEK_DELETE(idDelete)
let urlDel = PROYEK_DELETE(idDelete, company_id);
const result = await axios.delete(urlDel, HEADER)
.then(res => res)
.catch((error) => error.response);

3
src/views/SimproV2/Closing/index.js

@ -14,6 +14,7 @@ import { formatRupiah } from '../../../const/CustomFunc'
const url = "";
const proyek_id = localStorage.getItem('proyek_id');
const role_id = localStorage.getItem('role_id');
const company_id = window.localStorage.getItem('company_id');
const format = "DD-MM-YYYY";
const Closing = ({ params, ...props }) => {
@ -144,7 +145,7 @@ const Closing = ({ params, ...props }) => {
const toggleAddDialogView = () => setOpenDialogViewDetail(!openDialogViewDetail)
const onConfirmDelete = async () => {
let urlDel = PROYEK_DELETE(idDelete)
let urlDel = PROYEK_DELETE(idDelete, company_id)
const result = await axios.delete(urlDel, HEADER)
.then(res => res)
.catch((error) => error.response);

2
src/views/SimproV2/CreatedProyek/AsignCustProject.js

@ -84,7 +84,7 @@ const AssignCustProject = ({ openDialog, closeDialog, toggleDialog, idTask, proy
}
const onConfirmDelete = async () => {
let urlDel = ASSIGN_HR_PROJECT_DELETE(idDelete)
let urlDel = ASSIGN_HR_PROJECT_DELETE(idDelete, company_id)
const result = await axios.delete(urlDel, HEADER)
.then(res => res)
.catch((error) => error.response);

22
src/views/SimproV2/CreatedProyek/DialogDocument.js

@ -5,7 +5,7 @@ import { Table, Tooltip, Popover, Spin } from 'antd';
import 'antd/dist/antd.css';
import moment from 'moment';
import SweetAlert from 'react-bootstrap-sweetalert';
import { DOCUMENT_DOWNLOAD, DOCUMENT_GET, BASE_SIMPRO_LUMEN_FILE, REQUEST_MATERIAL_EDIT, DOCUMENT_DELETE, DOCUMENT_SEARCH, FOLDER_DOCUMENT_PROYEK_GET_TREE, FOLDER_DOCUMENT_PROYEK_DELETE } from '../../../const/ApiConst';
import { DOCUMENT_DOWNLOAD, DOCUMENT_GET, BASE_SIMPRO_LUMEN_FILE_COMPANY, REQUEST_MATERIAL_EDIT, DOCUMENT_DELETE, DOCUMENT_SEARCH, FOLDER_DOCUMENT_PROYEK_GET_TREE, FOLDER_DOCUMENT_PROYEK_DELETE } from '../../../const/ApiConst';
import axios from "../../../const/interceptorApi"
import { NotificationContainer, NotificationManager } from 'react-notifications';
import DialogRequest from './FormDocument';
@ -18,13 +18,6 @@ const DialogDocument = ({ openDialog, closeDialog, toggleDialog, idTask, proyekN
"Authorization": `Bearer ${token}`
}
}
const config = {
headers:
{
Authorization: `Bearer ${token}`,
"Content-type": `application/json`
}
};
const [dataDocument, setDataDocument] = useState([])
const [openDialogReq, setOpenDialogReq] = useState(false)
const [openDialogNewFolder, setOpenDialogNewFolder] = useState(false)
@ -34,6 +27,7 @@ const DialogDocument = ({ openDialog, closeDialog, toggleDialog, idTask, proyekN
const [dataEdit, setDataEdit] = useState(null)
const [parentIdNewFolder, setParentIdNewFolder] = useState(0)
const [loading, setLoading] = useState(true);
const company_id = window.localStorage.getItem('company_id');
useEffect(() => {
if (idTask > 0) {
@ -58,7 +52,7 @@ const DialogDocument = ({ openDialog, closeDialog, toggleDialog, idTask, proyekN
const getDataDocument = async () => {
const url = FOLDER_DOCUMENT_PROYEK_GET_TREE(idTask)
const result = await axios
.get(url, config)
.get(url, HEADER)
.then(res => res)
.catch((error) => error.response);
if (result && result.data && result.data.code == 200) {
@ -86,7 +80,7 @@ const DialogDocument = ({ openDialog, closeDialog, toggleDialog, idTask, proyekN
}
const handleDownload = (id, file) => {
fetch(DOCUMENT_DOWNLOAD(id), {
fetch(DOCUMENT_DOWNLOAD(id, company_id), {
headers: new Headers({
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
@ -101,14 +95,12 @@ const DialogDocument = ({ openDialog, closeDialog, toggleDialog, idTask, proyekN
a.click();
a.remove();
});
//window.location.href = response.url;
});
// const urlDownload = DOCUMENT_DOWNLOAD(id);
// window.open(urlDownload);
}
const handleShow = (file) => {
const urlShow = `${BASE_SIMPRO_LUMEN_FILE}/${file}`
const configApp = JSON.parse(window.localStorage.getItem('configApp'));
const urlShow = `${BASE_SIMPRO_LUMEN_FILE_COMPANY(file, configApp.company_name)}`
window.open(urlShow);
}
@ -215,7 +207,7 @@ const DialogDocument = ({ openDialog, closeDialog, toggleDialog, idTask, proyekN
}
const onConfirmDelete = async () => {
let urlDel = DOCUMENT_DELETE(idDelete)
let urlDel = DOCUMENT_DELETE(idDelete, company_id)
const result = await axios.delete(urlDel, HEADER)
.then(res => res)
.catch((error) => error.response);

9
src/views/SimproV2/CreatedProyek/FormDocument.js

@ -33,8 +33,11 @@ const DialogRequest = ({ openDialog, closeDialog, toggleDialog, idTask, parentId
}
const uploadDokumen = async () => {
const configApp = JSON.parse(window.localStorage.getItem('configApp'));
const formData = new FormData;
formData.append('dokumen', file, file.name);
formData.append('company_name',configApp.company_name);
if (parentIdNewFolder > 0) {
formData.append('ref_id', parentIdNewFolder); // folder_id
@ -49,12 +52,12 @@ const DialogRequest = ({ openDialog, closeDialog, toggleDialog, idTask, parentId
.post(DOCUMENT_ADD, formData, HEADER)
.then(res => res)
.catch((error) => error.response);
const notif = result.data.message;
if (result && result.status == 200) {
NotificationManager.success('Dokumen project berhasil diupload!!', 'Success');
NotificationManager.success(notif, 'Success');
closeDialog('upload')
} else {
NotificationManager.error('Dokumen project gagal diupload!!', 'Failed');
NotificationManager.error(notif, 'Failed');
closeDialog('failed upload')
}
}

6
src/views/SimproV2/CreatedProyek/index.js

@ -968,7 +968,7 @@ const CreatedProyek = ({ params, ...props }) => {
};
const onConfirmDelete = async () => {
let urlDel = PROYEK_DELETE(idDelete);
let urlDel = PROYEK_DELETE(idDelete, company_id);
const result = await axios
.delete(urlDel, HEADER)
.then((res) => res)
@ -1189,8 +1189,8 @@ const CreatedProyek = ({ params, ...props }) => {
};
const deleteImage = async (id) => {
const URL = IMAGE_DELETE(id, 'project_structure_organization');
const company_id = localStorage.getItem("company_id");
const URL = IMAGE_DELETE(id, 'project_structure_organization', company_id);
await axios
.delete(URL, HEADER)
.then(res => res)

130
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) => (
<Menu
items={[
{
key: '3',
label: (
<a rel="noopener noreferrer" >
status is {column.status_progress}
</a>
),
},
{
key: '1',
label: (
<a rel="noopener noreferrer" onClick={() => handleEditBoard(column)} >
<EditOutlined style={{marginRight: "5px"}} /> Edit
</a>
),
},
{
key: '2',
label: (
<a rel="noopener noreferrer" onClick={() => handleDelete(column.id)} >
<DeleteOutlined style={{marginRight: "5px"}} /> Hapus
</a>
),
},
]}
/>
);
return (
<Col className="gutter-row" span={6}>
<div className='board' style={{backgroundColor: column.header_color}}>
<div className='title-board' style={{color: column.body_color}}>
<b>{column.name_board}</b>
{/* <a style={{color: column.body_color, marginRight: "7px", float: 'right'}} onClick={() => handleEditBoard(column)}><EditOutlined /></a> */}
{/* <a style={{color: column.body_color, marginRight: "7px", float: 'right'}} onClick={() => handleDelete(column.id)}><DeleteOutlined /></a> */}
<div style={{float: 'right', marginRight: "5px"}}>
<Dropdown overlay={menu(column)}>
<a onClick={(e) => e.preventDefault()}>
<MoreOutlined style={{fontSize:"20px"}} />
</a>
</Dropdown>
</div>
</div>
</div>
<Divider/>
<div className='destination'>
{ tasks.length === 0 &&
<p className='add-card' onClick={()=> handleOpenDialogActivity("Save", column.id)}>+ Add Activity</p>
}
<Droppable droppableId={column.key} type='TASK'>
{(provided, snapshot) => (
<TaskList
ref={provided.innerRef}
{...provided.droppableProps}
isDraggingOver={snapshot.isDraggingOver}
>
{tasks.map((task, index) => (
<Task
key={task.id}
task={task}
index={index}
bodyColor={column.body_color}
loadingCard={loadingCard}
handleDeleteCard={handleDeleteCard}
handleEditCard={handleEditCard} />
))}
{provided.placeholder}
</TaskList>
)}
</Droppable>
{ tasks.length > 0 &&
<p className='add-card' onClick={()=> handleOpenDialogActivity("Save", column.id)}>+ Add Activity</p>
}
</div>
</Col>
)
}
export default Column;

72
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 (
<>
{/* <Modal size="lg" isOpen={openDialogChild} toggle={toggleDialogChild} centered>
<ModalHeader className="capitalize" toggle={closeDialogChild}></ModalHeader>
<ModalBody>
</ModalBody>
<ModalFooter>
<Button color="primary" onClick={() => handleSave()}>{typeDialogChild == "Edit" ? `Edit` : "Save"}</Button>{' '}
<Button className="capitalize" color="secondary" onClick={() => handleCancel()}>Batal</Button>
</ModalFooter>
</Modal> */}
<Modal title="Card" open={openDialogChild} onOk={handleCancel} onCancel={handleCancel}
footer={[
<Button key="back" onClick={handleCancel}>
Close
</Button>
]}
>
<Row style={{padding: "20px"}}>
<Col md={6} style={{fontWeight: "bold"}} >Activity</Col>
<Col md={6}>{task.activity}</Col>
</Row>
<Row style={{padding: "20px"}}>
<Col md={6} style={{fontWeight: "bold"}}>Start Date</Col>
<Col md={6}>{moment(task.start_date).format('DD-MM-YYYY HH:mm')}</Col>
</Row>
<Row style={{padding: "20px"}}>
<Col md={6} style={{fontWeight: "bold"}}>End Date</Col>
<Col md={6}>{moment(task.end_date).format('DD-MM-YYYY HH:mm')}</Col>
</Row>
<Row style={{padding: "20px"}}>
<Col md={6} style={{fontWeight: "bold"}}>Bobot Planning </Col>
<Col md={6}>{task.bobot_planning}</Col>
</Row>
<Row style={{padding: "20px"}}>
<Col md={6} style={{fontWeight: "bold"}}>Presentase Progress </Col>
<Col md={6}>{task.persentase_progress}</Col>
</Row>
</Modal>
</>
)
}
export default DialogCard;

347
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 (
<Form>
<Row>
<Col md={12}>
<FormGroup>
<Label className="capitalize">Text</Label>
<Input type="text" value={text} onChange={(e) => setText(e.target.value)} />
</FormGroup>
</Col>
<Col md={12}>
<FormGroup>
<Label className="capitalize">Start Date</Label>
<DatePicker style={{ width: "100%" }} value={startDate} onChange={(dateItem, dateString) => {
dateItem.set({hour:0,minute:0,second:0})
setStartDate(dateItem)}}
/>
</FormGroup>
</Col>
<Col md={12}>
<FormGroup>
<Label className="capitalize">End date</Label>
<DatePicker style={{ width: "100%" }} value={endDate} onChange={(dateItem, dateString) => {
dateItem.set({hour:23,minute:59,second:59})
setEndDate(dateItem)}}
/>
</FormGroup>
</Col>
{/* <Col md={12}>
<FormGroup>
<Label className="capitalize">Duration</Label>
<Input type="number" value={duration} onChange={(e) => setDuration(e.target.value)}/>
</FormGroup>
</Col> */}
<Col md={12}>
<FormGroup>
<Label className="capitalize">Presentase Progress</Label>
{/* <Input type="number" value={progress} onChange={(e) => setProgress(e.target.value)}/> */}
<Progress percent={Math.round(progress)} />
</FormGroup>
</Col>
<Col md={12}>
<FormGroup>
<Label className="capitalize">Volume Plan</Label>
<Input type="number" value={volumePlan} onChange={(e) => setVolumePlan(e.target.value)} />
</FormGroup>
</Col>
<Col md={12}>
<FormGroup>
<Label className="capitalize">Human Resource</Label>
{/* <Select
showSearch
filterOption={(inputValue, option) =>
option.children.toLowerCase().includes(inputValue.toLowerCase())
}
value={hr}
defaultValue={hr}
onChange={onChangeHr}
style={{ width: "100%" }}
>
{dataHr.map((res) => (
<Option key={res.id} value={res.id}>
{res.name}
</Option>
))}
</Select> */}
<Select
value={hr}
defaultValue={hr}
isClearable={false}
isMulti
name="colors"
options={dataHr}
className="basic-multi-select"
classNamePrefix="select"
onChange={onChangeHr}
/>
</FormGroup>
</Col>
<Col md={12}>
<FormGroup>
<Label className="capitalize">Volume Actual : {Math.round(parseInt(progress) / 100 * volumePlan)}</Label>
<div>
<Button onClick={() => handleOpenDialogReport("Save")} type="primary">Detail Report</Button>
</div>
</FormGroup>
</Col>
</Row>
</Form>
)
}
return (
<>
<Drawer title="Basic Drawer" placement="right" onClose={() => handleCancel()} open={openDialogActivity}>
<div className='top'>
<Space>
<Button type='text' onClick={() => handleCancel()}><CloseOutlined /></Button>
</Space>
<Space style={{float:"right"}}>
<Button onClick={() => handleCancel()}>Cancel</Button>
<Button onClick={() => handleSave()} type="primary">
{typeDialogActivity == "Edit" ? `Edit` : "Add"} Activity
</Button>
</Space>
<Divider />
</div>
<div className='content' style={{alignContent:'center'}}>
{renderForm()}
</div>
<div className='bottom' style={{float:'bottom'}}>
</div>
</Drawer>
</>
)
}
export default DialogFormActivity;

176
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 (
<Form>
<Row>
<Col md={12}>
<FormGroup>
<Label className="capitalize">Name Board</Label>
<Input type="text" value={nameBoard} onChange={(e) => setNameBoard(e.target.value)} />
{/* <Input type="text" value={nameBoard} onChange={(e) => setNameBoard(e.target.value)} invalid={nameBoardVal} />
<FormFeedback>
Required!
</FormFeedback> */}
</FormGroup>
</Col>
</Row>
<Row>
<Col md={12}>
<FormGroup>
<Label className="capitalize">Status Progress</Label>
{/* <Input type="text" value={statusProgress} onChange={(e) => setStatusProgress(e.target.value)}/> */}
<Select
showSearch
filterOption={(inputValue, option) =>
option.children.toLowerCase().includes(inputValue.toLowerCase())
}
value={statusProgress}
defaultValue={statusProgress}
onChange={onChangeStatusProgress}
style={{ width: "100%" }}
>
<Option value="none">none</Option>
<Option value="open">open</Option>
<Option value="on-progress">on-progress</Option>
<Option value="done">done</Option>
</Select>
</FormGroup>
</Col>
</Row>
<Row>
<Col md={6}>
<FormGroup>
<Label className="capitalize">Header Color</Label>
<Input type="color" value={headerColor} onChange={(e) => setHeaderColor(e.target.value)} />
</FormGroup>
</Col>
<Col md={6}>
<FormGroup>
<Label className="capitalize">Text Header Color</Label>
<Input type="color" value={bodyColor} onChange={(e) => setBodyColor(e.target.value)} />
</FormGroup>
</Col>
</Row>
</Form>
)
}
return (
<>
<Drawer title="Basic Drawer" placement="right" onClose={() => handleCancel()} open={openDialogBoard}>
<div className='top'>
<Space>
<Button type='text' onClick={() => handleCancel()}><CloseOutlined /></Button>
</Space>
<Space style={{ float: "right" }}>
<Button onClick={() => handleCancel()}>Cancel</Button>
<Button onClick={() => handleSave()} type="primary">
{typeDialogBoard == "Edit" ? `Edit` : "Add"} Board
</Button>
</Space>
<Divider />
</div>
<div className='content' style={{ alignContent: 'center' }}>
{renderForm()}
</div>
<div className='bottom' style={{ float: 'bottom' }}>
</div>
</Drawer>
</>
)
}
export default DialogFormBoard;

69
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 (
<Form>
<Row>
<Col md={6}>
<FormGroup>
<Label className="capitalize">Name</Label>
<Input type="text" value={name} onChange={(e) => setName(e.target.value)}/>
</FormGroup>
</Col>
</Row>
</Form>
)
}
return (
<>
<Modal size="lg" isOpen={openDialogChild} toggle={toggleDialogChild}>
<ModalHeader className="capitalize" toggle={closeDialogChild}>{typeDialogChild == "Edit" ? `Edit` : "Add"} Child</ModalHeader>
<ModalBody>
{renderForm()}
</ModalBody>
<ModalFooter>
<Button color="primary" onClick={() => handleSave()}>{typeDialogChild == "Edit" ? `Edit` : "Save"}</Button>{' '}
<Button className="capitalize" color="secondary" onClick={() => handleCancel()}>Batal</Button>
</ModalFooter>
</Modal>
</>
)
}
export default DialogFormChild;

438
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) => (
<Tooltip title="Delete Report" placement="top">
<Button onClick={() => handleDeleteReport(id)} type="danger" style={{ backgroundColor:'transparent', color:'red', border:'none' }}>
<i class="nav-icon icon-trash"></i>
</Button>
</Tooltip>
),
},
];
const renderForm = () => {
return (
<Form>
<Row>
<Col md={12}>
<FormGroup>
<Label className="capitalize">Report Date</Label>
<DatePicker style={{ width: "100%" }} value={reportDate} onChange={(dateItem, dateString) => {
dateItem.set({ hour: 0, minute: 0, second: 0 })
setReportDate(dateItem)
}}
/>
</FormGroup>
</Col>
{/* <Col md={12}>
<FormGroup>
<Label className="capitalize">Duration</Label>
<Input type="number" value={duration} onChange={(e) => setDuration(e.target.value)}/>
</FormGroup>
</Col> */}
<Col md={12}>
<FormGroup>
<Label className="capitalize">Volume Actual</Label>
<Input type="number" value={volumeActual} onChange={(e) => setVolumeActual(e.target.value)} />
</FormGroup>
</Col>
<Col md={12}>
<FormGroup>
<Label className="capitalize">Human Resource</Label>
{/* <Select
showSearch
filterOption={(inputValue, option) =>
option.children.toLowerCase().includes(inputValue.toLowerCase())
}
value={hr}
defaultValue={hr}
onChange={onChangeHr}
style={{ width: "100%" }}
>
{dataHr.map((res) => (
<Option key={res.id} value={res.id}>
{res.name}
</Option>
))}
</Select> */}
<Select
value={hr}
defaultValue={hr}
isClearable={false}
name="colors"
options={dataHr}
className="basic-multi-select"
classNamePrefix="select"
onChange={onChangeHr}
/>
</FormGroup>
</Col>
<Col md={12}>
<FormGroup>
<Label className="capitalize">Description</Label>
<Input type="textarea" value={description} onChange={(e) => setDescription(e.target.value)} />
</FormGroup>
</Col>
<div>
{reportActivity.length > 0 ? (
<Table dataSource={reportActivity} columns={columns} />
) : null}
</div>
</Row>
</Form>
)
}
return (
<>
<Drawer title="Basic Drawer" placement="right" onClose={() => handleCancel()} open={openDialogReport}>
<SweetAlert
show={alertDelete}
warning
showCancel
confirmBtnText="Delete"
confirmBtnBsStyle="danger"
title={`Are you sure?`}
onConfirm={onConfirmDelete}
onCancel={() => cancelDelete()}
focusCancelBtn
>
Delete this data
</SweetAlert>
<div className='top'>
<Space>
<Button type='text' onClick={() => handleCancel()}><CloseOutlined /></Button>
</Space>
<Space style={{ float: "right" }}>
<Button onClick={() => handleCancel()}>Cancel</Button>
<Button onClick={() => handleSave()} type="primary">
{typeDialogReport == "Edit" ? `Edit` : "Add"} Report
</Button>
</Space>
<Divider />
</div>
<div className='content' style={{ alignContent: 'center' }}>
{renderForm()}
</div>
<div className='bottom' style={{ float: 'bottom' }}>
</div>
</Drawer>
</>
)
}
export default DialogFormReport;

136
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;
}

206
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) => (
<Menu
items={[
{
key: '1',
label: (
<a rel="noopener noreferrer" onClick={()=>handleEditCard(task)}>
<EditOutlined style={{marginRight: "5px"}} /> Edit
</a>
),
},
{
key: '2',
label: (
<a rel="noopener noreferrer" onClick={()=> handleDeleteCard(task.id)}>
<DeleteOutlined style={{marginRight: "5px"}} /> Hapus
</a>
),
},
]}
/>
);
return (
<Fragment>
<DialogCard
openDialogChild={openDialogChild}
closeDialogChild={handleCloseDialogChild}
toggleDialogChild={() => toggleAddDialogChild}
typeDialogChild={typeDialogChild}
clickOpenModalChild={clickOpenModalChild}
task={task}
/>
<Draggable
draggableId={task.id}
index={index}
isDragDisabled={isDragDisabled}
>
{(provided, snapshot) => (
<Card
className='cards'
{...provided.draggableProps}
{...provided.dragHandleProps}
ref={provided.innerRef}
isDragging={snapshot.isDragging}
isDragDisabled={isDragDisabled}
loading={loadingCard}
onDoubleClick={()=> handleEditCard(task)}
>
<div className='contents' >
<div style={{float: 'right'}}>
<Dropdown overlay={menu(task)}>
<a onClick={(e) => e.preventDefault()}>
<MoreOutlined style={{fontSize:"20px"}} />
</a>
</Dropdown>
</div>
<div><b>{task.activity}</b></div>
</div>
<div className='status' style={{marginTop: "15px"}}>
<Progress percent={Math.round(task.persentase_progress)} />
</div>
<div style={{marginTop: "20px"}}>
<div style={{float: 'left'}}>
<Avatar.Group>
{
task.assign_hr.map( (item, index) => {
if (index < 8) {
return <Tooltip title={item.name} placement="top">
<Avatar
style={{
backgroundColor: '#87d',
}}
icon={<UserOutlined />} size="small"
/>
</Tooltip>
} else if(index == 8) {
return <Avatar
style={{
backgroundColor: '#f56a00',
}}
size="small">
{/* + {["1","1","1","1","1","1","1","1"].length - 3} */}
+ {task.assign_hr.length - 8}
</Avatar>
}
})
}
</Avatar.Group>
</div>
<div style={{float: 'right'}}>
<p style={{color: "grey", fontSize: "12px", marginTop: "3px"}}>{moment(task.start_date).format('DD-MM-YYYY HH:mm')}</p>
</div>
</div>
</Card>
)}
</Draggable>
</Fragment>
)
}
export default Task;

912
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 (
<Option key={index} value={val.id}>{val.name_version}</Option>
)
})}
</>
)
}
const setupSelectDataProyek = () => {
return (
<>
{dataProyek.map((val, index) => {
return (
<Option key={index} value={val.id}>{val.nama}</Option>
)
})}
</>
)
}
const RenderStateKanban = useMemo(() => {
return (<>
{
stateKanban.columns.map((column) => {
return <Column
key={column.id}
column={column}
tasks={column.tasks}
handleOpenDialogBoard={handleOpenDialogBoard}
handleOpenDialogActivity={handleOpenDialogActivity}
handleOpenDialogChild={handleOpenDialogChild}
handleDelete={handleDelete}
handleDeleteCard={handleDeleteCard}
handleEditBoard={handleEditBoard}
loadingCard={loadingCard}
handleEditCard={handleEditCard}
/>
})
}
</>)
}, [stateKanban, loadingCard])
const RenderDialogFormActivity = useMemo(
() => (
<DialogFormActivity
activityProject={activityProject}
openDialogActivity={openDialogActivity}
closeDialogActivity={handleCloseDialogActivity}
handleOpenDialogReport={handleOpenDialogReport}
toggleDialogActivity={() => 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(
() => (
<DialogFormReport
activityProject={activityProject}
openDialogReport={openDialogReport}
closeDialogReport={handleCloseDialogReport}
typeDialogReport={typeDialogReport}
clickOpenModalReport={clickOpenModalActivity}
proyek_id={proyek_id}
version_gantt_id={version_gantt_id}
idBoard={idBoard}
dataHr={dataHr}
dataEditCard={dataEditCard}
userToActivityAdd={userToActivityAdd}
userToActivityDelete={userToActivityDelete}
/>
),
[openDialogReport]
);
return (
<Fragment>
<NotificationContainer />
<SweetAlert
show={alertDelete}
warning
showCancel
confirmBtnText="Delete"
confirmBtnBsStyle="danger"
title={`Are you sure?`}
onConfirm={onConfirmDelete}
onCancel={() => cancelDelete()}
focusCancelBtn
>
Delete this data
</SweetAlert>
<SweetAlert
show={alertDeleteCard}
warning
showCancel
confirmBtnText="Delete"
confirmBtnBsStyle="danger"
title={`Are you sure?`}
onConfirm={onConfirmDeleteCard}
onCancel={() => cancelDeleteCard()}
focusCancelBtn
>
Delete this data
</SweetAlert>
<DialogFormBoard
openDialogBoard={openDialogBoard}
closeDialogBoard={handleCloseDialogBoard}
toggleDialogBoard={() => toggleDialogBoard}
typeDialogBoard={typeDialogBoard}
clickOpenModalBoard={clickOpenModalBoard}
dataEditBoard={dataEditBoard}
proyek_id={project}
version_gantt_id={versionGantt}
/>
{RenderDialogFormActivity}
{RenderDialogFormReport}
<DialogFormChild
openDialogChild={openDialogChild}
closeDialogChild={handleCloseDialogChild}
toggleDialogChild={() => toggleAddDialogChild}
typeDialogChild={typeDialogChild}
clickOpenModalChild={clickOpenModalChild}
/>
<div>
<Card>
<CardHeader style={{ display: "flex", justifyContent: "space-between" }}>
<h4 className="capitalize">Kanban</h4>
<Row>
<Col>
<Select
showSearch
filterOption={(input, option) => (
option?.children ?? '').toLowerCase().includes(input.toLowerCase()
)}
defaultValue={proyek_id ? parseInt(proyek_id) : 'Pilih Project'}
onChange={onChangeProject}
style={{
width: 200,
marginRight: 10
}}
// options={optionProyek}
>
{setupSelectDataProyek()}
</Select>
<Select
showSearch
filterOption={(input, option) => (
option?.children ?? '').toLowerCase().includes(input.toLowerCase()
)}
defaultValue={version_gantt_id ? parseInt(version_gantt_id) : 'Pilih Gantt'}
onChange={onChangeGantt}
style={{
width: 200,
marginRight: 10
}}
// option={optionGantt}
>
{setupSelectVersionGantt()}
</Select>
</Col>
</Row>
</CardHeader>
</Card>
{project != "" && versionGantt != 0 ? (
<>
<Row gutter={16}>
<Col className="gutter-row" span={6}>
<div className='board-add' onClick={() => handleOpenDialogBoard("Save")} >
<div className='title-board-add'>
<div>
+ Add Board
</div>
</div>
</div>
</Col>
</Row>
<Row gutter={16} className='body'>
<DragDropContext onDragEnd={onDragEnd}>
{RenderStateKanban}
</DragDropContext>
</Row>
</>
) : (
<p style={{ textAlign: 'center' }}>Please select Project and Gantt Option</p>
)}
</div>
</Fragment>
);
}
export default Kanban;

4
src/views/SimproV2/ResourceWorker/DialogForm.js

@ -135,6 +135,7 @@ const DialogForm = ({ openDialog, closeDialog, toggleDialog, typeDialog, dataEdi
username,
password,
email,
company_id: parseInt(company_id)
}
closeDialog('edit', data);
@ -215,8 +216,7 @@ const DialogForm = ({ openDialog, closeDialog, toggleDialog, typeDialog, dataEdi
<Col md={6}>
<FormGroup>
<Label className="capitalize">{t('nik')} <span style={{ color: "red" }}>*</span></Label>
{/* <Input type="text" value={ktpNumber} onChange={(e) => setKtpNumber(e.target.value.replace(/[^0-9]/g, ''))} placeholder={`Input NIK (KTP)...`} maxLength="16" /> */}
<Input type="text" value={ktpNumber} onChange={(e) => setKtpNumber(e.target.value.replace(/[^0-9]/g, ''))} placeholder={t('inputNik')} maxLength="16" />
<Input type="text" value={ktpNumber} onChange={(e) => setKtpNumber(e.target.value)} placeholder={'KTP/ID Card'} maxLength="16" />
</FormGroup>
</Col>
<Col md={6}>

Loading…
Cancel
Save