root
11 months ago
48 changed files with 4069 additions and 2429 deletions
After Width: | Height: | Size: 103 KiB |
@ -0,0 +1,418 @@
|
||||
import * as XLSX from 'xlsx'; |
||||
import DialogForm from './DialogForm.js'; |
||||
import React, { useState, useEffect, useMemo } from 'react'; |
||||
import SweetAlert from 'react-bootstrap-sweetalert'; |
||||
import axios from 'axios'; |
||||
import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap'; |
||||
import { Card, CardBody, CardHeader, Col, Row, Input } from 'reactstrap'; |
||||
import { MENU_ADD, MENU_SEARCH, MENU_EDIT, MENU_DELETE, MENU_LIST } from '../../../const/ApiConst.js'; |
||||
import { NotificationContainer, NotificationManager } from 'react-notifications'; |
||||
import { Pagination, Tooltip, Table, Spin } from 'antd'; |
||||
import { useTranslation } from 'react-i18next'; |
||||
const token = window.localStorage.getItem('token'); |
||||
const column = [ |
||||
{ name: "Nama" }, |
||||
{ name: "Url" }, |
||||
{ name: "Ikon" }, |
||||
{ name: "Alias" }, |
||||
{ name: "Urutan" }, |
||||
{ name: "Parent" }, |
||||
] |
||||
|
||||
const Index = ({ params, openDialog, toggleDialog, closeDialog }) => { |
||||
const [alertDelete, setAlertDelete] = useState(false) |
||||
const [allDataMenu, setAllDataMenu] = useState([]) |
||||
const [clickOpenModal, setClickOpenModal] = useState(false) |
||||
const [currentPage, setCurrentPage] = useState(1) |
||||
const [dataEdit, setDataEdit] = useState([]) |
||||
const [dataExport, setDataExport] = useState([]) |
||||
const [dataTable, setDatatable] = useState([]) |
||||
const [idDelete, setIdDelete] = useState(0) |
||||
const [rowsPerPage, setRowsPerPage] = useState(10) |
||||
const [search, setSearch] = useState('') |
||||
const [tooltipDelete, setTooltipDelete] = useState(false) |
||||
const [tooltipEdit, setTooltipEdit] = useState(false) |
||||
const [tooltipExport, setTooltipExport] = useState(false) |
||||
const [tooltipTambah, setTooltipTambah] = useState(false) |
||||
const [totalPage, setTotalPage] = useState(0) |
||||
const [typeDialog, setTypeDialog] = useState('Save') |
||||
const [loading, SetLoading] = useState(false) |
||||
const { t } = useTranslation() |
||||
// const pageName = params.name;
|
||||
|
||||
const config = { |
||||
headers: |
||||
{ |
||||
Authorization: `Bearer ${token}`, |
||||
"Content-type": `application/json` |
||||
} |
||||
}; |
||||
|
||||
useEffect(() => { |
||||
getDataMenu(); |
||||
}, [search, currentPage, rowsPerPage]) |
||||
|
||||
useEffect(() => { |
||||
const cekData = dataExport || [] |
||||
if (cekData.length > 0) { |
||||
exportExcel() |
||||
} |
||||
}, [dataExport]) |
||||
|
||||
const handleSearch = e => { |
||||
const value = e.target.value |
||||
setSearch(value); |
||||
setCurrentPage(1) |
||||
}; |
||||
|
||||
const getDataAllMenu = async () => { |
||||
const result = await axios |
||||
.get(MENU_LIST, config) |
||||
.then(res => res) |
||||
.catch((error) => error.response); |
||||
|
||||
if (result && result.data && result.data.code == 200) { |
||||
let arr = [] |
||||
let dataRes = result.data.data; |
||||
for (const v in dataRes) { |
||||
arr.push(dataRes[v]) |
||||
} |
||||
setAllDataMenu(arr); |
||||
} else { |
||||
NotificationManager.error('Gagal Mengambil Data!!', 'Failed'); |
||||
} |
||||
} |
||||
|
||||
const getDataMenu = async () => { |
||||
SetLoading(true) |
||||
let start = 0; |
||||
|
||||
if (currentPage !== 1 && currentPage > 1) { |
||||
start = (currentPage * rowsPerPage) - rowsPerPage |
||||
} |
||||
|
||||
const payload = { |
||||
"paging": { "start": start, "length": rowsPerPage }, |
||||
"columns": [ |
||||
{ "name": "name", "logic_operator": "ilike", "value": search, "operator": "AND" } |
||||
], |
||||
"joins": [{ |
||||
"name": "m_menu", |
||||
"column_join": "parent_id", |
||||
"column_results": [ |
||||
"name" |
||||
] |
||||
}], |
||||
"orders": { "columns": ["id"], "ascending": false } |
||||
} |
||||
|
||||
const result = await axios |
||||
.post(MENU_SEARCH, payload, config) |
||||
.then(res => res) |
||||
.catch((error) => error.response); |
||||
|
||||
if (result && result.data && result.data.code == 200) { |
||||
setDatatable(result.data.data); |
||||
setTotalPage(result.data.totalRecord); |
||||
SetLoading(false) |
||||
} else { |
||||
NotificationManager.error('Gagal Mengambil Data!!', 'Failed'); |
||||
SetLoading(false) |
||||
} |
||||
} |
||||
|
||||
const handleOpenDialog = async (type) => { |
||||
await setTypeDialog(type) |
||||
getDataAllMenu(); |
||||
// setOpenDialog(true)
|
||||
} |
||||
|
||||
const handleCloseDialog = (type, data) => { |
||||
if (type === "save") { |
||||
saveMenu(data); |
||||
} else if (type === "edit") { |
||||
editMenu(data); |
||||
} |
||||
setDataEdit([]) |
||||
// setOpenDialog(false)
|
||||
} |
||||
|
||||
const toggleAddDialog = () => { |
||||
// setOpenDialog(!openDialog)
|
||||
} |
||||
|
||||
const handleCancel = () => { |
||||
closeDialog('cancel', 'none') |
||||
// setDatatable([]);
|
||||
// setBaseUrl([]);
|
||||
// setAvailableBaseUrl(false);
|
||||
// setLoading(false);
|
||||
} |
||||
|
||||
|
||||
const onConfirmDelete = async () => { |
||||
const url = MENU_DELETE(idDelete) |
||||
const result = await axios.delete(url, config) |
||||
.then(res => res) |
||||
.catch((error) => error.response); |
||||
if (result && result.data && result.data.code === 200) { |
||||
getDataMenu() |
||||
setIdDelete(0) |
||||
setAlertDelete(false) |
||||
NotificationManager.success(`Data menu berhasil dihapus`, 'Success!!'); |
||||
} else { |
||||
setIdDelete(0) |
||||
setAlertDelete(false) |
||||
NotificationManager.error(`Data menu gagal dihapus`, 'Failed!!'); |
||||
} |
||||
} |
||||
|
||||
const saveMenu = async (data) => { |
||||
const formData = data |
||||
|
||||
const result = await axios.post(MENU_ADD, formData, config) |
||||
.then(res => res) |
||||
.catch((error) => error.response); |
||||
|
||||
if (result && result.data && result.data.code === 200) { |
||||
getDataMenu(); |
||||
getDataAllMenu(); |
||||
NotificationManager.success(`Data menu berhasil ditambahkan`, 'Success!!'); |
||||
} else { |
||||
NotificationManager.error(`Data menu gagal ditambahkan`, 'Failed!!'); |
||||
} |
||||
|
||||
} |
||||
|
||||
const editMenu = async (data) => { |
||||
const formData = data |
||||
const url = MENU_EDIT(data.id) |
||||
const result = await axios.put(url, formData, config) |
||||
.then(res => res) |
||||
.catch((error) => error.response); |
||||
|
||||
if (result && result.data && result.data.code === 200) { |
||||
getDataMenu(); |
||||
NotificationManager.success(`Data menu berhasil diubah`, 'Success!!'); |
||||
} else { |
||||
NotificationManager.error(`Data menu gagal diubah`, `Failed!!`); |
||||
} |
||||
} |
||||
|
||||
const handleEdit = (data) => { |
||||
setDataEdit(data) |
||||
handleOpenDialog('Edit'); |
||||
} |
||||
|
||||
const handleDelete = async (id) => { |
||||
await setAlertDelete(true) |
||||
await setIdDelete(id) |
||||
} |
||||
|
||||
const onShowSizeChange = (current, pageSize) => { |
||||
setRowsPerPage(pageSize) |
||||
} |
||||
|
||||
const onPagination = (current, pageSize) => { |
||||
setCurrentPage(current) |
||||
} |
||||
|
||||
const toggle = (param) => { |
||||
if (param === "edit") { |
||||
setTooltipEdit(!tooltipEdit) |
||||
} else if (param === "delete") { |
||||
setTooltipDelete(!tooltipDelete) |
||||
} else if (param === "tambah") { |
||||
setTooltipTambah(!tooltipTambah) |
||||
} else if (param === "export") { |
||||
setTooltipExport(!tooltipExport) |
||||
} |
||||
} |
||||
|
||||
const handleExportExcel = async () => { |
||||
const payload = { |
||||
"paging": { "start": 0, "length": -1 }, |
||||
"columns": [ |
||||
{ "name": "name", "logic_operator": "ilike", "value": search, "operator": "AND" } |
||||
], |
||||
"joins": [{ |
||||
"name": "m_menu", |
||||
"column_join": "parent_id", |
||||
"column_results": [ |
||||
"name" |
||||
] |
||||
}], |
||||
"orders": { "columns": ["id"], "ascending": false } |
||||
} |
||||
|
||||
const result = await axios |
||||
.post(MENU_SEARCH, payload, config) |
||||
.then(res => res) |
||||
.catch((error) => error.response); |
||||
|
||||
if (result && result.data && result.data.code == 200) { |
||||
let resData = result.data.data; |
||||
const excelData = []; |
||||
resData.map((val, index) => { |
||||
let dataRow = { |
||||
"Nama": val.name, |
||||
"Url": val.url, |
||||
"Icon": val.icon, |
||||
"Alias Name": val.alias_name, |
||||
"Urutan": val.sequence, |
||||
"Parent Name": val.join_first_name ? val.join_first_name : "-" |
||||
} |
||||
excelData.push(dataRow) |
||||
}) |
||||
await setDataExport(excelData); |
||||
} else { |
||||
NotificationManager.error('Gagal Export Data!!', 'Failed'); |
||||
} |
||||
} |
||||
|
||||
const exportExcel = () => { |
||||
const dataExcel = dataExport || []; |
||||
const fileName = `Data Master Menu.xlsx`; |
||||
const ws = XLSX.utils.json_to_sheet(dataExcel); |
||||
const wb = XLSX.utils.book_new(); |
||||
XLSX.utils.book_append_sheet(wb, ws, `Data Master Menu`); |
||||
XLSX.writeFile(wb, fileName); |
||||
setDataExport([]) |
||||
} |
||||
|
||||
const cancelDelete = () => { |
||||
setAlertDelete(false) |
||||
setIdDelete(0) |
||||
} |
||||
|
||||
const renderTable = useMemo(() => { |
||||
const columns = [ |
||||
{ |
||||
title: t('action'), |
||||
dataIndex: '', |
||||
key: 'x', |
||||
render: (text, record) => <> |
||||
<Tooltip title={t('delete')}> |
||||
<i id="TooltipDelete" className="fa fa-trash" style={{ color: 'red', marginRight: '10px', cursor: "pointer" }} onClick={() => handleDelete(text.id)}></i> |
||||
</Tooltip> |
||||
<Tooltip title={t('edit')}> |
||||
<i id="TooltipEdit" className="fa fa-edit" style={{ color: 'green', cursor: "pointer" }} onClick={() => handleEdit(text)}></i> |
||||
</Tooltip> |
||||
</>, |
||||
}, |
||||
{ title: t('name'), dataIndex: 'name', key: 'name' }, |
||||
{ title: 'Url', dataIndex: 'url', key: 'url' }, |
||||
{ title: t('icon'), dataIndex: 'icon', key: 'icon' }, |
||||
{ title: 'Alias', dataIndex: 'alias_name', key: 'alias_name' }, |
||||
{ title: t('order'), dataIndex: 'sequence', key: 'sequence' }, |
||||
{ title: t('parentMenu'), dataIndex: 'join_first_name', key: 'join_first_name', render: (text, record) => (text ? text : "-") } |
||||
]; |
||||
return ( |
||||
<Table |
||||
rowKey="id" |
||||
size="small" |
||||
columns={columns} |
||||
dataSource={dataTable} |
||||
pagination={false} |
||||
/> |
||||
) |
||||
}, [dataTable]) |
||||
|
||||
return ( |
||||
<div> |
||||
<NotificationContainer /> |
||||
<SweetAlert |
||||
show={alertDelete} |
||||
warning |
||||
showCancel |
||||
confirmBtnText="Delete" |
||||
confirmBtnBsStyle="danger" |
||||
title={t('deleteConfirm')} |
||||
onConfirm={onConfirmDelete} |
||||
onCancel={() => cancelDelete()} |
||||
focusCancelBtn |
||||
> |
||||
{t('deleteMsg')} |
||||
</SweetAlert> |
||||
{/* <DialogForm |
||||
openDialog={openDialog} |
||||
closeDialog={handleCloseDialog} |
||||
toggleDialog={() => toggleAddDialog} |
||||
typeDialog={typeDialog} |
||||
dataEdit={dataEdit} |
||||
clickOpenModal={clickOpenModal} |
||||
dataMenu={allDataMenu} |
||||
/> */} |
||||
<Modal size="xl" fullscreen="xl" scrollable={true} isOpen={openDialog} toggle={toggleDialog}> |
||||
<ModalHeader className="capitalize withBtn" toggle={closeDialog} style={{ width: "100%" }}> |
||||
MASTER MENU |
||||
<div style={{ display: 'flex' }}> |
||||
<Tooltip title={t('menuAdd')}> |
||||
<Button style={{ marginLeft: "5px" }} id="TooltipTambah" color="success" onClick={() => handleOpenDialog('Save')}><i className="fa fa-plus"></i></Button> |
||||
</Tooltip> |
||||
<Tooltip title={t('exportExcel')}> |
||||
<Button style={{ marginLeft: "5px" }} id="TooltipExport" color="primary" onClick={() => handleExportExcel()}><i className="fa fa-print"></i></Button> |
||||
</Tooltip> |
||||
</div> |
||||
</ModalHeader> |
||||
<ModalBody> |
||||
<> |
||||
<Spin tip="Loading..." spinning={loading}> |
||||
{renderTable} |
||||
<Pagination |
||||
style={{ marginTop: "25px" }} |
||||
showSizeChanger |
||||
onShowSizeChange={onShowSizeChange} |
||||
onChange={onPagination} |
||||
pageSizeOptions={["10", "25", "50"]} |
||||
total={totalPage} |
||||
pageSize={rowsPerPage} |
||||
current={currentPage} |
||||
/> |
||||
</Spin> |
||||
</> |
||||
|
||||
</ModalBody> |
||||
<ModalFooter> |
||||
{/* {typeDialog !== "Set" && typeDialog !== "Set-Menu" && ( |
||||
<Button color="primary" onClick={() => handleSave()}>{typeDialog}</Button> |
||||
)} */} |
||||
<Button className="capitalize" color="secondary" onClick={() => handleCancel()}>cancel</Button> |
||||
</ModalFooter> |
||||
</Modal> |
||||
{/* <Card> |
||||
<CardHeader style={{ display: "flex", justifyContent: "space-between" }}> |
||||
<h4 className="capitalize">{pageName}</h4> |
||||
<Row> |
||||
<Col> |
||||
<Input onChange={handleSearch} value={search} type="text" name="search" id="search" placeholder={t('searchMenu')} /> |
||||
</Col> |
||||
<Col> |
||||
<Tooltip title={t('menuAdd')}> |
||||
<Button id="TooltipTambah" color="success" onClick={() => handleOpenDialog('Save')}><i className="fa fa-plus"></i></Button> |
||||
</Tooltip> |
||||
<Tooltip title={t('exportExcel')}> |
||||
<Button style={{ marginLeft: "5px" }} id="TooltipExport" color="primary" onClick={() => handleExportExcel()}><i className="fa fa-print"></i></Button> |
||||
</Tooltip> |
||||
</Col> |
||||
</Row> |
||||
</CardHeader> |
||||
<CardBody> |
||||
{renderTable} |
||||
<Pagination |
||||
style={{ marginTop: "25px" }} |
||||
showSizeChanger |
||||
onShowSizeChange={onShowSizeChange} |
||||
onChange={onPagination} |
||||
current={currentPage} |
||||
pageSize={rowsPerPage} |
||||
total={totalPage} |
||||
pageSizeOptions={["10", "15", "20", "25", "30", "35", "40"]} |
||||
/> |
||||
</CardBody> |
||||
</Card> */} |
||||
</div> |
||||
) |
||||
} |
||||
|
||||
export default Index; |
@ -1,397 +1,458 @@
|
||||
import * as XLSX from 'xlsx'; |
||||
import DialogForm from './DialogForm'; |
||||
import React, { useState, useEffect, useMemo } from 'react'; |
||||
import SweetAlert from 'react-bootstrap-sweetalert'; |
||||
import axios from "../../../const/interceptorApi" |
||||
import moment from 'moment' |
||||
import { Card, CardBody, CardHeader, Col, Row, Input } from 'reactstrap'; |
||||
import { NotificationContainer, NotificationManager } from 'react-notifications'; |
||||
import { PROJECT_PHASE_ADD, PROJECT_PHASE_EDIT, PROJECT_PHASE_DELETE, PROJECT_PHASE_SEARCH, PROJECT_PHASE_LIST, BASE_OSPRO } from '../../../const/ApiConst'; |
||||
import { Pagination, Button, Tooltip, Table } from 'antd'; |
||||
import { useTranslation } from 'react-i18next'; |
||||
|
||||
|
||||
const ProjectPhase = ({ params, ...props }) => { |
||||
let role_id = '', user_id = '', proyek_id = '', isLogin = '', token = '', company_id = 0, role_name = ''; |
||||
if (props.location.state && props.location.state.role_id && props.location.state.user_id) { |
||||
role_id = props.location.state.role_id; |
||||
user_id = props.location.state.user_id; |
||||
token = props.location.state.token; |
||||
isLogin = props.location.state.isLogin; |
||||
role_name = props.location.state.role_name; |
||||
} else { |
||||
role_id = localStorage.getItem("role_id"); |
||||
proyek_id = localStorage.getItem("proyek_id"); |
||||
user_id = localStorage.getItem("user_id"); |
||||
token = localStorage.getItem("token"); |
||||
isLogin = localStorage.getItem("isLogin"); |
||||
company_id = localStorage.getItem('company_id'); |
||||
role_name = localStorage.getItem('role_name'); |
||||
} |
||||
|
||||
const HEADER = { |
||||
headers: { |
||||
"Content-Type": "application/json", |
||||
"Authorization": `Bearer ${token}` |
||||
} |
||||
} |
||||
const pageName = params.name; |
||||
|
||||
const [alertDelete, setAlertDelete] = useState(false) |
||||
const [allDataMenu, setAllDataMenu] = useState([]) |
||||
const [clickOpenModal, setClickOpenModal] = useState(false) |
||||
const [currentPage, setCurrentPage] = useState(1) |
||||
const [dataEdit, setDataEdit] = useState([]) |
||||
const [dataExport, setDataExport] = useState([]) |
||||
const [dataTable, setDatatable] = useState([]) |
||||
const [idDelete, setIdDelete] = useState(0) |
||||
const [idPhaseProject, setIdPhaseProject] = useState(0) |
||||
const [openDialog, setOpenDialog] = useState(false) |
||||
const [openDialogIG, setOpenDialogIG] = useState(false) |
||||
const [rowsPerPage, setRowsPerPage] = useState(10) |
||||
const [search, setSearch] = useState('') |
||||
const [totalPage, setTotalPage] = useState(0) |
||||
const [typeDialog, setTypeDialog] = useState('Save') |
||||
const { t } = useTranslation() |
||||
useEffect(() => { |
||||
getDataProjectPhase() |
||||
}, [currentPage, rowsPerPage, search]) |
||||
|
||||
useEffect(() => { |
||||
const cekData = dataExport || [] |
||||
if (cekData.length > 0) { |
||||
exportExcel() |
||||
} |
||||
}, [dataExport]) |
||||
|
||||
const getDataProjectPhase = async () => { |
||||
let start = 0; |
||||
if (currentPage !== 1 && currentPage > 1) { |
||||
start = (currentPage * rowsPerPage) - rowsPerPage |
||||
} |
||||
const payload = { |
||||
"columns": [ |
||||
{ |
||||
"name": "name", |
||||
"logic_operator": "ilike", |
||||
"value": search, |
||||
"operator": "AND" |
||||
} |
||||
], |
||||
"orders": { |
||||
"ascending": true, |
||||
"columns": [ |
||||
'id' |
||||
] |
||||
}, |
||||
"paging": { |
||||
"length": rowsPerPage, |
||||
"start": start |
||||
} |
||||
} |
||||
if (role_name !== "Super Admin") { |
||||
payload.columns.push( |
||||
{ "name": "company_id", "logic_operator": "=", "value": company_id, "operator": "AND" }, |
||||
) |
||||
} else { |
||||
payload.columns.push( |
||||
{ "name": "company_id", "logic_operator": "is null", "value": "", "operator": "AND" }, |
||||
) |
||||
} |
||||
const result = await axios |
||||
.post(PROJECT_PHASE_SEARCH, payload, HEADER) |
||||
.then((res) => res) |
||||
.catch((err) => err.response); |
||||
|
||||
if (result && result.data && result.data.code == 200) { |
||||
result.data.data.map((res) => { |
||||
res.key = res.id.toString() |
||||
}); |
||||
setDatatable(result.data.data); |
||||
setTotalPage(result.data.totalRecord); |
||||
} else { |
||||
NotificationManager.error('Gagal Mengambil Data!!', 'Failed'); |
||||
} |
||||
} |
||||
|
||||
const handleSearch = e => { |
||||
const value = e.target.value |
||||
setSearch(value); |
||||
setCurrentPage(1) |
||||
}; |
||||
const handleOpenDialog = (type) => { |
||||
setOpenDialog(true) |
||||
setTypeDialog(type) |
||||
} |
||||
|
||||
const handleExportExcel = async () => { |
||||
let start = 0; |
||||
|
||||
if (currentPage !== 1 && currentPage > 1) { |
||||
start = (currentPage * rowsPerPage) - rowsPerPage |
||||
} |
||||
|
||||
const payload = { |
||||
"columns": [ |
||||
{ |
||||
"name": "name", |
||||
"logic_operator": "like", |
||||
"value": search, |
||||
"operator": "AND" |
||||
} |
||||
], |
||||
"orders": { |
||||
"ascending": true, |
||||
"columns": [ |
||||
'id' |
||||
] |
||||
}, |
||||
"paging": { |
||||
"length": rowsPerPage, |
||||
"start": start |
||||
} |
||||
} |
||||
|
||||
const result = await axios |
||||
.post(PROJECT_PHASE_SEARCH, payload, HEADER) |
||||
.then(res => res) |
||||
.catch((error) => error.response); |
||||
if (result && result.data && result.data.code == 200) { |
||||
let resData = result.data.data; |
||||
const excelData = []; |
||||
resData.map((val, index) => { |
||||
let dataRow = { |
||||
"Nama": val.name, |
||||
"Color": val.color, |
||||
} |
||||
excelData.push(dataRow) |
||||
}) |
||||
await setDataExport(excelData) |
||||
} else { |
||||
NotificationManager.error('Gagal Export Data!!', 'Failed'); |
||||
} |
||||
} |
||||
|
||||
const exportExcel = () => { |
||||
const dataExcel = dataExport || []; |
||||
const fileName = `Data ${pageName}.xlsx`; |
||||
const ws = XLSX.utils.json_to_sheet(dataExcel); |
||||
const wb = XLSX.utils.book_new(); |
||||
XLSX.utils.book_append_sheet(wb, ws, `Data ${pageName}`); |
||||
XLSX.writeFile(wb, fileName); |
||||
setDataExport([]) |
||||
} |
||||
|
||||
const handleEdit = (data) => { |
||||
setDataEdit(data) |
||||
handleOpenDialog('Edit'); |
||||
} |
||||
|
||||
const handleDelete = async (id) => { |
||||
await setAlertDelete(true) |
||||
await setIdDelete(id) |
||||
} |
||||
|
||||
const handleCloseDialog = (type, data) => { |
||||
if (type === "save") { |
||||
saveProjectPhase(data); |
||||
} else if (type === "edit") { |
||||
editMaterialR(data); |
||||
} |
||||
setDataEdit([]) |
||||
setOpenDialog(false) |
||||
} |
||||
|
||||
const saveProjectPhase = async (data) => { |
||||
const formData = data |
||||
const result = await axios.post(PROJECT_PHASE_ADD, formData, HEADER) |
||||
.then(res => res) |
||||
.catch((error) => error.response); |
||||
|
||||
if (result && result.data && result.data.code === 200) { |
||||
getDataProjectPhase() |
||||
NotificationManager.success(`Data project type berhasil ditambah`, 'Success!!'); |
||||
} else { |
||||
NotificationManager.error(`${result.data.message}`, 'Failed!!'); |
||||
} |
||||
} |
||||
|
||||
const editMaterialR = async (data) => { |
||||
let urlEdit = PROJECT_PHASE_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) { |
||||
getDataProjectPhase(); |
||||
NotificationManager.success(`Data project phase berhasil diedit`, 'Success!!'); |
||||
} else { |
||||
NotificationManager.error(`Data project phase gagal di edit`, `Failed!!`); |
||||
} |
||||
} |
||||
|
||||
const toggleAddDialog = () => { |
||||
setOpenDialog(!openDialog) |
||||
} |
||||
|
||||
const handleDialogIg = (id) => { |
||||
setIdPhaseProject(id) |
||||
setOpenDialogIG(true) |
||||
} |
||||
|
||||
const closeDialogIG = () => { |
||||
setIdPhaseProject(0) |
||||
setOpenDialogIG(false) |
||||
} |
||||
|
||||
const toggleDialogIG = () => { |
||||
if (openDialogIG) { |
||||
setIdPhaseProject(0) |
||||
} |
||||
setOpenDialogIG(!openDialogIG); |
||||
} |
||||
|
||||
const onConfirmDelete = async () => { |
||||
let url = PROJECT_PHASE_DELETE(idDelete); |
||||
|
||||
const result = await axios.delete(url, HEADER) |
||||
.then(res => res) |
||||
.catch((error) => error.response); |
||||
|
||||
if (result && result.data && result.data.code === 200) { |
||||
getDataProjectPhase() |
||||
setIdDelete(0) |
||||
setAlertDelete(false) |
||||
NotificationManager.success(`Data project phase berhasil dihapus!`, 'Success!!'); |
||||
} else { |
||||
setIdDelete(0) |
||||
setAlertDelete(false) |
||||
NotificationManager.error(`Data project phase gagal dihapus!}`, 'Failed!!'); |
||||
} |
||||
} |
||||
|
||||
const cancelDelete = () => { |
||||
setAlertDelete(false) |
||||
setIdDelete(0) |
||||
} |
||||
|
||||
const onShowSizeChange = (current, pageSize) => { |
||||
setRowsPerPage(pageSize) |
||||
} |
||||
|
||||
const onPagination = (current, pageSize) => { |
||||
setCurrentPage(current) |
||||
} |
||||
|
||||
const dataNotAvailable = () => { |
||||
if (dataTable.length === 0) { |
||||
return ( |
||||
<tr> |
||||
<td align="center" colSpan="3">{t('noData')}</td> |
||||
</tr> |
||||
) |
||||
} |
||||
} |
||||
|
||||
const renderTable = useMemo(() => { |
||||
const columns = [ |
||||
{ |
||||
title: t('action'), |
||||
dataIndex: '', |
||||
key: 'x', |
||||
className: 'nowrap', |
||||
render: (text, record) => <> |
||||
<Tooltip title={t('delete')}> |
||||
<i className="fa fa-trash" style={{ color: 'red', marginRight: '10px', cursor: "pointer" }} onClick={() => handleDelete(text.id)}></i> |
||||
</Tooltip> |
||||
<Tooltip title={t('Edit')}> |
||||
<i className="fa fa-edit" style={{ color: 'green', cursor: "pointer" }} onClick={() => handleEdit(text)}></i> |
||||
</Tooltip>{" "} |
||||
</>, |
||||
}, |
||||
{ title: t('phase'), dataIndex: 'name', key: 'name', className: "nowrap" }, |
||||
{ |
||||
title: t('color'), |
||||
dataIndex: 'color', |
||||
key: 'color', |
||||
render: (text) => <> |
||||
<Tooltip title={text}> |
||||
<i className="fa fa-square" style={{ color: text }} ></i> |
||||
</Tooltip> |
||||
</>, |
||||
}, |
||||
]; |
||||
return ( |
||||
<Table |
||||
rowKey="id" |
||||
size="small" |
||||
columns={columns} |
||||
dataSource={dataTable} |
||||
pagination={false} |
||||
/> |
||||
) |
||||
}, [dataTable]) |
||||
|
||||
return ( |
||||
<div> |
||||
<NotificationContainer /> |
||||
<SweetAlert |
||||
show={alertDelete} |
||||
warning |
||||
showCancel |
||||
confirmBtnText="Delete" |
||||
confirmBtnBsStyle="danger" |
||||
title={t('deleteConfirm')} |
||||
onConfirm={onConfirmDelete} |
||||
onCancel={cancelDelete} |
||||
focusCancelBtn |
||||
> |
||||
{t('deleteMsg')} |
||||
</SweetAlert> |
||||
<DialogForm |
||||
openDialog={openDialog} |
||||
closeDialog={handleCloseDialog} |
||||
toggleDialog={() => toggleAddDialog} |
||||
typeDialog={typeDialog} |
||||
dataEdit={dataEdit} |
||||
clickOpenModal={clickOpenModal} |
||||
dataParent={allDataMenu} |
||||
/> |
||||
<Card> |
||||
<CardHeader style={{ display: "flex", justifyContent: "space-between" }}> |
||||
<h4 className="capitalize">{pageName}</h4> |
||||
<Row> |
||||
<Col> |
||||
<Input onChange={handleSearch} value={search} type="text" name="search" id="search" placeholder={t('searchPhase')} /> |
||||
</Col> |
||||
<Col> |
||||
<Tooltip title={t('projectPhase')}> |
||||
<Button style={{ background: "#4caf50", color: "#fff" }} onClick={() => handleOpenDialog('Save')}><i className="fa fa-plus"></i></Button> |
||||
</Tooltip> |
||||
<Tooltip title={t('exportExcel')}> |
||||
<Button style={{ marginLeft: "5px" }} onClick={() => handleExportExcel()}><i className="fa fa-print"></i></Button> |
||||
</Tooltip> |
||||
</Col> |
||||
</Row> |
||||
</CardHeader> |
||||
<CardBody> |
||||
{renderTable} |
||||
<Pagination |
||||
style={{ marginTop: "25px" }} |
||||
showSizeChanger |
||||
onShowSizeChange={onShowSizeChange} |
||||
onChange={onPagination} |
||||
defaultCurrent={currentPage} |
||||
pageSize={rowsPerPage} |
||||
total={totalPage} |
||||
pageSizeOptions={["10", "15", "20", "25", "30", "35", "40"]} |
||||
/> |
||||
</CardBody> |
||||
</Card> |
||||
</div> |
||||
) |
||||
} |
||||
|
||||
export default ProjectPhase; |
||||
import * as XLSX from 'xlsx'; |
||||
import DialogForm from './DialogForm'; |
||||
import React, { useState, useEffect, useMemo } from 'react'; |
||||
import SweetAlert from 'react-bootstrap-sweetalert'; |
||||
import axios from "../../../const/interceptorApi" |
||||
import moment from 'moment' |
||||
import { Card, CardBody, CardHeader, Col, Row, Input } from 'reactstrap'; |
||||
import { NotificationContainer, NotificationManager } from 'react-notifications'; |
||||
import { PROJECT_PHASE_ADD, PROJECT_PHASE_EDIT, PROJECT_PHASE_DELETE, PROJECT_PHASE_SEARCH, COMPANY_MANAGEMENT_LIST, BASE_OSPRO } from '../../../const/ApiConst'; |
||||
import { Pagination, Button, Tooltip, Table } from 'antd'; |
||||
import { useTranslation } from 'react-i18next'; |
||||
|
||||
|
||||
const ProjectPhase = ({ params, ...props }) => { |
||||
let role_id = 0, user_id = 0, isLogin = false, token = '', company_id = 0, all_project = null, role_name = '', hierarchy = [], user_name = ''; |
||||
if (props && props.role_id && props.user_id) { |
||||
role_id = props.role_id; |
||||
user_id = props.user_id; |
||||
token = props.token; |
||||
isLogin = props.isLogin; |
||||
company_id = props.company_id; |
||||
all_project = props.all_project; |
||||
role_name = props.role_name; |
||||
isLogin = props.isLogin; |
||||
hierarchy = props.hierarchy; |
||||
user_name = props.user_name; |
||||
} |
||||
|
||||
const HEADER = { |
||||
headers: { |
||||
"Content-Type": "application/json", |
||||
"Authorization": `Bearer ${token}` |
||||
} |
||||
} |
||||
const pageName = params.name; |
||||
|
||||
const [alertDelete, setAlertDelete] = useState(false) |
||||
const [allDataMenu, setAllDataMenu] = useState([]) |
||||
const [clickOpenModal, setClickOpenModal] = useState(false) |
||||
const [currentPage, setCurrentPage] = useState(1) |
||||
const [dataEdit, setDataEdit] = useState([]) |
||||
const [dataExport, setDataExport] = useState([]) |
||||
const [dataTable, setDatatable] = useState([]) |
||||
const [idDelete, setIdDelete] = useState(0) |
||||
const [idPhaseProject, setIdPhaseProject] = useState(0) |
||||
const [openDialog, setOpenDialog] = useState(false) |
||||
const [openDialogIG, setOpenDialogIG] = useState(false) |
||||
const [rowsPerPage, setRowsPerPage] = useState(10) |
||||
const [search, setSearch] = useState('') |
||||
const [totalPage, setTotalPage] = useState(0) |
||||
const [typeDialog, setTypeDialog] = useState('Save') |
||||
const [listCompany, setListCompany] = useState([]) |
||||
const { t } = useTranslation() |
||||
useEffect(() => { |
||||
getDataProjectPhase() |
||||
}, [currentPage, rowsPerPage, search]) |
||||
|
||||
useEffect(() => { |
||||
const cekData = dataExport || [] |
||||
if (cekData.length > 0) { |
||||
exportExcel() |
||||
} |
||||
}, [dataExport]) |
||||
|
||||
useEffect(() => { |
||||
getDataCompany() |
||||
}, []) |
||||
|
||||
const getDataCompany = async () => { |
||||
const result = await axios |
||||
.get(COMPANY_MANAGEMENT_LIST, HEADER) |
||||
.then(res => res) |
||||
.catch((error) => error.response); |
||||
|
||||
if (result && result.data && result.data.code == 200) { |
||||
setListCompany(result.data.data); |
||||
} |
||||
} |
||||
|
||||
const getDataProjectPhase = async () => { |
||||
let start = 0; |
||||
if (currentPage !== 1 && currentPage > 1) { |
||||
start = (currentPage * rowsPerPage) - rowsPerPage |
||||
} |
||||
const payload = { |
||||
group_column: { |
||||
"operator": "AND", |
||||
"group_operator": "OR", |
||||
"where": [ |
||||
{ |
||||
"name": "name", |
||||
"logic_operator": "~*", |
||||
"value": search, |
||||
} |
||||
] |
||||
}, |
||||
"columns": [], |
||||
"orders": { |
||||
"ascending": true, |
||||
"columns": [ |
||||
'id' |
||||
] |
||||
}, |
||||
"paging": { |
||||
"length": rowsPerPage, |
||||
"start": start |
||||
}, |
||||
'joins': [] |
||||
} |
||||
if (role_name !== "Super Admin") { |
||||
payload.columns.push( |
||||
{ "name": "company_id", "logic_operator": "=", "value": company_id, "operator": "AND" }, |
||||
) |
||||
} else { |
||||
payload.columns.push( |
||||
{ "name": "company_id", "logic_operator": "is null", "value": "", "operator": "AND" }, |
||||
) |
||||
payload.joins.push( |
||||
{ name: "m_company", column_join: "company_id", column_results: ["company_name"] } |
||||
) |
||||
payload.group_column.where.push( |
||||
{ name: "company_name", logic_operator: "~*", value: search, table_name: "m_company" } |
||||
) |
||||
} |
||||
const result = await axios |
||||
.post(PROJECT_PHASE_SEARCH, payload, HEADER) |
||||
.then((res) => res) |
||||
.catch((err) => err.response); |
||||
|
||||
if (result && result.data && result.data.code == 200) { |
||||
result.data.data.map((res) => { |
||||
res.key = res.id.toString() |
||||
}); |
||||
setDatatable(result.data.data); |
||||
setTotalPage(result.data.totalRecord); |
||||
} else { |
||||
NotificationManager.error('Gagal Mengambil Data!!', 'Failed'); |
||||
} |
||||
} |
||||
|
||||
const handleSearch = e => { |
||||
const value = e.target.value |
||||
setSearch(value); |
||||
setCurrentPage(1) |
||||
}; |
||||
const handleOpenDialog = (type) => { |
||||
setOpenDialog(true) |
||||
setTypeDialog(type) |
||||
} |
||||
|
||||
const handleExportExcel = async () => { |
||||
let start = 0; |
||||
|
||||
if (currentPage !== 1 && currentPage > 1) { |
||||
start = (currentPage * rowsPerPage) - rowsPerPage |
||||
} |
||||
|
||||
const payload = { |
||||
group_column: { |
||||
"operator": "AND", |
||||
"group_operator": "OR", |
||||
"where": [ |
||||
{ |
||||
"name": "name", |
||||
"logic_operator": "~*", |
||||
"value": search, |
||||
} |
||||
] |
||||
}, |
||||
"columns": [], |
||||
"orders": { |
||||
"ascending": true, |
||||
"columns": [ |
||||
'id' |
||||
] |
||||
}, |
||||
"paging": { |
||||
"length": rowsPerPage, |
||||
"start": start |
||||
}, |
||||
'joins': [] |
||||
} |
||||
if (role_name !== "Super Admin") { |
||||
payload.columns.push( |
||||
{ "name": "company_id", "logic_operator": "=", "value": company_id, "operator": "AND" }, |
||||
) |
||||
} else { |
||||
payload.columns.push( |
||||
{ "name": "company_id", "logic_operator": "is null", "value": "", "operator": "AND" }, |
||||
) |
||||
payload.joins.push( |
||||
{ name: "m_company", column_join: "company_id", column_results: ["company_name"] } |
||||
) |
||||
payload.group_column.where.push( |
||||
{ name: "company_name", logic_operator: "~*", value: search, table_name: "m_company" } |
||||
) |
||||
} |
||||
|
||||
const result = await axios |
||||
.post(PROJECT_PHASE_SEARCH, payload, HEADER) |
||||
.then(res => res) |
||||
.catch((error) => error.response); |
||||
if (result && result.data && result.data.code == 200) { |
||||
let resData = result.data.data; |
||||
const excelData = []; |
||||
resData.map((val, index) => { |
||||
let dataRow = {}; |
||||
if (role_name === 'Super Admin') { |
||||
dataRow.Company = val.join_first_company_name; |
||||
} |
||||
dataRow.Nama = val.name; |
||||
dataRow.Color = val.color; |
||||
excelData.push(dataRow) |
||||
}) |
||||
await setDataExport(excelData) |
||||
} else { |
||||
NotificationManager.error('Gagal Export Data!!', 'Failed'); |
||||
} |
||||
} |
||||
|
||||
const exportExcel = () => { |
||||
const dataExcel = dataExport || []; |
||||
const fileName = `Data ${pageName}.xlsx`; |
||||
const ws = XLSX.utils.json_to_sheet(dataExcel); |
||||
const wb = XLSX.utils.book_new(); |
||||
XLSX.utils.book_append_sheet(wb, ws, `Data ${pageName}`); |
||||
XLSX.writeFile(wb, fileName); |
||||
setDataExport([]) |
||||
} |
||||
|
||||
const handleEdit = (data) => { |
||||
setDataEdit(data) |
||||
handleOpenDialog('Edit'); |
||||
} |
||||
|
||||
const handleDelete = async (id) => { |
||||
await setAlertDelete(true) |
||||
await setIdDelete(id) |
||||
} |
||||
|
||||
const handleCloseDialog = (type, data) => { |
||||
if (type === "save") { |
||||
saveProjectPhase(data); |
||||
} else if (type === "edit") { |
||||
editProjectPhase(data); |
||||
} |
||||
setDataEdit([]) |
||||
setOpenDialog(false) |
||||
} |
||||
|
||||
const saveProjectPhase = async (data) => { |
||||
const formData = data |
||||
const result = await axios.post(PROJECT_PHASE_ADD, formData, HEADER) |
||||
.then(res => res) |
||||
.catch((error) => error.response); |
||||
|
||||
if (result && result.data && result.data.code === 200) { |
||||
getDataProjectPhase() |
||||
NotificationManager.success(`Data project type berhasil ditambah`, 'Success!!'); |
||||
} else { |
||||
NotificationManager.error(`${result.data.message}`, 'Failed!!'); |
||||
} |
||||
} |
||||
|
||||
const editProjectPhase = async (data) => { |
||||
let urlEdit = PROJECT_PHASE_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) { |
||||
getDataProjectPhase(); |
||||
NotificationManager.success(`Data project phase berhasil diedit`, 'Success!!'); |
||||
} else { |
||||
NotificationManager.error(`Data project phase gagal di edit`, `Failed!!`); |
||||
} |
||||
} |
||||
|
||||
const toggleAddDialog = () => { |
||||
setOpenDialog(!openDialog) |
||||
} |
||||
|
||||
const handleDialogIg = (id) => { |
||||
setIdPhaseProject(id) |
||||
setOpenDialogIG(true) |
||||
} |
||||
|
||||
const closeDialogIG = () => { |
||||
setIdPhaseProject(0) |
||||
setOpenDialogIG(false) |
||||
} |
||||
|
||||
const toggleDialogIG = () => { |
||||
if (openDialogIG) { |
||||
setIdPhaseProject(0) |
||||
} |
||||
setOpenDialogIG(!openDialogIG); |
||||
} |
||||
|
||||
const onConfirmDelete = async () => { |
||||
let url = PROJECT_PHASE_DELETE(idDelete); |
||||
|
||||
const result = await axios.delete(url, HEADER) |
||||
.then(res => res) |
||||
.catch((error) => error.response); |
||||
|
||||
if (result && result.data && result.data.code === 200) { |
||||
getDataProjectPhase() |
||||
setIdDelete(0) |
||||
setAlertDelete(false) |
||||
NotificationManager.success(`Data project phase berhasil dihapus!`, 'Success!!'); |
||||
} else { |
||||
setIdDelete(0) |
||||
setAlertDelete(false) |
||||
NotificationManager.error(`Data project phase gagal dihapus!}`, 'Failed!!'); |
||||
} |
||||
} |
||||
|
||||
const cancelDelete = () => { |
||||
setAlertDelete(false) |
||||
setIdDelete(0) |
||||
} |
||||
|
||||
const onShowSizeChange = (current, pageSize) => { |
||||
setRowsPerPage(pageSize) |
||||
} |
||||
|
||||
const onPagination = (current, pageSize) => { |
||||
setCurrentPage(current) |
||||
} |
||||
|
||||
const dataNotAvailable = () => { |
||||
if (dataTable.length === 0) { |
||||
return ( |
||||
<tr> |
||||
<td align="center" colSpan="3">{t('noData')}</td> |
||||
</tr> |
||||
) |
||||
} |
||||
} |
||||
|
||||
const renderTable = useMemo(() => { |
||||
const columns = [ |
||||
{ |
||||
title: t('action'), |
||||
dataIndex: '', |
||||
key: 'x', |
||||
className: 'nowrap', |
||||
render: (text, record) => <> |
||||
<Tooltip title={t('delete')}> |
||||
<i className="fa fa-trash" style={{ color: 'red', marginRight: '10px', cursor: "pointer" }} onClick={() => handleDelete(text.id)}></i> |
||||
</Tooltip> |
||||
<Tooltip title={t('Edit')}> |
||||
<i className="fa fa-edit" style={{ color: 'green', cursor: "pointer" }} onClick={() => handleEdit(text)}></i> |
||||
</Tooltip>{" "} |
||||
</>, |
||||
}, |
||||
...(role_name === 'Super Admin' ? |
||||
[{ |
||||
title: t('companyName'), |
||||
dataIndex: "join_first_company_name", |
||||
key: "join_first_company_name", |
||||
render: (text, record) => ( |
||||
<span>{record.join_first_company_name}</span> |
||||
) |
||||
}] |
||||
: []), |
||||
{ |
||||
title: t('phase'), dataIndex: 'name', key: 'name', className: "nowrap" |
||||
}, |
||||
{ |
||||
title: t('color'), |
||||
dataIndex: 'color', |
||||
key: 'color', |
||||
render: (text) => <> |
||||
<Tooltip title={text}> |
||||
<i className="fa fa-square" style={{ color: text }} ></i> |
||||
</Tooltip> |
||||
</>, |
||||
}, |
||||
]; |
||||
return ( |
||||
<Table |
||||
rowKey="id" |
||||
size="small" |
||||
columns={columns} |
||||
dataSource={dataTable} |
||||
pagination={false} |
||||
/> |
||||
) |
||||
}, [dataTable]) |
||||
|
||||
return ( |
||||
<div> |
||||
<NotificationContainer /> |
||||
<SweetAlert |
||||
show={alertDelete} |
||||
warning |
||||
showCancel |
||||
confirmBtnText="Delete" |
||||
confirmBtnBsStyle="danger" |
||||
title={t('deleteConfirm')} |
||||
onConfirm={onConfirmDelete} |
||||
onCancel={cancelDelete} |
||||
focusCancelBtn |
||||
> |
||||
{t('deleteMsg')} |
||||
</SweetAlert> |
||||
<DialogForm |
||||
openDialog={openDialog} |
||||
closeDialog={handleCloseDialog} |
||||
toggleDialog={() => toggleAddDialog} |
||||
typeDialog={typeDialog} |
||||
dataEdit={dataEdit} |
||||
clickOpenModal={clickOpenModal} |
||||
dataParent={allDataMenu} |
||||
listCompany={listCompany} |
||||
role_name={role_name} |
||||
company_id={company_id} |
||||
/> |
||||
<Card> |
||||
<CardHeader style={{ display: "flex", justifyContent: "space-between" }}> |
||||
<h4 className="capitalize">{pageName}</h4> |
||||
<Row> |
||||
<Col> |
||||
<Input onChange={handleSearch} value={search} type="text" name="search" id="search" placeholder={t('searchPhase')} /> |
||||
</Col> |
||||
<Col> |
||||
<Tooltip title={t('projectPhase')}> |
||||
<Button style={{ background: "#4caf50", color: "#fff" }} onClick={() => handleOpenDialog('Save')}><i className="fa fa-plus"></i></Button> |
||||
</Tooltip> |
||||
<Tooltip title={t('exportExcel')}> |
||||
<Button style={{ marginLeft: "5px" }} onClick={() => handleExportExcel()}><i className="fa fa-print"></i></Button> |
||||
</Tooltip> |
||||
</Col> |
||||
</Row> |
||||
</CardHeader> |
||||
<CardBody> |
||||
{renderTable} |
||||
<Pagination |
||||
style={{ marginTop: "25px" }} |
||||
showSizeChanger |
||||
onShowSizeChange={onShowSizeChange} |
||||
onChange={onPagination} |
||||
defaultCurrent={currentPage} |
||||
pageSize={rowsPerPage} |
||||
total={totalPage} |
||||
pageSizeOptions={["10", "15", "20", "25", "30", "35", "40"]} |
||||
/> |
||||
</CardBody> |
||||
</Card> |
||||
</div> |
||||
) |
||||
} |
||||
|
||||
export default ProjectPhase; |
||||
|
@ -1,116 +1,182 @@
|
||||
import React, { Component } from 'react' |
||||
import { Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap'; |
||||
import { Button, Form, FormGroup, Label, Input } from 'reactstrap'; |
||||
import 'antd/dist/antd.css'; |
||||
import { withTranslation } from 'react-i18next'; |
||||
const company_id = localStorage.getItem("company_id") |
||||
class DialogForm extends Component { |
||||
constructor(props) { |
||||
super(props) |
||||
this.state = { |
||||
id: 0, |
||||
name: "", |
||||
description: "", |
||||
openDialog: false, |
||||
isParentClick: false, |
||||
company_id |
||||
} |
||||
} |
||||
|
||||
async componentDidMount() { |
||||
this.props.showDialog(this.showDialog); |
||||
} |
||||
|
||||
async componentDidUpdate() { |
||||
if (this.state.isParentClick === true) { |
||||
if (this.props.typeDialog === "Edit") { |
||||
const { dataEdit } = this.props |
||||
this.setState({ |
||||
id: dataEdit.id, |
||||
name: dataEdit.name, |
||||
description: dataEdit.description, |
||||
company_id |
||||
}) |
||||
} else { |
||||
this.setState({ |
||||
id: 0, |
||||
name: "", |
||||
description: "" |
||||
}) |
||||
} |
||||
this.setState({ isParentClick: false }); |
||||
} |
||||
} |
||||
|
||||
|
||||
showDialog = () => { |
||||
this.setState({ isParentClick: true }); |
||||
} |
||||
|
||||
|
||||
handleSave = () => { |
||||
const { |
||||
id, |
||||
name, |
||||
description |
||||
} = this.state |
||||
|
||||
let data = ''; |
||||
if (this.props.typeDialog === "Save") { |
||||
data = { |
||||
id, |
||||
name, |
||||
description, |
||||
company_id |
||||
} |
||||
this.props.closeDialog('save', data); |
||||
} else { |
||||
data = { |
||||
id, |
||||
name, |
||||
description, |
||||
company_id |
||||
} |
||||
this.props.closeDialog('edit', data); |
||||
} |
||||
|
||||
this.setState({ id: 0 }); |
||||
|
||||
} |
||||
|
||||
handleCancel = () => { |
||||
this.props.closeDialog('cancel', 'none') |
||||
} |
||||
|
||||
renderForm = () => { |
||||
const { t } = this.props; |
||||
return ( |
||||
<Form> |
||||
<FormGroup> |
||||
<Label>{this.props.t('name')}</Label> |
||||
<Input type="text" value={this.state.name} onChange={(e) => this.setState({ name: e.target.value })} placeholder={this.props.t('inputName')} /> |
||||
</FormGroup> |
||||
<FormGroup> |
||||
<Label>{this.props.t('description')}</Label> |
||||
<Input type="text" value={this.state.description} onChange={(e) => this.setState({ description: e.target.value })} placeholder={this.props.t('inputDescription')} /> |
||||
</FormGroup> |
||||
</Form> |
||||
) |
||||
} |
||||
|
||||
render() { |
||||
return ( |
||||
<Modal isOpen={this.props.openDialog} toggle={this.props.toggleDialog}> |
||||
<ModalHeader toggle={this.props.closeDialog}>{this.props.typeDialog == "Save" ? "Tambah" : "Edit"} {this.props.t('nameProjectRole')}</ModalHeader> |
||||
<ModalBody> |
||||
{this.renderForm()} |
||||
</ModalBody> |
||||
<ModalFooter> |
||||
<Button color="primary" onClick={() => this.handleSave()}>{this.props.typeDialog}</Button>{' '} |
||||
<Button color="secondary" onClick={() => this.handleCancel()}>{this.props.t('cancel')}</Button> |
||||
</ModalFooter> |
||||
</Modal> |
||||
) |
||||
} |
||||
} |
||||
export default withTranslation()(DialogForm); |
||||
import React, { Component } from 'react' |
||||
import { Modal, ModalHeader, ModalBody, ModalFooter, Button, Form, FormGroup, Label, Input } from 'reactstrap'; |
||||
import 'antd/dist/antd.css'; |
||||
import axios from 'axios'; |
||||
import { COMPANY_MANAGEMENT_LIST } from '../../../const/ApiConst.js'; |
||||
import { withTranslation } from 'react-i18next'; |
||||
import { NotificationManager } from 'react-notifications'; |
||||
import { Select } from 'antd'; |
||||
const { Option } = Select |
||||
class DialogForm extends Component { |
||||
constructor(props) { |
||||
super(props) |
||||
this.state = { |
||||
id: 0, |
||||
name: "", |
||||
description: "", |
||||
openDialog: false, |
||||
companyList: [], |
||||
scompany_id:null, |
||||
isParentClick: false, |
||||
company_id: props.company_id || null, |
||||
role_name: props.role_name || '', |
||||
token: props.token || '', |
||||
config: { |
||||
headers: { |
||||
Authorization: `Bearer ${props.token || ''}`, |
||||
"Content-type": "application/json", |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
async componentDidMount() { |
||||
this.props.showDialog(this.showDialog); |
||||
this.getDataProyekCompany(); |
||||
} |
||||
|
||||
async componentDidUpdate() { |
||||
if (this.state.isParentClick === true) { |
||||
if (this.props.typeDialog === "Edit") { |
||||
const { dataEdit } = this.props |
||||
this.setState({ |
||||
id: dataEdit.id, |
||||
name: dataEdit.name, |
||||
description: dataEdit.description, |
||||
scompany_id : dataEdit.company_id |
||||
}) |
||||
} else { |
||||
this.setState({ |
||||
id: 0, |
||||
name: "", |
||||
description: "", |
||||
scompany_id:null |
||||
}) |
||||
} |
||||
this.setState({ isParentClick: false }); |
||||
} |
||||
} |
||||
|
||||
|
||||
showDialog = () => { |
||||
this.setState({ isParentClick: true }); |
||||
} |
||||
|
||||
getDataProyekCompany = async () => { |
||||
const result = await axios |
||||
.get(COMPANY_MANAGEMENT_LIST, this.state.config) |
||||
.then((res) => res) |
||||
.catch((error) => error.response); |
||||
|
||||
if (result && result.data && result.data.code == 200) { |
||||
let dataRes = result.data.data || []; |
||||
this.setState({companyList: dataRes}) |
||||
} else { |
||||
NotificationManager.error("Gagal Mengambil Data!!", "Failed"); |
||||
} |
||||
}; |
||||
|
||||
validation = () => { |
||||
if (this.state.role_name === 'Super Admin' && !this.state.scompany_id || this.state.scompany_id === "") { |
||||
alert("Company data cannot be empty!"); |
||||
return true; |
||||
} |
||||
} |
||||
|
||||
handleSave = () => { |
||||
const { |
||||
id, |
||||
name, |
||||
description, |
||||
role_name, |
||||
company_id, |
||||
scompany_id |
||||
} = this.state |
||||
|
||||
let data = ''; |
||||
const err = this.validation(); |
||||
if(!err) { |
||||
if (this.props.typeDialog === "Save") { |
||||
data = { |
||||
id, |
||||
name, |
||||
description, |
||||
company_id : role_name !== 'Super Admin' ? company_id : scompany_id, |
||||
} |
||||
this.props.closeDialog('save', data); |
||||
} else { |
||||
data = { |
||||
id, |
||||
name, |
||||
description, |
||||
company_id : role_name !== 'Super Admin' ? company_id : scompany_id, |
||||
} |
||||
this.props.closeDialog('edit', data); |
||||
} |
||||
} |
||||
this.setState({ id: 0 }); |
||||
} |
||||
|
||||
handleCancel = () => { |
||||
this.props.closeDialog('cancel', 'none') |
||||
} |
||||
|
||||
onChangeCompanyProject = (val) => { |
||||
this.setState({scompany_id : val}); |
||||
}; |
||||
|
||||
renderForm = () => { |
||||
const { t } = this.props; |
||||
return ( |
||||
<Form> |
||||
{ |
||||
this.state.role_name === 'Super Admin' && ( |
||||
<FormGroup> |
||||
<Label className="capitalize">Assign Company Project<span style={{ color: "red" }}>*</span></Label> |
||||
<Select |
||||
showSearch |
||||
filterOption={(inputValue, option) => |
||||
option.children.toLowerCase().includes(inputValue.toLowerCase()) |
||||
} |
||||
value={this.state.scompany_id} |
||||
defaultValue={this.state.scompany_id} |
||||
onChange={this.onChangeCompanyProject} |
||||
style={{ width: "100%" }} |
||||
> |
||||
{this.state.companyList.map((res) => ( |
||||
<Option key={res.id} value={res.id}> |
||||
{res.company_name} |
||||
</Option> |
||||
))} |
||||
</Select> |
||||
</FormGroup> |
||||
) |
||||
} |
||||
<FormGroup> |
||||
<Label>{this.props.t('name')}</Label> |
||||
<Input type="text" value={this.state.name} onChange={(e) => this.setState({ name: e.target.value })} placeholder={this.props.t('inputName')} /> |
||||
</FormGroup> |
||||
<FormGroup> |
||||
<Label>{this.props.t('description')}</Label> |
||||
<Input type="text" value={this.state.description} onChange={(e) => this.setState({ description: e.target.value })} placeholder={this.props.t('inputDescription')} /> |
||||
</FormGroup> |
||||
</Form> |
||||
) |
||||
} |
||||
|
||||
render() { |
||||
return ( |
||||
<Modal isOpen={this.props.openDialog} toggle={this.props.toggleDialog}> |
||||
<ModalHeader toggle={this.props.closeDialog}>{this.props.typeDialog == "Save" ? "Tambah" : "Edit"} {this.props.t('nameProjectRole')}</ModalHeader> |
||||
<ModalBody> |
||||
{this.renderForm()} |
||||
</ModalBody> |
||||
<ModalFooter> |
||||
<Button color="primary" onClick={() => this.handleSave()}>{this.props.typeDialog}</Button>{' '} |
||||
<Button color="secondary" onClick={() => this.handleCancel()}>{this.props.t('cancel')}</Button> |
||||
</ModalFooter> |
||||
</Modal> |
||||
) |
||||
} |
||||
} |
||||
export default withTranslation()(DialogForm); |
||||
|
@ -1,339 +1,370 @@
|
||||
import React, { useState, useEffect, useMemo } from 'react'; |
||||
import { Card, CardBody, CardHeader, Col, Row, Input } from 'reactstrap'; |
||||
import { Button } from 'reactstrap'; |
||||
import axios from 'axios'; |
||||
import * as XLSX from 'xlsx'; |
||||
import { NotificationContainer, NotificationManager } from 'react-notifications'; |
||||
import { Pagination, Tooltip, Table, DatePicker } from 'antd'; |
||||
import moment from 'moment'; |
||||
import { PRESENCE_SEARCH, IMAGE_GET_BY_ID } from '../../../const/ApiConst.js'; |
||||
import DialogFoto from './DialogFoto'; |
||||
import { useTranslation } from 'react-i18next'; |
||||
const { RangePicker } = DatePicker; |
||||
const token = window.localStorage.getItem('token'); |
||||
|
||||
|
||||
const Index = ({ params }) => { |
||||
const [dataTable, setDatatable] = useState([]) |
||||
const [search, setSearch] = useState('') |
||||
const [currentPage, setCurrentPage] = useState(1) |
||||
const [totalPage, setTotalPage] = useState(0) |
||||
const [rowsPerPage, setRowsPerPage] = useState(10) |
||||
const [dataExport, setDataExport] = useState([]) |
||||
const [startDate, setStartDate] = useState(moment(moment().format("YYYY-M-D"))) |
||||
const [endDate, setEndDate] = useState(moment(moment().format("YYYY-M-D"))) |
||||
const [currentDay, setCurrentDay] = useState("today") |
||||
const [dataImage, setDataImage] = useState(null) |
||||
const [openImage, setOpenImage] = useState(false) |
||||
const { t } = useTranslation() |
||||
const pageName = params.name; |
||||
|
||||
const config = { |
||||
headers: |
||||
{ |
||||
Authorization: `Bearer ${token}`, |
||||
"Content-type": `application/json` |
||||
} |
||||
}; |
||||
|
||||
useEffect(() => { |
||||
getDataPresence(); |
||||
}, [search, currentPage, rowsPerPage, startDate, endDate]) |
||||
|
||||
useEffect(() => { |
||||
const cekData = dataExport || [] |
||||
if (cekData.length > 0) { |
||||
exportExcel() |
||||
} |
||||
}, [dataExport]) |
||||
|
||||
const handleSearch = e => { |
||||
const value = e.target.value |
||||
setSearch(value); |
||||
setCurrentPage(1) |
||||
}; |
||||
|
||||
const getDataPresence = async () => { |
||||
|
||||
|
||||
let start = 0; |
||||
|
||||
if (currentPage !== 1 && currentPage > 1) { |
||||
start = (currentPage * rowsPerPage) - rowsPerPage |
||||
} |
||||
|
||||
let dateStart = moment(startDate).format("YYYY-MM-DD 00:00:00"); |
||||
let dateEnd = moment(endDate).format("YYYY-MM-DD 23:59:59"); |
||||
|
||||
const payload = { |
||||
"paging": { "start": start, "length": rowsPerPage }, |
||||
"columns": [ |
||||
{ "name": "name", "logic_operator": "ilike", "value": search, "table_name": "m_users" }, |
||||
{ "name": "clock_in", "logic_operator": "range", "value": dateStart, "value1": dateEnd }, |
||||
], |
||||
"joins": [{ |
||||
"name": "m_users", |
||||
"column_join": "user_id", |
||||
"column_results": [ |
||||
"name", |
||||
"ktp_number" |
||||
] |
||||
}], |
||||
"orders": { "columns": ["id"], "ascending": false } |
||||
} |
||||
|
||||
|
||||
|
||||
const result = await axios |
||||
.post(PRESENCE_SEARCH, payload, config) |
||||
.then(res => res) |
||||
.catch((error) => error.response); |
||||
|
||||
|
||||
if (result && result.data && result.data.code == 200) { |
||||
setDatatable(result.data.data); |
||||
setTotalPage(result.data.totalRecord); |
||||
} else { |
||||
NotificationManager.error('Gagal Mengambil Data!!', 'Failed'); |
||||
} |
||||
} |
||||
|
||||
const handleImage = async (id, name) => { |
||||
|
||||
let url = IMAGE_GET_BY_ID(id, "presensi"); |
||||
|
||||
const result = await axios |
||||
.get(url, config) |
||||
.then(res => res) |
||||
.catch((error) => error.response); |
||||
if (result && result.data.code == 200) { |
||||
let dataRes = result.data.data |
||||
let dataImage = { |
||||
name, |
||||
url: dataRes.image |
||||
} |
||||
await setDataImage(dataImage) |
||||
setOpenImage(true) |
||||
} else { |
||||
NotificationManager.error('Data image tidak ditemukan!', 'Failed'); |
||||
} |
||||
} |
||||
|
||||
const closeDialogImage = () => { |
||||
setOpenImage(false) |
||||
setDataImage(null) |
||||
} |
||||
|
||||
const toggleDialogImage = () => { |
||||
if (openImage) { |
||||
setDataImage(null) |
||||
} |
||||
setOpenImage(!openImage) |
||||
} |
||||
|
||||
const onShowSizeChange = (current, pageSize) => { |
||||
setRowsPerPage(pageSize) |
||||
} |
||||
|
||||
const onPagination = (current, pageSize) => { |
||||
setCurrentPage(current) |
||||
} |
||||
|
||||
const renderDurasiKerja = (jamMasuk, jamKeluar) => { |
||||
if (jamMasuk && jamKeluar) { |
||||
let start = moment(jamMasuk), |
||||
end = moment(jamKeluar); |
||||
|
||||
let diff = end.diff(start); |
||||
let result = moment.utc(diff).format('HH:mm:ss'); |
||||
|
||||
if (result) { |
||||
return result; |
||||
} else { |
||||
return "-" |
||||
} |
||||
|
||||
} else { |
||||
return "-" |
||||
} |
||||
} |
||||
|
||||
const handleExportExcel = async () => { |
||||
|
||||
let dateStart = moment(startDate).format("YYYY-MM-DD 00:00:00"); |
||||
let dateEnd = moment(endDate).format("YYYY-MM-DD 23:59:59"); |
||||
|
||||
const payload = { |
||||
"paging": { "start": 0, "length": -1 }, |
||||
"columns": [ |
||||
{ "name": "name", "logic_operator": "ilike", "value": search, "table_name": "m_users" }, |
||||
{ "name": "clock_in", "logic_operator": "range", "value": dateStart, "value1": dateEnd }, |
||||
], |
||||
"joins": [{ |
||||
"name": "m_users", |
||||
"column_join": "user_id", |
||||
"column_results": [ |
||||
"name", |
||||
"ktp_number" |
||||
] |
||||
}], |
||||
"orders": { "columns": ["id"], "ascending": false } |
||||
} |
||||
|
||||
|
||||
|
||||
const result = await axios |
||||
.post(PRESENCE_SEARCH, payload, config) |
||||
.then(res => res) |
||||
.catch((error) => error.response); |
||||
|
||||
|
||||
if (result && result.data && result.data.code == 200) { |
||||
let resData = result.data.data; |
||||
const excelData = []; |
||||
resData.map((val, index) => { |
||||
let dataRow = { |
||||
"NIK/ID Card": val.join_first_ktp_number ? val.join_first_ktp_number : '-', |
||||
"Nama Human Resource": val.join_first_name ? val.join_first_name : '', |
||||
"Jam Masuk": val.clock_in ? moment(val.clock_in).format("D-M-YYYY HH:mm:ss") : '-', |
||||
"Jam Keluar": val.clock_out ? moment(val.clock_out).format("D-M-YYYY HH:mm:ss") : '-', |
||||
"Durasi Kerja": renderDurasiKerja(val.clock_in, val.clock_out), |
||||
"Lokasi Masuk": val.clock_in_loc && val.clock_in_loc !== '' ? val.clock_in_loc : '-', |
||||
"Lokasi Pulang": val.clock_out_loc && val.clock_out_loc !== '' ? val.clock_out_loc : '-', |
||||
"Area Kerja In": val.clock_in_boundary ? "Sesuai" : "Tidak Sesuai", |
||||
"Area Kerja Out": val.clock_out_boundary == null ? "-" : val.clock_out_boundary ? "Sesuai" : "Tidak Sesuai", |
||||
} |
||||
excelData.push(dataRow) |
||||
}) |
||||
await setDataExport(excelData); |
||||
} else { |
||||
NotificationManager.error('Gagal Export Data!!', 'Failed'); |
||||
} |
||||
} |
||||
|
||||
|
||||
const exportExcel = () => { |
||||
const dataExcel = dataExport || []; |
||||
const fileName = `Data ${pageName}.xlsx`; |
||||
const ws = XLSX.utils.json_to_sheet(dataExcel); |
||||
const wb = XLSX.utils.book_new(); |
||||
XLSX.utils.book_append_sheet(wb, ws, `Data ${pageName}`); |
||||
|
||||
XLSX.writeFile(wb, fileName); |
||||
setDataExport([]) |
||||
} |
||||
const renderTable = useMemo(() => { |
||||
const columns = [ |
||||
{ |
||||
title: 'Action', |
||||
dataIndex: '', |
||||
key: 'x', |
||||
render: (text, record) => <> |
||||
<Tooltip title={t('image')}> |
||||
<i id="TooltipEdit" className="fa fa-image" style={{ color: 'green', cursor: "pointer" }} onClick={() => handleImage(text.id, text.join_first_name)}></i> |
||||
</Tooltip> |
||||
</>, |
||||
}, |
||||
{ title: t('nik'), dataIndex: 'join_first_ktp_number', key: 'join_first_ktp_number' }, |
||||
{ title: t('nameHR'), dataIndex: 'join_first_name', key: 'join_first_name' }, |
||||
{ title: t('presenceIn'), dataIndex: 'clock_in', key: 'clock_in', render: (text, record) => (<div style={{ whiteSpace: "nowrap" }}>{text ? moment(text).format("D-M-YYYY HH:mm:ss") : "-"}</div>) }, |
||||
{ title: t('presenceOut'), dataIndex: 'clock_out', key: 'clock_out', render: (text, record) => (<div style={{ whiteSpace: "nowrap" }}>{text ? moment(text).format("D-M-YYYY HH:mm:ss") : "-"}</div>) }, |
||||
{ title: t('workDuration'), render: (text, record) => (<div style={{ whiteSpace: "nowrap" }}>{renderDurasiKerja(record.clock_in, record.clock_out)}</div>) }, |
||||
{ title: t('locIn'), dataIndex: 'clock_in_loc', key: 'clock_in_loc', render: (text, record) => <>{text && text !== '' ? text : '-'}</> }, |
||||
{ title: t('locOut'), dataIndex: 'clock_out_loc', key: 'clock_out_loc', render: (text, record) => <>{text && text !== '' ? text : '-'}</> }, |
||||
{ title: t('workAreaIn'), dataIndex: 'clock_out_loc', key: 'clock_out_loc', render: (text, record) => (<div style={{ whiteSpace: "nowrap", textAlign: "center" }}>{record.clock_in_boundary ? <i className="fa fa-check-circle fa-lg" style={{ color: 'green' }}></i> : <i className="fa fa-times-circle fa-lg" style={{ color: 'red' }}></i>}</div>) }, |
||||
{ title: t('workAreaOut'), dataIndex: 'clock_out_loc', key: 'clock_out_loc', render: (text, record) => (<div style={{ whiteSpace: "nowrap", textAlign: "center" }}>{record.clock_out_boundary == null ? <i class="fa fa-window-minimize fa-lg" style={{ color: 'orange' }}></i> : record.clock_out_boundary ? <i className="fa fa-check-circle fa-lg" style={{ color: 'green' }}></i> : <i className="fa fa-times-circle fa-lg" style={{ color: 'red' }}></i>}</div>) }, |
||||
]; |
||||
return ( |
||||
<Table |
||||
rowKey="id" |
||||
size="small" |
||||
columns={columns} |
||||
dataSource={dataTable} |
||||
pagination={false} |
||||
/> |
||||
) |
||||
}, [dataTable]) |
||||
|
||||
const handleDatePicker = (date, dateString) => { |
||||
setStartDate(date[0]) |
||||
setEndDate(date[1]) |
||||
} |
||||
|
||||
const handleChangeDay = (e) => { |
||||
const val = e.target.value; |
||||
setCurrentDay(val) |
||||
if (val === "today") { |
||||
setStartDate(moment(moment().format("YYYY-M-D"))) |
||||
setEndDate(moment(moment().format("YYYY-M-D"))) |
||||
setCurrentPage(1) |
||||
} else if (val === "3 day") { |
||||
setStartDate(moment(moment().subtract(3, "days").format("YYYY-M-D"))) |
||||
setEndDate(moment(moment().format("YYYY-M-D"))) |
||||
setCurrentPage(1) |
||||
} else if (val === "7 day") { |
||||
setStartDate(moment(moment().subtract(7, "days").format("YYYY-M-D"))) |
||||
setEndDate(moment(moment().format("YYYY-M-D"))) |
||||
setCurrentPage(1) |
||||
} else { |
||||
setStartDate(moment(moment().format("YYYY-M-D"))) |
||||
setEndDate(moment(moment().format("YYYY-M-D"))) |
||||
setCurrentPage(1) |
||||
} |
||||
} |
||||
|
||||
return ( |
||||
<div> |
||||
<NotificationContainer /> |
||||
<DialogFoto |
||||
openDialog={openImage} |
||||
closeDialog={closeDialogImage} |
||||
toggleDialog={toggleDialogImage} |
||||
dataImage={dataImage} |
||||
/> |
||||
<Card> |
||||
<CardHeader style={{ display: "flex", justifyContent: "space-between" }}> |
||||
<h4 className="capitalize">{pageName}</h4> |
||||
<Row> |
||||
<Col> |
||||
<Tooltip title={t('exportExcel')}> |
||||
<Button style={{ marginLeft: "5px" }} id="TooltipExport" color="primary" onClick={() => handleExportExcel()}><i className="fa fa-print"></i></Button> |
||||
</Tooltip> |
||||
</Col> |
||||
</Row> |
||||
</CardHeader> |
||||
<CardBody> |
||||
<div style={{ display: "flex", justifyContent: "space-between" }}> |
||||
<div style={{ width: "100%", display: "flex", alignItems: "center" }}> |
||||
<div style={{ width: "100%", marginRight: "10px", maxWidth: "200px" }}> |
||||
<Input type="select" onChange={(e) => handleChangeDay(e)} defaultValue={currentDay}> |
||||
<option value="today">{t('today')}</option> |
||||
<option value="3 day">{t('3days')}</option> |
||||
<option value="7 day">{t('7days')}</option> |
||||
</Input> |
||||
</div> |
||||
<div style={{ width: "50%", marginTop: "3px" }}> |
||||
<RangePicker format="DD-MM-YYYY" size="default" allowClear={false} value={[startDate, endDate]} onChange={handleDatePicker} />{' '} |
||||
<Button color="primary" onClick={() => getDataPresence()}>{t('search')}</Button> |
||||
</div> |
||||
</div> |
||||
<Input onChange={handleSearch} value={search} type="text" name="search" id="search" placeholder={t('searchHR')} style={{ maxWidth: "200px", marginBottom: "20px" }} /> |
||||
</div> |
||||
{renderTable} |
||||
<Pagination |
||||
style={{ marginTop: "25px" }} |
||||
showSizeChanger |
||||
onShowSizeChange={onShowSizeChange} |
||||
onChange={onPagination} |
||||
current={currentPage} |
||||
pageSize={rowsPerPage} |
||||
total={totalPage} |
||||
pageSizeOptions={["10", "15", "20", "25", "30", "35", "40"]} |
||||
/> |
||||
</CardBody> |
||||
</Card> |
||||
</div> |
||||
) |
||||
} |
||||
|
||||
export default Index; |
||||
import React, { useState, useEffect, useMemo } from 'react'; |
||||
import { Card, CardBody, CardHeader, Col, Row, Input } from 'reactstrap'; |
||||
import { Button } from 'reactstrap'; |
||||
import axios from 'axios'; |
||||
import * as XLSX from 'xlsx'; |
||||
import { NotificationContainer, NotificationManager } from 'react-notifications'; |
||||
import { Pagination, Tooltip, Table, DatePicker } from 'antd'; |
||||
import moment from 'moment'; |
||||
import { PRESENCE_SEARCH, IMAGE_GET_BY_ID } from '../../../const/ApiConst.js'; |
||||
import DialogFoto from './DialogFoto'; |
||||
import { useTranslation } from 'react-i18next'; |
||||
const { RangePicker } = DatePicker; |
||||
|
||||
const Index = ({ params, ...props }) => { |
||||
let role_id = 0, user_id = 0, isLogin = false, token = '', company_id = 0, all_project = null, role_name = '', hierarchy = [], user_name = ''; |
||||
if (props && props.role_id && props.user_id) { |
||||
role_id = props.role_id; |
||||
user_id = props.user_id; |
||||
token = props.token; |
||||
isLogin = props.isLogin; |
||||
company_id = props.company_id; |
||||
all_project = props.all_project; |
||||
role_name = props.role_name; |
||||
isLogin = props.isLogin; |
||||
hierarchy = props.hierarchy; |
||||
user_name = props.user_name; |
||||
} |
||||
|
||||
const [dataTable, setDatatable] = useState([]) |
||||
const [search, setSearch] = useState('') |
||||
const [currentPage, setCurrentPage] = useState(1) |
||||
const [totalPage, setTotalPage] = useState(0) |
||||
const [rowsPerPage, setRowsPerPage] = useState(10) |
||||
const [dataExport, setDataExport] = useState([]) |
||||
const [startDate, setStartDate] = useState(moment(moment().format("YYYY-M-D"))) |
||||
const [endDate, setEndDate] = useState(moment(moment().format("YYYY-M-D"))) |
||||
const [currentDay, setCurrentDay] = useState("today") |
||||
const [dataImage, setDataImage] = useState(null) |
||||
const [openImage, setOpenImage] = useState(false) |
||||
const { t } = useTranslation() |
||||
const pageName = params.name; |
||||
|
||||
const config = { |
||||
headers: |
||||
{ |
||||
Authorization: `Bearer ${token}`, |
||||
"Content-type": `application/json` |
||||
} |
||||
}; |
||||
|
||||
useEffect(() => { |
||||
getDataPresence(); |
||||
}, [search, currentPage, rowsPerPage, startDate, endDate]) |
||||
|
||||
useEffect(() => { |
||||
const cekData = dataExport || [] |
||||
if (cekData.length > 0) { |
||||
exportExcel() |
||||
} |
||||
}, [dataExport]) |
||||
|
||||
const handleSearch = e => { |
||||
const value = e.target.value |
||||
setSearch(value); |
||||
setCurrentPage(1) |
||||
}; |
||||
|
||||
const getDataPresence = async () => { |
||||
let start = 0; |
||||
|
||||
if (currentPage !== 1 && currentPage > 1) { |
||||
start = (currentPage * rowsPerPage) - rowsPerPage |
||||
} |
||||
|
||||
let dateStart = moment(startDate).format("YYYY-MM-DD 00:00:00"); |
||||
let dateEnd = moment(endDate).format("YYYY-MM-DD 23:59:59"); |
||||
|
||||
const payload = { |
||||
"paging": { "start": start, "length": rowsPerPage }, |
||||
"columns": [ |
||||
{ "name": "name", "logic_operator": "ilike", "value": search, "table_name": "m_users" }, |
||||
{ "name": "clock_in", "logic_operator": "range", "value": dateStart, "value1": dateEnd }, |
||||
], |
||||
"joins": [{ |
||||
"name": "m_users", |
||||
"column_join": "user_id", |
||||
"column_results": [ |
||||
"name", |
||||
"ktp_number" |
||||
] |
||||
}], |
||||
"orders": { "columns": ["id"], "ascending": false } |
||||
} |
||||
|
||||
if (role_name === 'Super Admin') { |
||||
payload.joins.push( |
||||
{ name: "m_company", column_join: "company_id", column_results: ["company_name"] } |
||||
) |
||||
payload.columns.push( |
||||
{ name: "company_name", logic_operator: "~*", value: search, table_name: "m_company" } |
||||
) |
||||
} |
||||
|
||||
|
||||
|
||||
const result = await axios |
||||
.post(PRESENCE_SEARCH, payload, config) |
||||
.then(res => res) |
||||
.catch((error) => error.response); |
||||
|
||||
|
||||
if (result && result.data && result.data.code == 200) { |
||||
setDatatable(result.data.data); |
||||
setTotalPage(result.data.totalRecord); |
||||
} else { |
||||
NotificationManager.error('Gagal Mengambil Data!!', 'Failed'); |
||||
} |
||||
} |
||||
|
||||
const handleImage = async (id, name) => { |
||||
|
||||
let url = IMAGE_GET_BY_ID(id, "presensi"); |
||||
|
||||
const result = await axios |
||||
.get(url, config) |
||||
.then(res => res) |
||||
.catch((error) => error.response); |
||||
if (result && result.data.code == 200) { |
||||
let dataRes = result.data.data |
||||
let dataImage = { |
||||
name, |
||||
url: dataRes.image |
||||
} |
||||
await setDataImage(dataImage) |
||||
setOpenImage(true) |
||||
} else { |
||||
NotificationManager.error('Data image tidak ditemukan!', 'Failed'); |
||||
} |
||||
} |
||||
|
||||
const closeDialogImage = () => { |
||||
setOpenImage(false) |
||||
setDataImage(null) |
||||
} |
||||
|
||||
const toggleDialogImage = () => { |
||||
if (openImage) { |
||||
setDataImage(null) |
||||
} |
||||
setOpenImage(!openImage) |
||||
} |
||||
|
||||
const onShowSizeChange = (current, pageSize) => { |
||||
setRowsPerPage(pageSize) |
||||
} |
||||
|
||||
const onPagination = (current, pageSize) => { |
||||
setCurrentPage(current) |
||||
} |
||||
|
||||
const renderDurasiKerja = (jamMasuk, jamKeluar) => { |
||||
if (jamMasuk && jamKeluar) { |
||||
let start = moment(jamMasuk), |
||||
end = moment(jamKeluar); |
||||
|
||||
let diff = end.diff(start); |
||||
let result = moment.utc(diff).format('HH:mm:ss'); |
||||
|
||||
if (result) { |
||||
return result; |
||||
} else { |
||||
return "-" |
||||
} |
||||
|
||||
} else { |
||||
return "-" |
||||
} |
||||
} |
||||
|
||||
const handleExportExcel = async () => { |
||||
|
||||
let dateStart = moment(startDate).format("YYYY-MM-DD 00:00:00"); |
||||
let dateEnd = moment(endDate).format("YYYY-MM-DD 23:59:59"); |
||||
|
||||
const payload = { |
||||
"paging": { "start": 0, "length": -1 }, |
||||
"columns": [ |
||||
{ "name": "name", "logic_operator": "ilike", "value": search, "table_name": "m_users" }, |
||||
{ "name": "clock_in", "logic_operator": "range", "value": dateStart, "value1": dateEnd }, |
||||
], |
||||
"joins": [{ |
||||
"name": "m_users", |
||||
"column_join": "user_id", |
||||
"column_results": [ |
||||
"name", |
||||
"ktp_number" |
||||
] |
||||
}], |
||||
"orders": { "columns": ["id"], "ascending": false } |
||||
} |
||||
|
||||
|
||||
|
||||
const result = await axios |
||||
.post(PRESENCE_SEARCH, payload, config) |
||||
.then(res => res) |
||||
.catch((error) => error.response); |
||||
|
||||
|
||||
if (result && result.data && result.data.code == 200) { |
||||
let resData = result.data.data; |
||||
const excelData = []; |
||||
resData.map((val, index) => { |
||||
let dataRow = { |
||||
"NIK/ID Card": val.join_first_ktp_number ? val.join_first_ktp_number : '-', |
||||
"Nama Human Resource": val.join_first_name ? val.join_first_name : '', |
||||
"Jam Masuk": val.clock_in ? moment(val.clock_in).format("D-M-YYYY HH:mm:ss") : '-', |
||||
"Jam Keluar": val.clock_out ? moment(val.clock_out).format("D-M-YYYY HH:mm:ss") : '-', |
||||
"Durasi Kerja": renderDurasiKerja(val.clock_in, val.clock_out), |
||||
"Lokasi Masuk": val.clock_in_loc && val.clock_in_loc !== '' ? val.clock_in_loc : '-', |
||||
"Lokasi Pulang": val.clock_out_loc && val.clock_out_loc !== '' ? val.clock_out_loc : '-', |
||||
"Area Kerja In": val.clock_in_boundary ? "Sesuai" : "Tidak Sesuai", |
||||
"Area Kerja Out": val.clock_out_boundary == null ? "-" : val.clock_out_boundary ? "Sesuai" : "Tidak Sesuai", |
||||
} |
||||
excelData.push(dataRow) |
||||
}) |
||||
await setDataExport(excelData); |
||||
} else { |
||||
NotificationManager.error('Gagal Export Data!!', 'Failed'); |
||||
} |
||||
} |
||||
|
||||
|
||||
const exportExcel = () => { |
||||
const dataExcel = dataExport || []; |
||||
const fileName = `Data ${pageName}.xlsx`; |
||||
const ws = XLSX.utils.json_to_sheet(dataExcel); |
||||
const wb = XLSX.utils.book_new(); |
||||
XLSX.utils.book_append_sheet(wb, ws, `Data ${pageName}`); |
||||
|
||||
XLSX.writeFile(wb, fileName); |
||||
setDataExport([]) |
||||
} |
||||
const renderTable = useMemo(() => { |
||||
const columns = [ |
||||
{ |
||||
title: 'Action', |
||||
dataIndex: '', |
||||
key: 'x', |
||||
render: (text, record) => <> |
||||
<Tooltip title={t('image')}> |
||||
<i id="TooltipEdit" className="fa fa-image" style={{ color: 'green', cursor: "pointer" }} onClick={() => handleImage(text.id, text.join_first_name)}></i> |
||||
</Tooltip> |
||||
</>, |
||||
}, |
||||
...(role_name === 'Super Admin' |
||||
? [ |
||||
{ |
||||
title: t('company'), |
||||
dataIndex: "join_second_company_name", |
||||
key: "join_second_company_name", |
||||
render: (text, record) => ( |
||||
<span>{record.join_second_company_name}</span> |
||||
) |
||||
}, |
||||
] |
||||
: []), |
||||
{ title: t('nik'), dataIndex: 'join_first_ktp_number', key: 'join_first_ktp_number' }, |
||||
{ title: t('nameHR'), dataIndex: 'join_first_name', key: 'join_first_name' }, |
||||
{ title: t('presenceIn'), dataIndex: 'clock_in', key: 'clock_in', render: (text, record) => (<div style={{ whiteSpace: "nowrap" }}>{text ? moment(text).format("D-M-YYYY HH:mm:ss") : "-"}</div>) }, |
||||
{ title: t('presenceOut'), dataIndex: 'clock_out', key: 'clock_out', render: (text, record) => (<div style={{ whiteSpace: "nowrap" }}>{text ? moment(text).format("D-M-YYYY HH:mm:ss") : "-"}</div>) }, |
||||
{ title: t('workDuration'), render: (text, record) => (<div style={{ whiteSpace: "nowrap" }}>{renderDurasiKerja(record.clock_in, record.clock_out)}</div>) }, |
||||
{ title: t('locIn'), dataIndex: 'clock_in_loc', key: 'clock_in_loc', render: (text, record) => <>{text && text !== '' ? text : '-'}</> }, |
||||
{ title: t('locOut'), dataIndex: 'clock_out_loc', key: 'clock_out_loc', render: (text, record) => <>{text && text !== '' ? text : '-'}</> }, |
||||
{ title: t('workAreaIn'), dataIndex: 'clock_out_loc', key: 'clock_out_loc', render: (text, record) => (<div style={{ whiteSpace: "nowrap", textAlign: "center" }}>{record.clock_in_boundary ? <i className="fa fa-check-circle fa-lg" style={{ color: 'green' }}></i> : <i className="fa fa-times-circle fa-lg" style={{ color: 'red' }}></i>}</div>) }, |
||||
{ title: t('workAreaOut'), dataIndex: 'clock_out_loc', key: 'clock_out_loc', render: (text, record) => (<div style={{ whiteSpace: "nowrap", textAlign: "center" }}>{record.clock_out_boundary == null ? <i class="fa fa-window-minimize fa-lg" style={{ color: 'orange' }}></i> : record.clock_out_boundary ? <i className="fa fa-check-circle fa-lg" style={{ color: 'green' }}></i> : <i className="fa fa-times-circle fa-lg" style={{ color: 'red' }}></i>}</div>) }, |
||||
]; |
||||
return ( |
||||
<Table |
||||
rowKey="id" |
||||
size="small" |
||||
columns={columns} |
||||
dataSource={dataTable} |
||||
pagination={false} |
||||
/> |
||||
) |
||||
}, [dataTable]) |
||||
|
||||
const handleDatePicker = (date, dateString) => { |
||||
setStartDate(date[0]) |
||||
setEndDate(date[1]) |
||||
} |
||||
|
||||
const handleChangeDay = (e) => { |
||||
const val = e.target.value; |
||||
setCurrentDay(val) |
||||
if (val === "today") { |
||||
setStartDate(moment(moment().format("YYYY-M-D"))) |
||||
setEndDate(moment(moment().format("YYYY-M-D"))) |
||||
setCurrentPage(1) |
||||
} else if (val === "3 day") { |
||||
setStartDate(moment(moment().subtract(3, "days").format("YYYY-M-D"))) |
||||
setEndDate(moment(moment().format("YYYY-M-D"))) |
||||
setCurrentPage(1) |
||||
} else if (val === "7 day") { |
||||
setStartDate(moment(moment().subtract(7, "days").format("YYYY-M-D"))) |
||||
setEndDate(moment(moment().format("YYYY-M-D"))) |
||||
setCurrentPage(1) |
||||
} else { |
||||
setStartDate(moment(moment().format("YYYY-M-D"))) |
||||
setEndDate(moment(moment().format("YYYY-M-D"))) |
||||
setCurrentPage(1) |
||||
} |
||||
} |
||||
|
||||
return ( |
||||
<div> |
||||
<NotificationContainer /> |
||||
<DialogFoto |
||||
openDialog={openImage} |
||||
closeDialog={closeDialogImage} |
||||
toggleDialog={toggleDialogImage} |
||||
dataImage={dataImage} |
||||
/> |
||||
<Card> |
||||
<CardHeader style={{ display: "flex", justifyContent: "space-between" }}> |
||||
<h4 className="capitalize">{pageName}</h4> |
||||
<Row> |
||||
<Col> |
||||
<Tooltip title={t('exportExcel')}> |
||||
<Button style={{ marginLeft: "5px" }} id="TooltipExport" color="primary" onClick={() => handleExportExcel()}><i className="fa fa-print"></i></Button> |
||||
</Tooltip> |
||||
</Col> |
||||
</Row> |
||||
</CardHeader> |
||||
<CardBody> |
||||
<div style={{ display: "flex", justifyContent: "space-between" }}> |
||||
<div style={{ width: "100%", display: "flex", alignItems: "center" }}> |
||||
<div style={{ width: "100%", marginRight: "10px", maxWidth: "200px" }}> |
||||
<Input type="select" onChange={(e) => handleChangeDay(e)} defaultValue={currentDay}> |
||||
<option value="today">{t('today')}</option> |
||||
<option value="3 day">{t('3days')}</option> |
||||
<option value="7 day">{t('7days')}</option> |
||||
</Input> |
||||
</div> |
||||
<div style={{ width: "50%", marginTop: "3px" }}> |
||||
<RangePicker format="DD-MM-YYYY" size="default" allowClear={false} value={[startDate, endDate]} onChange={handleDatePicker} />{' '} |
||||
<Button color="primary" onClick={() => getDataPresence()}>{t('search')}</Button> |
||||
</div> |
||||
</div> |
||||
<Input onChange={handleSearch} value={search} type="text" name="search" id="search" placeholder={t('searchHR')} style={{ maxWidth: "200px", marginBottom: "20px" }} /> |
||||
</div> |
||||
{renderTable} |
||||
<Pagination |
||||
style={{ marginTop: "25px" }} |
||||
showSizeChanger |
||||
onShowSizeChange={onShowSizeChange} |
||||
onChange={onPagination} |
||||
current={currentPage} |
||||
pageSize={rowsPerPage} |
||||
total={totalPage} |
||||
pageSizeOptions={["10", "15", "20", "25", "30", "35", "40"]} |
||||
/> |
||||
</CardBody> |
||||
</Card> |
||||
</div> |
||||
) |
||||
} |
||||
|
||||
export default Index; |
||||
|
Loading…
Reference in new issue