diff --git a/src/views/Master/ProjectInvoice/DialogForm.js b/src/views/Master/ProjectInvoice/DialogForm.js
new file mode 100644
index 0000000..2a42dc1
--- /dev/null
+++ b/src/views/Master/ProjectInvoice/DialogForm.js
@@ -0,0 +1,107 @@
+import React, { useEffect, useState } from 'react'
+import {
+ Modal, ModalHeader, ModalBody, ModalFooter,
+ Button, Form, FormGroup, Label, Input, Col, Row
+} from 'reactstrap';
+import 'antd/dist/antd.css';
+import { Select } from "antd";
+import InputColor from "./InputColor";
+import "./styles.css";
+import "rc-color-picker/assets/index.css";
+import { useTranslation } from 'react-i18next';
+const company_id = localStorage.getItem("company_id")
+const { Option } = Select;
+const DialogForm = ({ openDialog, closeDialog, toggleDialog, typeDialog, dataEdit }) => {
+ const [id, setId] = useState(0)
+ const [projectInvoice, setProjectInvoice] = useState('')
+ const [color, setColor] = useState('')
+ const { t } = useTranslation();
+ useEffect(() => {
+ if (typeDialog === "Edit") {
+ setId(dataEdit.id)
+ setProjectInvoice(dataEdit.name)
+ setColor(dataEdit.color)
+ } else {
+ setId(0)
+ setColor('')
+ setProjectInvoice('')
+ }
+ }, [dataEdit, openDialog])
+
+ const handleSave = () => {
+ let data = '';
+ if (typeDialog === "Save") {
+ data = {
+ name: projectInvoice,
+ color,
+ company_id
+ }
+ closeDialog('save', data);
+ } else {
+ data = {
+ id,
+ name: projectInvoice,
+ color,
+ company_id
+ }
+ closeDialog('edit', data);
+ }
+ setId(0)
+ }
+
+ const handleCancel = () => {
+ closeDialog('cancel', 'none')
+ setId(0)
+ }
+
+ const renderForm = () => {
+ return (
+
+ )
+ }
+
+ return (
+ <>
+
+ {typeDialog == "Save" ? `Add` : "Edit"} data
+
+ {renderForm()}
+
+
+ {' '}
+
+
+
+ >
+ )
+}
+
+export default DialogForm;
diff --git a/src/views/Master/ProjectInvoice/DialogFormInitial.js b/src/views/Master/ProjectInvoice/DialogFormInitial.js
new file mode 100644
index 0000000..200ba87
--- /dev/null
+++ b/src/views/Master/ProjectInvoice/DialogFormInitial.js
@@ -0,0 +1,89 @@
+import React, { useEffect, useState } from 'react'
+import {
+ Modal, ModalHeader, ModalBody, ModalFooter,
+ Button, Form, FormGroup, Label, Input, Col, Row
+} from 'reactstrap';
+
+const DialogForm = ({ openDialog, closeDialog, toggleDialog, typeDialog, dataEdit, idActivity, projectTypeId }) => {
+
+ const [id, setId] = useState(0)
+ const [activity, setActivity] = useState('')
+
+ useEffect(() => {
+ if (typeDialog === "edit") {
+ setId(dataEdit.id)
+ setActivity(dataEdit.name_activity)
+
+ } else {
+ setId(0)
+ setActivity('')
+ }
+ }, [dataEdit, openDialog])
+
+ const handleSave = () => {
+ let data = '';
+
+ if (typeDialog === "add") {
+ data = {
+ name_activity: activity,
+ proyek_type_id: projectTypeId
+ }
+
+ if (idActivity && idActivity > 0) {
+ data['parent_id'] = idActivity
+ }
+
+ closeDialog('save', data);
+ } else {
+ data = {
+ id,
+ name_activity: activity,
+ proyek_type_id: projectTypeId
+ }
+
+ if (idActivity && idActivity > 0) {
+ data['parent_id'] = idActivity
+ }
+
+ closeDialog('edit', data);
+ }
+ setId(0)
+ setActivity('')
+ }
+
+ const handleCancel = () => {
+ closeDialog('cancel', 'none')
+ setId(0)
+ setActivity('')
+ }
+
+ const renderForm = () => {
+ return (
+
+ )
+ }
+
+
+ return (
+ <>
+
+ {typeDialog == "add" ? `Add` : "Edit"} Activity
+
+ {renderForm()}
+
+
+ {' '}
+
+
+
+ >
+ )
+
+}
+
+export default DialogForm;
\ No newline at end of file
diff --git a/src/views/Master/ProjectInvoice/InputColor.js b/src/views/Master/ProjectInvoice/InputColor.js
new file mode 100644
index 0000000..ff2f865
--- /dev/null
+++ b/src/views/Master/ProjectInvoice/InputColor.js
@@ -0,0 +1,46 @@
+import React from "react";
+import Input from "antd/lib/input";
+import Button from "antd/lib/button";
+import Dropdown from "antd/lib/dropdown";
+import { Panel } from 'rc-color-picker'
+import "antd/dist/antd.css";
+import "./styles.css";
+
+export default function InputColor(props) {
+ const { color, onChange } = props;
+
+ const [internalColor, setInternalColor] = React.useState(color);
+
+ const handleChange = (color) => {
+ setInternalColor(color.color);
+
+ if (onChange) {
+ onChange(color);
+ }
+ };
+
+ const overlay = (
+
+ );
+
+ return (
+ <>
+ setInternalColor(e.target.value)}
+ suffix={
+
+
+
+ }
+ />
+ >
+ );
+}
+
diff --git a/src/views/Master/ProjectInvoice/index.js b/src/views/Master/ProjectInvoice/index.js
new file mode 100644
index 0000000..da213a1
--- /dev/null
+++ b/src/views/Master/ProjectInvoice/index.js
@@ -0,0 +1,392 @@
+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_INVOICE_ADD, PROJECT_INVOICE_EDIT, PROJECT_INVOICE_DELETE, PROJECT_INVOICE_SEARCH } from '../../../const/ApiConst';
+import { Pagination, Button, Tooltip, Table } from 'antd';
+import { useTranslation } from 'react-i18next';
+
+
+const ProjectInvoice = ({ 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 [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(() => {
+ getDataProjectInvoice()
+ }, [currentPage, rowsPerPage, search])
+
+ useEffect(() => {
+ const cekData = dataExport || []
+ if (cekData.length > 0) {
+ exportExcel()
+ }
+ }, [dataExport])
+
+ const getDataProjectInvoice = 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_INVOICE_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_INVOICE_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") {
+ saveProjectInvoice(data);
+ } else if (type === "edit") {
+ editMaterialR(data);
+ }
+ setDataEdit([])
+ setOpenDialog(false)
+ }
+
+ const saveProjectInvoice = async (data) => {
+ const result = await axios.post(PROJECT_INVOICE_ADD, data, HEADER)
+ .then(res => res)
+ .catch((error) => error.response);
+
+ if (result && result.data && result.data.code === 200) {
+ getDataProjectInvoice()
+ NotificationManager.success(`Data project invoice berhasil ditambah`, 'Success!!');
+ } else {
+ NotificationManager.error(`${result.data.message}`, 'Failed!!');
+ }
+ }
+
+ const editMaterialR = async (data) => {
+ let urlEdit = PROJECT_INVOICE_EDIT(data.id)
+ const result = await axios.put(urlEdit, data, HEADER)
+ .then(res => res)
+ .catch((error) => error.response);
+
+ if (result && result.data && result.data.code === 200) {
+ getDataProjectInvoice();
+ NotificationManager.success(`Data project invoice berhasil diedit`, 'Success!!');
+ } else {
+ NotificationManager.error(`Data project invoice 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_INVOICE_DELETE(idDelete);
+
+ const result = await axios.delete(url, HEADER)
+ .then(res => res)
+ .catch((error) => error.response);
+
+ if (result && result.data && result.data.code === 200) {
+ getDataProjectInvoice()
+ setIdDelete(0)
+ setAlertDelete(false)
+ NotificationManager.success(`Data project invoice berhasil dihapus!`, 'Success!!');
+ } else {
+ setIdDelete(0)
+ setAlertDelete(false)
+ NotificationManager.error(`Data project invoice 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 (
+
+ {t('noData')} |
+
+ )
+ }
+ }
+
+ const renderTable = useMemo(() => {
+ const columns = [
+ {
+ title: t('action'),
+ dataIndex: '',
+ key: 'x',
+ className: 'nowrap',
+ render: (text, record) => <>
+
+ handleDelete(text.id)}>
+
+
+ handleEdit(text)}>
+ {" "}
+ >,
+ },
+ { title: t('Project Invoice'), dataIndex: 'name', key: 'name', className: "nowrap" },
+ {
+ title: t('color'),
+ dataIndex: 'color',
+ key: 'color',
+ render: (text) => <>
+
+
+
+ >,
+ },
+ ];
+ return (
+
+ )
+ }, [dataTable])
+
+ return (
+
+
+
+ {t('deleteMsg')}
+
+
toggleAddDialog}
+ typeDialog={typeDialog}
+ dataEdit={dataEdit}
+ clickOpenModal={clickOpenModal}
+ />
+
+
+ {pageName}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {renderTable}
+
+
+
+
+ )
+}
+
+export default ProjectInvoice;
diff --git a/src/views/Master/ProjectInvoice/styles.css b/src/views/Master/ProjectInvoice/styles.css
new file mode 100644
index 0000000..d48cce6
--- /dev/null
+++ b/src/views/Master/ProjectInvoice/styles.css
@@ -0,0 +1,41 @@
+.App {
+ font-family: sans-serif;
+ padding: 20px;
+}
+
+h2 {
+ margin-top: 40px;
+}
+
+.rc-color-picker-panel {
+ border: 1px solid #ccc;
+}
+.rc-color-picker-panel-inner {
+ border: none;
+ box-shadow: none;
+}
+.rc-color-picker-panel-board-hsv {
+ border-radius: 12px;
+ outline: none;
+}
+.rc-color-picker-panel-board-value {
+ border: none;
+ border-radius: 12px;
+}
+.rc-color-picker-panel-board-saturation {
+ border: none;
+ border-radius: 12px;
+}
+.rc-color-picker-panel-ribbon {
+ border-radius: 12px;
+}
+.rc-color-picker-panel-wrap-preview {
+ border-radius: 12px;
+}
+.rc-color-picker-panel-preview span {
+ border-radius: 12px;
+}
+.rc-color-picker-panel-preview input {
+ border-radius: 12px;
+}
+