From d8ad27944e681e70065026b7f53030caa2eb9acb Mon Sep 17 00:00:00 2001 From: wahyu Date: Mon, 26 Feb 2024 16:40:47 +0700 Subject: [PATCH 01/24] menu state update --- .../Master/MasterRoles/DialogMenuRoles.js | 106 +++++++++++++++--- 1 file changed, 92 insertions(+), 14 deletions(-) diff --git a/src/views/Master/MasterRoles/DialogMenuRoles.js b/src/views/Master/MasterRoles/DialogMenuRoles.js index b066bc4..348f614 100644 --- a/src/views/Master/MasterRoles/DialogMenuRoles.js +++ b/src/views/Master/MasterRoles/DialogMenuRoles.js @@ -337,31 +337,104 @@ export default class DialogMenuRoles extends Component { this.setState({ stateMenu: copyStateMenu }) } - handleChangeCheckboxRead = (checked, index) => { + handleChangeCheckboxRead = (checked, index, menuItem = null, menuIdxList = []) => { let copyStateRead = [...this.state.stateRead]; copyStateRead[index] = checked; + let menu = this.state.menu; + let checkMenuParent = menu.map((state, index) => { + if (state.parent_id === menuItem.id) { + return state.id + } else { + return null + } + }).filter(index => index !== null); + let stateReadIdx = []; + menuIdxList.forEach((menu, index) => { + checkMenuParent.forEach((check, idx) => { + if (check === menu) { + stateReadIdx.push(index); + } + }); + }); + stateReadIdx.map((stateRead) => { + copyStateRead[stateRead] = checked + }) this.setState({ stateRead: copyStateRead }) } - handleChangeCheckboxCreate = (checked, index) => { + handleChangeCheckboxCreate = (checked, index, menuItem = null, menuIdxList = []) => { let copyStateCreate = [...this.state.stateCreate]; copyStateCreate[index] = checked; - + let menu = this.state.menu; + let checkMenuParent = menu.map((state, index) => { + if (state.parent_id === menuItem.id) { + return state.id + } else { + return null + } + }).filter(index => index !== null); + let stateCreateIdx = []; + menuIdxList.forEach((menu, index) => { + checkMenuParent.forEach((check, idx) => { + if (check === menu) { + stateCreateIdx.push(index); + } + }); + }); + stateCreateIdx.map((stateCreate) => { + copyStateCreate[stateCreate] = checked + }) this.setState({ stateCreate: copyStateCreate }) } - handleChangeCheckboxEdit = (checked, index) => { + handleChangeCheckboxEdit = (checked, index, menuItem = null, menuIdxList = []) => { let copyStateEdit = [...this.state.stateUpdate]; copyStateEdit[index] = checked; - + let menu = this.state.menu; + let checkMenuParent = menu.map((state, index) => { + if (state.parent_id === menuItem.id) { + return state.id + } else { + return null + } + }).filter(index => index !== null); + let stateEditIdx = []; + menuIdxList.forEach((menu, index) => { + checkMenuParent.forEach((check, idx) => { + if (check === menu) { + stateEditIdx.push(index); + } + }); + }); + stateEditIdx.map((stateEdit) => { + copyStateEdit[stateEdit] = checked + }) this.setState({ stateUpdate: copyStateEdit }) } - handleChangeCheckboxDelete = (checked, index) => { + handleChangeCheckboxDelete = (checked, index, menuItem = null, menuIdxList = []) => { let copyStateDelete = [...this.state.stateDelete]; copyStateDelete[index] = checked; - + let menu = this.state.menu; + let checkMenuParent = menu.map((state, index) => { + if (state.parent_id === menuItem.id) { + return state.id + } else { + return null + } + }).filter(index => index !== null); + let stateDeleteIdx = []; + menuIdxList.forEach((menu, index) => { + checkMenuParent.forEach((check, idx) => { + if (check === menu) { + stateDeleteIdx.push(index); + } + }); + }); + stateDeleteIdx.map((stateDelete) => { + copyStateDelete[stateDelete] = checked + }) this.setState({ stateDelete: copyStateDelete }) } @@ -419,7 +492,8 @@ export default class DialogMenuRoles extends Component { renderForm = () => { const { menu, stateRead, stateCreate, stateUpdate, stateDelete } = this.state; - + let menuIdxList = [] + let menuIdx = 0 const getChildren = (parentId) => { return menu.filter(item => item.parent_id === parentId); }; @@ -429,24 +503,28 @@ export default class DialogMenuRoles extends Component { const children = getChildren(parentId); return children.map((menuItem, index) => { + const currentIndex = menuIdx; // Capture current index + menuIdxList[currentIndex] = menuItem.id; + menuIdx++ const paddingLeft = depth * 20; const fontWeight = menuItem.parent_id === null ? 'bold' : 'normal'; return ( - + {menuItem.name} - this.handleChangeCheckboxRead(e.target.checked, index)} defaultChecked={stateRead[index]} /> - this.handleChangeCheckboxCreate(e.target.checked, index)} defaultChecked={stateCreate[index]} /> - this.handleChangeCheckboxEdit(e.target.checked, index)} defaultChecked={stateUpdate[index]} /> - this.handleChangeCheckboxDelete(e.target.checked, index)} defaultChecked={stateDelete[index]} /> + this.handleChangeCheckboxRead(e.target.checked, currentIndex, menuItem, menuIdxList)} defaultChecked={stateRead[currentIndex]} /> + this.handleChangeCheckboxCreate(e.target.checked, currentIndex, menuItem, menuIdxList)} defaultChecked={stateCreate[currentIndex]} /> + this.handleChangeCheckboxEdit(e.target.checked, currentIndex, menuItem, menuIdxList)} defaultChecked={stateUpdate[currentIndex]} /> + this.handleChangeCheckboxDelete(e.target.checked, currentIndex, menuItem, menuIdxList)} defaultChecked={stateDelete[currentIndex]} /> {renderMenu(menuItem.id, depth + 1)} {/* Recursively render children */} ); }); }; - + menuIdxList = [] + menuIdx = 0 // Render top-level menu items (parents) return ( From 9b1d2964abe971036c16465749cdf3222ca71dc8 Mon Sep 17 00:00:00 2001 From: wahyu Date: Tue, 27 Feb 2024 11:09:42 +0700 Subject: [PATCH 02/24] dynamic dashboard init --- package.json | 1 + src/components/wj/App.jsx | 270 +++++++++++++ src/components/wj/components/BarChart.jsx | 11 + src/components/wj/components/Blank.jsx | 3 + src/components/wj/components/BubbleChart.jsx | 9 + src/components/wj/components/BulletGraph.jsx | 22 ++ src/components/wj/components/ColumnChart.jsx | 11 + src/components/wj/components/Grid.jsx | 12 + src/components/wj/components/LineChart.jsx | 10 + src/components/wj/components/LinearGauge.jsx | 26 ++ src/components/wj/components/RadialGauge.jsx | 12 + src/components/wj/components/Tile.jsx | 14 + src/components/wj/index.css | 394 +++++++++++++++++++ src/components/wj/index.jsx | 10 + src/components/wj/license.js | 2 + src/components/wj/utils/DragDropTouch.js | 370 +++++++++++++++++ src/routes.js | 2 + 17 files changed, 1179 insertions(+) create mode 100644 src/components/wj/App.jsx create mode 100644 src/components/wj/components/BarChart.jsx create mode 100644 src/components/wj/components/Blank.jsx create mode 100644 src/components/wj/components/BubbleChart.jsx create mode 100644 src/components/wj/components/BulletGraph.jsx create mode 100644 src/components/wj/components/ColumnChart.jsx create mode 100644 src/components/wj/components/Grid.jsx create mode 100644 src/components/wj/components/LineChart.jsx create mode 100644 src/components/wj/components/LinearGauge.jsx create mode 100644 src/components/wj/components/RadialGauge.jsx create mode 100644 src/components/wj/components/Tile.jsx create mode 100644 src/components/wj/index.css create mode 100644 src/components/wj/index.jsx create mode 100644 src/components/wj/license.js create mode 100644 src/components/wj/utils/DragDropTouch.js diff --git a/package.json b/package.json index 964c241..81dbc9c 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "@dabeng/react-orgchart": "^1.0.0", "@develoka/angka-rupiah-js": "^1.0.3", "@faker-js/faker": "^7.3.0", + "@grapecity/wijmo.react.all": "5.20213", "@iconify/icons-ant-design": "^1.0.6", "@iconify/icons-fa-solid": "^1.0.6", "@iconify/icons-fe": "^1.0.3", diff --git a/src/components/wj/App.jsx b/src/components/wj/App.jsx new file mode 100644 index 0000000..09b5099 --- /dev/null +++ b/src/components/wj/App.jsx @@ -0,0 +1,270 @@ +// React +import * as React from 'react'; +// Wijmo +import * as wjcCore from '@grapecity/wijmo'; +import { Grid } from './components/Grid'; +import { RadialGauge } from './components/RadialGauge'; +import { LinearGauge } from './components/LinearGauge'; +import { BarChart } from './components/BarChart'; +import { ColumnChart } from './components/ColumnChart'; +import { LineChart } from './components/LineChart'; +import { BubbleChart } from './components/BubbleChart'; +import { BulletGraph } from './components/BulletGraph'; +import { Blank } from './components/Blank'; +import { Tile } from './components/Tile'; +// Wijmo and Material Design Lite +import '@grapecity/wijmo.styles/themes/material/wijmo.theme.material.indigo-amber.css'; +// Styles +import './index.css'; + +import './license' + +export default class App extends React.Component { + constructor() { + super(...arguments); + // Color palette + this.palette = ['#8e99f3', '#ffca28', '#5c6bc0', '#bbdefb']; + // Icons assets + this.icons = { + grid: [ + , + , + ], + barChart: [ + , + , + , + , + ], + columnChart: [ + , + , + , + , + ], + bubbleChart: [ + , + , + , + ], + lineChart: [ + , + , + , + , + ], + radialGauge: [ + , + , + , + ], + linearGauge: [ + , + , + , + ], + bulletGraph: [ + , + , + , + , + , + , + , + , + , + ], + blank: [ + , + ], + }; + // Tile names and types + this.tileCatalog = [ + { name: 'Grid', tile: Grid, icon: this.icons.grid }, + { name: 'Radial Gauge', tile: RadialGauge, icon: this.icons.radialGauge }, + { name: 'Linear Gauge', tile: LinearGauge, icon: this.icons.linearGauge }, + { name: 'Bar Chart', tile: BarChart, icon: this.icons.barChart }, + { name: 'Column Chart', tile: ColumnChart, icon: this.icons.columnChart }, + { name: 'Line Chart', tile: LineChart, icon: this.icons.lineChart }, + { name: 'Bubble Chart', tile: BubbleChart, icon: this.icons.bubbleChart }, + { name: 'Bullet Graph', tile: BulletGraph, icon: this.icons.bulletGraph }, + { name: 'Blank', tile: Blank, icon: this.icons.blank }, + ]; + this.key = 0; + this.state = { + isWideMenu: false, + tileCatalog: new wjcCore.CollectionView(this.tileCatalog), + tiles: this.getTiles(), + key: this.key, + data: this.getData(), + }; + } + // tiles currently in use + getTiles() { + return [ + { name: this.tileCatalog[1].name, key: this.key += 1 }, + { name: this.tileCatalog[2].name, key: this.key += 1 }, + { name: this.tileCatalog[5].name, key: this.key += 1 }, + { name: this.tileCatalog[7].name, key: this.key += 1 }, + { name: this.tileCatalog[0].name, key: this.key += 1 }, + ]; + } + // generate some data to show in the tiles + getData() { + let data = []; + const today = new Date(); + for (let i = 0; i < 12; i++) { + const sales = 100 + Math.random() * 800 + i * 50; + const expenses = 50 + Math.random() * 300 + i * 5; + data.push({ + id: i, + date: wjcCore.DateTime.addMonths(today, 12 - i), + sales: sales, + expenses: expenses, + profit: sales - expenses, + }); + } + return data; + } + // gets a tile content by name + getTileContent(name) { + const { data, tileCatalog } = this.state; + const arr = tileCatalog.items; + for (let i = 0; i < arr.length; i++) { + if (arr[i].name == name) { + return React.createElement(arr[i].tile, { + data: new wjcCore.CollectionView(data), + palette: this.palette + }); + } + } + throw '*** tile not found: ' + name; + } + // adds a tile to the dashboard + addTile(name) { + const { tiles, key: stateKey } = this.state; + const key = stateKey + 1; + this.setState({ tiles: [{ name, key }, ...tiles], key }); + } + // removes a tile from the dashboard + removeTile(tileIndex) { + const tiles = this.state.tiles.filter((item, index) => index != tileIndex); + this.setState({ tiles: tiles }); + } + // initialize component after it has been mounted + componentDidMount() { + // enable tile drag/drop + const panel = document.querySelector('.dashboard'); + this.enableItemReorder(panel); + } + // allow users to re-order elements within a panel element + // we work with the DOM elements and update the state when done. + enableItemReorder(panel) { + let dragSource = null; + let dropTarget = null; + // add drag/drop event listeners + panel.addEventListener('dragstart', (e) => { + const target = wjcCore.closest(e.target, '.tile'); + if (target && target.parentElement == panel) { + dragSource = target; + wjcCore.addClass(dragSource, 'drag-source'); + const dt = e.dataTransfer; + dt.effectAllowed = 'move'; + dt.setData('text', dragSource.innerHTML); + } + }); + panel.addEventListener('dragover', (e) => { + if (dragSource) { + let tile = wjcCore.closest(e.target, '.tile'); + if (tile == dragSource) { + tile = null; + } + if (dragSource && tile && tile != dragSource) { + e.preventDefault(); + e.dataTransfer.dropEffect = 'move'; + } + if (dropTarget != tile) { + wjcCore.removeClass(dropTarget, 'drag-over'); + dropTarget = tile; + wjcCore.addClass(dropTarget, 'drag-over'); + } + } + }); + panel.addEventListener('drop', (e) => { + if (dragSource && dropTarget) { + // finish drag/drop + e.stopPropagation(); + e.stopImmediatePropagation(); + e.preventDefault(); + // re-order HTML elements (optional here, we're updating the state later) + const srcIndex = getIndex(dragSource); + const dstIndex = getIndex(dropTarget); + const refChild = srcIndex > dstIndex ? dropTarget : dropTarget.nextElementSibling; + dragSource.parentElement.insertBefore(dragSource, refChild); + // focus and view on the tile that was dragged + dragSource.focus(); + // update state + let tiles = this.state.tiles.slice(); + tiles.splice(srcIndex, 1); + tiles.splice(dstIndex, 0, this.state.tiles[srcIndex]); + this.setState({ tiles: tiles }); + } + }); + panel.addEventListener('dragend', () => { + wjcCore.removeClass(dragSource, 'drag-source'); + wjcCore.removeClass(dropTarget, 'drag-over'); + dragSource = dropTarget = null; + }); + function getIndex(e) { + const p = e.parentElement; + for (let i = 0; i < p.children.length; i++) { + if (p.children[i] == e) + return i; + } + return -1; + } + } + // render the dashboard + render() { + const { tiles, isWideMenu } = this.state; + // animated toggle menu + const renderMenuToggle = (
this.setState({ isWideMenu: !isWideMenu })}> + + + + + +
); + // menu items + const renderMenuItems = ( + {this.tileCatalog.map((item) => (
this.addTile(item.name)}> + + {item.icon.map((entity, key) => ({entity}))} + +
{item.name}
+
))} +
); + // displayed when the dashboard is empty + const renderBlankTile = (
+ + + +
Click on an item on the menu bar to add the new tile to the dashboard.
+
); + // list of tiles + const renderTiles = ( + {tiles.map((item, index) => ())} + ); + const renderDashboard = tiles.length ? renderTiles : renderBlankTile; + return (
+
+ {renderMenuToggle} + {renderMenuItems} +
+
+
+
{renderDashboard}
+
+
); + } +} diff --git a/src/components/wj/components/BarChart.jsx b/src/components/wj/components/BarChart.jsx new file mode 100644 index 0000000..a9dc664 --- /dev/null +++ b/src/components/wj/components/BarChart.jsx @@ -0,0 +1,11 @@ +// React +import * as React from 'react'; +// Wijmo +import * as wjcChart from '@grapecity/wijmo.chart'; +import * as wjChart from '@grapecity/wijmo.react.chart'; +export const BarChart = ({ data, palette }) => ( + + + + + ); diff --git a/src/components/wj/components/Blank.jsx b/src/components/wj/components/Blank.jsx new file mode 100644 index 0000000..28f67ca --- /dev/null +++ b/src/components/wj/components/Blank.jsx @@ -0,0 +1,3 @@ +// React +import * as React from 'react'; +export const Blank = () =>
This is an empty tile.
; diff --git a/src/components/wj/components/BubbleChart.jsx b/src/components/wj/components/BubbleChart.jsx new file mode 100644 index 0000000..ad8f98d --- /dev/null +++ b/src/components/wj/components/BubbleChart.jsx @@ -0,0 +1,9 @@ +// React +import * as React from 'react'; +// Wijmo +import * as wjcChart from '@grapecity/wijmo.chart'; +import * as wjChart from '@grapecity/wijmo.react.chart'; +export const BubbleChart = ({ data, palette }) => ( + + + ); diff --git a/src/components/wj/components/BulletGraph.jsx b/src/components/wj/components/BulletGraph.jsx new file mode 100644 index 0000000..ed7af7a --- /dev/null +++ b/src/components/wj/components/BulletGraph.jsx @@ -0,0 +1,22 @@ +// React +import * as React from 'react'; +// Wijmo +import * as wjcCore from '@grapecity/wijmo'; +import * as wjGauge from '@grapecity/wijmo.react.gauge'; +export const BulletGraph = ({ data }) => (
+ + + {data.items.map((item, index) => ( + + + + ))} + +
{wjcCore.Globalize.format(item.date, 'MMM yyyy')} + + + + + +
+
); diff --git a/src/components/wj/components/ColumnChart.jsx b/src/components/wj/components/ColumnChart.jsx new file mode 100644 index 0000000..5d8e495 --- /dev/null +++ b/src/components/wj/components/ColumnChart.jsx @@ -0,0 +1,11 @@ +// React +import * as React from 'react'; +// Wijmo +import * as wjcChart from '@grapecity/wijmo.chart'; +import * as wjChart from '@grapecity/wijmo.react.chart'; +export const ColumnChart = ({ data, palette }) => ( + + + + + ); diff --git a/src/components/wj/components/Grid.jsx b/src/components/wj/components/Grid.jsx new file mode 100644 index 0000000..1d0caef --- /dev/null +++ b/src/components/wj/components/Grid.jsx @@ -0,0 +1,12 @@ +// React +import * as React from 'react'; +// Wijmo +import * as wjcGrid from '@grapecity/wijmo.grid'; +import * as wjGrid from '@grapecity/wijmo.react.grid'; +export const Grid = ({ data, palette }) => ( + + + + + + ); diff --git a/src/components/wj/components/LineChart.jsx b/src/components/wj/components/LineChart.jsx new file mode 100644 index 0000000..6f93ab2 --- /dev/null +++ b/src/components/wj/components/LineChart.jsx @@ -0,0 +1,10 @@ +// React +import * as React from 'react'; +// Wijmo +import * as wjcChart from '@grapecity/wijmo.chart'; +import * as wjChart from '@grapecity/wijmo.react.chart'; +export const LineChart = ({ data, palette }) => ( + + + + ); diff --git a/src/components/wj/components/LinearGauge.jsx b/src/components/wj/components/LinearGauge.jsx new file mode 100644 index 0000000..3a14ec3 --- /dev/null +++ b/src/components/wj/components/LinearGauge.jsx @@ -0,0 +1,26 @@ +// React +import * as React from 'react'; +// Wijmo +import * as wjcCore from '@grapecity/wijmo'; +import * as wjGauge from '@grapecity/wijmo.react.gauge'; +export const LinearGauge = ({ data, palette }) => { + const lastItem = data.items[data.items.length - 1]; + return (
+
+

Sales: {wjcCore.Globalize.format(lastItem.sales, 'c')}

+ +
+ +
+

Expenses: {wjcCore.Globalize.format(lastItem.expenses, 'c')}

+ +
+ +
+

Profit: {wjcCore.Globalize.format(lastItem.profit, 'c')}

+ +
+ +

KPIs for {wjcCore.Globalize.format(lastItem.date, 'MMMM yyyy')}

+
); +}; diff --git a/src/components/wj/components/RadialGauge.jsx b/src/components/wj/components/RadialGauge.jsx new file mode 100644 index 0000000..00faeb9 --- /dev/null +++ b/src/components/wj/components/RadialGauge.jsx @@ -0,0 +1,12 @@ +// React +import * as React from 'react'; +// Wijmo +import * as wjcCore from '@grapecity/wijmo'; +import * as wjGauge from '@grapecity/wijmo.react.gauge'; +export const RadialGauge = ({ data, palette }) => { + const lastItem = data.items[data.items.length - 1]; + return ( + +

Profit for {wjcCore.Globalize.format(lastItem.date, 'MMMM yyyy')}

+
); +}; diff --git a/src/components/wj/components/Tile.jsx b/src/components/wj/components/Tile.jsx new file mode 100644 index 0000000..62cdcf5 --- /dev/null +++ b/src/components/wj/components/Tile.jsx @@ -0,0 +1,14 @@ +import * as React from 'react'; +export const Tile = ({ header, content, index, onRemove }) => (
+
+
{header}
+
+
onRemove(index)}> + + + +
+
+
+
{content}
+
); diff --git a/src/components/wj/index.css b/src/components/wj/index.css new file mode 100644 index 0000000..87262fc --- /dev/null +++ b/src/components/wj/index.css @@ -0,0 +1,394 @@ +/* app */ +*, *::before, *::after { + box-sizing: border-box; +} + +/* customize the browser's scrollbar: */ +*::-webkit-scrollbar { + width: 6px; + height: 6px; +} +*::-webkit-scrollbar-track { + border-radius: 0.25rem; + background: rgba(0, 0, 0, 0.1); +} +*::-webkit-scrollbar-thumb { + border-radius: 0.25rem; + background: rgba(0, 0, 0, 0.2); +} +*::-webkit-scrollbar-thumb:hover { + background: rgba(0, 0, 0, 0.4); +} +*::-webkit-scrollbar-thumb:active { + background: rgba(0, 0, 0, 0.9); +} + +html, +body { + height: 100%; +} + +body { + background-color: #f7faff; + font-size: 0.875rem; + font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Ubuntu, 'Helvetica Neue', sans-serif; + padding: 0; + margin: 0; + display: flex; + flex-direction: column; +} + +h3 { + font-size: 0.875rem; + font-weight: 500; + text-align: center; +} + +h4 { + font-size: 0.875rem; + font-size: 0.75rem; + font-weight: 400; + min-width: 7rem; +} + +.flex-row { + display: flex; + flex-direction: row; + align-items: center; + padding: 0 1rem; +} + +.button { + cursor: pointer; +} + +#app { + flex: 1 1 auto; + overflow: hidden; +} + +.container { + display: flex; + flex-direction: row; + height: 100%; + width: 100%; + max-width: 100%; + background: #f2f6fe; +} + +.hr { + width: 1px; + height: 100%; + position: relative; + background: #e4ecfb; + z-index: 1; +} +.hr::before { + content: ''; + display: block; + width: 1px; + height: 100%; + background-color: #f6f9fe; +} + +.blank { + display: flex; + flex-direction: column; + align-items: center; + width: 100%; + height: 100%; + justify-content: center; + text-align: center; +} + +.blank svg { + width: 3rem; + height: 3rem; + margin-bottom: 1rem; +} + +.content { + flex: 1 1 auto; + overflow: auto; + background-color: #f7faff; + width: 100%; + padding: 2rem; + box-sizing: border-box; +} +@media only screen and (max-width: 811px) { + .content { + padding: 0.25rem; + } +} + +.menu { + display: flex; + flex-direction: column; + font-size: 0.85rem; + justify-content: flex-start; + flex: 0 0 auto; + padding: 0; +} +@media only screen and (max-width: 840px), (max-height: 840px) { + .menu { + overflow: auto; + font: 0.75rem; + } +} +.menu.menu--open .menu-item-name { + display: block; +} + +.menu-toggle { + transition: all 500ms; + padding: 1rem; + text-align: center; + border-bottom: 1px solid #e6ecf1; + cursor: pointer; +} +.menu-toggle svg { + transition: all 250ms ease-out 50ms; + transform-origin: center center; + transform: scaleX(1); +} +.menu.menu--open .menu-toggle svg { + transform: scaleX(-1); +} + +.menu-item { + display: flex; + flex-direction: row; + align-items: center; + padding: 0.5rem 1rem; + cursor: pointer; + user-select: none; + position: relative; + text-align: left; +} +.menu-item:hover { + background: #f7faff; +} +.menu-item:hover:before { + content: ''; + display: inline-block; + width: 1rem; + height: 1rem; + bottom: 2rem; + left: 2.5rem; + position: absolute; + background: linear-gradient(#fff, #fff), linear-gradient(#fff, #fff), #0085c7; + background-position: center; + background-size: 50% 2px, 2px 50%; /*thickness = 2px, length = 50% (25px)*/ + background-repeat: no-repeat; + border-radius: 50%; + box-shadow: 0 1px 2px rgba(55, 63, 66, 0.07), 0 2px 4px rgba(55, 63, 66, 0.07), 0 4px 8px rgba(55, 63, 66, 0.07), + 0 8px 16px rgba(55, 63, 66, 0.07), 0 16px 24px rgba(55, 63, 66, 0.07), 0 24px 32px rgba(55, 63, 66, 0.07); +} +.menu-item-name { + margin: 0 0.5rem 0 1rem; + white-space: nowrap; + display: none; +} +@media only screen and (max-width: 840px), (max-height: 840px) { + .menu-toggle { + padding: 1rem 0; + } + .menu-item { + padding: 0.5rem; + } + .menu-item:hover:before { + bottom: 1rem; + left: 1rem; + } + .menu-item svg { + width: 32px; + height: 32px; + } +} + +/* dashboard and tiles */ +.dashboard { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + margin: auto; + height: 100%; +} + +.table { + margin: 0; + table-layout: fixed; + width: 100%; +} + +.table td { + padding: 0.15rem 0.5rem; + font-size: 0.75rem; + white-space: nowrap; + width: 1.5rem; +} + +.table td:first-child { + width: 4rem; +} + +.table td:last-child { + width: auto; +} + +.tile { + display: flex; + flex-direction: column; + flex: 0 0 auto; + width: calc(25% - 1rem); + margin: 0.5rem; + height: 50vh; + max-height: calc(50% - 1rem); + overflow: hidden; + background: white; + page-break-inside: avoid; /* important when printing the dashboard */ + transition: all 250ms; + border-radius: 0.5rem; + box-sizing: border-box; + box-shadow: 0 1px 2px rgba(55, 63, 66, 0.07), 0 2px 4px rgba(55, 63, 66, 0.07), 0 4px 8px rgba(55, 63, 66, 0.07), + 0 8px 16px rgba(55, 63, 66, 0.07), 0 16px 24px rgba(55, 63, 66, 0.07), 0 24px 32px rgba(55, 63, 66, 0.07); +} +@media only screen and (max-width: 1599px) { + .tile { + width: calc(33.33% - 1rem); + } +} +@media only screen and (max-width: 1079px) { + .tile { + width: calc(50% - 1rem); + } +} +@media only screen and (max-width: 1023px) { + .tile { + width: calc(100% - 1rem); + } +} +@media only screen and (max-height: 800px) { + .tile { + max-height: 400px; + } +} +.tile:last-child { + flex: 1 1 auto; +} +.tile:hover { + border-color: #adb7bd; +} +.tile.drag-over { + border: 2px dashed #000; +} + +.tile .buttons { + transition: all 250ms; + opacity: 0; +} +@media (hover: none) and (pointer: coarse) { + .tile .buttons { + opacity: 1; + } +} +.tile:hover .buttons { + opacity: 1; +} +.tile .buttons > span { + padding: 0 0.5rem; + cursor: pointer; +} +.tile.drag-over { + border: 2px dashed #000; + background-color: rgba(0, 0, 0, 0.1); + transition: all 250ms; +} +.tile.drag-source { + opacity: 0.4; + box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23); + background-color: rgba(145, 200, 248, 0.75); + transform: scale(0.9); + transition: all 250ms; +} + +.tile .tile-container { + border-bottom: 1px solid #e0e0e0; + padding: 0.75rem 1rem; + display: flex; + cursor: move; +} + +.tile .tile-header { + flex-grow: 1; + font-size: 1rem; + font-weight: 400; + padding: 0.125rem; + opacity: 0.75; +} + +.tile .tile-content { + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + flex: 1 1 auto; + overflow: auto; + height: 100%; +} + +.tile .blank-tile { + height: 100vh; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +/* tile content */ + +.wj-flexgrid { + border: none; + height: 100%; +} + +.wj-flexgrid .wj-cell { + border-right: none; + border-bottom: 1px solid rgba(0, 0, 0, 0.1); + padding: 0.35rem 1rem; + font-size: 0.8125rem; +} + +.wj-flexchart { + background: transparent; + height: calc(100%); + width: 100%; + border: none; + padding: 1rem; + margin: 0; + overflow: hidden; +} +.wj-radialgauge { + width: 60%; + max-width: 300px; + padding: 1rem; + overflow: hidden; +} +.wj-radialgauge .wj-value { + font-size: 0.75rem; + font-weight: 500; +} + +.wj-lineargauge { + max-height: 1rem; + width: 100%; + overflow: hidden; +} + +.wj-gauge .wj-face path { + stroke: none; +} + +.wj-ranges { + opacity: 0.15; +} diff --git a/src/components/wj/index.jsx b/src/components/wj/index.jsx new file mode 100644 index 0000000..4f7df64 --- /dev/null +++ b/src/components/wj/index.jsx @@ -0,0 +1,10 @@ +import './license'; +// Wijmo and Material Design Lite +import '@grapecity/wijmo.styles/themes/material/wijmo.theme.material.indigo-amber.css'; +// Styles +import './index.css'; +//React +import * as React from 'react'; +import * as ReactDOM from 'react-dom'; +import { App } from './App'; +ReactDOM.render(React.createElement(App), document.getElementById('app')); diff --git a/src/components/wj/license.js b/src/components/wj/license.js new file mode 100644 index 0000000..5c66de4 --- /dev/null +++ b/src/components/wj/license.js @@ -0,0 +1,2 @@ +import { setLicenseKey } from '@grapecity/wijmo'; +setLicenseKey('GrapeCity,427351286422477#B0JoIIklkIs4nIzYXMyAjMiojIyVmdiwSZzxWYmpjIyNHZisnOiwmbBJye0ICRiwiI34TQvAVW7ZWbqNjeWZzRh9Ucl5UTuZVaCR7ZpB5UH9EVC3kaqJWZ0pnasJ7Q9I4bB3GR0F6aIt6NZ96MFN4aotEZrUXe4kHUlllerlGb9dDSPhEcFFmclJXd8syNEtENzMTOBNVYpFje6lDUlBlbkdmcNNGOrAHR9pXNSl6NpVkbCNWUxlkZ7o7QT3icHNGavFXdapWQDZ5KsN5N7dDcyRUW85ESFBlb4JUZq3GWlBldXBzU0hjW9wkb8cncopnSOBjNkJFdyNmSqB7YVlnNzpnb9BDSMllTSFDaNFjca54dtBXatlEdS5mVNV6dxIEN7RUNxYGSvU7KIdndPpENvlXW6hzbCh7RsZ7YLJzboNnU7ZERTRkVBNlN7RWSGhDVrdmZqZ4cq94aFpkbWZDMGBHdVZ5YwUTTIdlN0RnVvMDdFJzbQt6bolDM5NkdrsUO536LrNWSotmI0IyUiwiIDRTR4MjM9QjI0ICSiwSMyIjM9UTMwEjM0IicfJye35XX3JSSwIjUiojIDJCLi86bpNnblRHeFBCI4VWZoNFelxmRg2Wbql6ViojIOJyes4nI5kkTRJiOiMkIsIibvl6cuVGd8VEIgIXZ7VWaWRncvBXZSBybtpWaXJiOi8kI1xSfis4N8gkI0IyQiwiIu3Waz9WZ4hXRgAydvJVa4xWdNBybtpWaXJiOi8kI1xSfiQjR6QkI0IyQiwiIu3Waz9WZ4hXRgACUBx4TgAybtpWaXJiOi8kI1xSfiMzQwIkI0IyQiwiIlJ7bDBybtpWaXJiOi8kI1xSfiUFO7EkI0IyQiwiIu3Waz9WZ4hXRgACdyFGaDxWYpNmbh9WaGBybtpWaXJiOi8kI1tlOiQmcQJCLiETMwAzNwACOwcDMwIDMyIiOiQncDJCLi46bj9idlRWe4l6YlBXYydmLqwibj9SbvNmL9RXajVGchJ7ZuoCLt36YukHdpNWZwFmcn9iKsI7au26YukHdpNWZwFmcn9iKsAnau26YukHdpNWZwFmcn9iKiojIz5GRiwiI9RXaDVGchJ7RiojIh94QiwiI7cDNyIDN6gjMxUzMdI6N'); diff --git a/src/components/wj/utils/DragDropTouch.js b/src/components/wj/utils/DragDropTouch.js new file mode 100644 index 0000000..dfcdbe7 --- /dev/null +++ b/src/components/wj/utils/DragDropTouch.js @@ -0,0 +1,370 @@ +import * as wjcCore from '@grapecity/wijmo'; +/** + * Object used to hold the data that is being dragged during drag and drop operations. + * + * It may hold one or more data items of different types. For more information about + * drag and drop operations and data transfer objects, see + * HTML Drag and Drop API. + * + * This object is created automatically by the @see:DragDropTouch singleton and is + * accessible through the @see:dataTransfer property of all drag events. + */ +export class DataTransfer { + constructor() { + this._dropEffect = 'move'; + this._effectAllowed = 'all'; + this._data = {}; + } + /** + * Gets or sets the type of drag-and-drop operation currently selected. + * The value must be 'none', 'copy', 'link', or 'move'. + */ + get dropEffect() { + return this._dropEffect; + } + set dropEffect(value) { + this._dropEffect = wjcCore.asString(value); + } + /** + * Gets or sets the types of operations that are possible. + * Must be one of 'none', 'copy', 'copyLink', 'copyMove', 'link', + * 'linkMove', 'move', 'all' or 'uninitialized'. + */ + get effectAllowed() { + return this._effectAllowed; + } + set effectAllowed(value) { + this._effectAllowed = wjcCore.asString(value); + } + /** + * Gets an array of strings giving the formats that were set in the @see:dragstart event. + */ + get types() { + return Object.keys(this._data); + } + /** + * Removes the data associated with a given type. + * + * The type argument is optional. If the type is empty or not specified, the data + * associated with all types is removed. If data for the specified type does not exist, + * or the data transfer contains no data, this method will have no effect. + * + * @param type Type of data to remove. + */ + clearData(type) { + if (type != null) { + delete this._data[type]; + } + else { + this._data = null; + } + } + /** + * Retrieves the data for a given type, or an empty string if data for that type does + * not exist or the data transfer contains no data. + * + * @param type Type of data to retrieve. + */ + getData(type) { + return this._data[type] || ''; + } + /** + * Set the data for a given type. + * + * For a list of recommended drag types, please see + * https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Recommended_Drag_Types. + * + * @param type Type of data to add. + * @param value Data to add. + */ + setData(type, value) { + this._data[type] = value; + } + /** + * Set the image to be used for dragging if a custom one is desired. + * + * @param img An image element to use as the drag feedback image. + * @param offsetX The horizontal offset within the image. + * @param offsetY The vertical offset within the image. + */ + setDragImage(img, offsetX, offsetY) { + var ddt = DragDropTouch._instance; + ddt._imgCustom = img; + ddt._imgOffset = new wjcCore.Point(offsetX, offsetY); + } +} +/** + * Defines a class that adds support for touch-based HTML5 drag/drop operations. + * + * The @see:DragDropTouch class listens to touch events and raises the + * appropriate HTML5 drag/drop events as if the events had been caused + * by mouse actions. + * + * The purpose of this class is to enable using existing, standard HTML5 + * drag/drop code on mobile devices running IOS or Android. + * + * To use, include the DragDropTouch.js file on the page. The class will + * automatically start monitoring touch events and will raise the HTML5 + * drag drop events (dragstart, dragenter, dragleave, drop, dragend) which + * should be handled by the application. + * + * For details and examples on HTML drag and drop, see + * https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Drag_operations. + */ +export class DragDropTouch { + /** + * Initializes the single instance of the @see:DragDropTouch class. + */ + constructor() { + this._lastClick = 0; + // enforce singleton pattern + wjcCore.assert(!DragDropTouch._instance, 'DragDropTouch instance already created.'); + // listen to touch events + if ('ontouchstart' in document) { + var d = document, ts = this._touchstart.bind(this), tm = this._touchmove.bind(this), te = this._touchend.bind(this); + d.addEventListener('touchstart', ts); + d.addEventListener('touchmove', tm); + d.addEventListener('touchend', te); + d.addEventListener('touchcancel', te); + } + } + /** + * Gets a reference to the @see:DragDropTouch singleton. + */ + static getInstance() { + return DragDropTouch._instance; + } + // ** event handlers + _touchstart(e) { + if (this._shouldHandle(e)) { + // raise double-click and prevent zooming + if (Date.now() - this._lastClick < DragDropTouch._DBLCLICK) { + if (this._dispatchEvent(e, 'dblclick', e.target)) { + e.preventDefault(); + this._reset(); + return; + } + } + // clear all variables + this._reset(); + // get nearest draggable element + var src = wjcCore.closest(e.target, '[draggable]'); + if (src) { + // give caller a chance to handle the hover/move events + if (!this._dispatchEvent(e, 'mousemove', e.target) && + !this._dispatchEvent(e, 'mousedown', e.target)) { + // get ready to start dragging + this._dragSource = src; + this._ptDown = this._getPoint(e); + this._lastTouch = e; + e.preventDefault(); + // show context menu if the user hasn't started dragging after a while + setTimeout(() => { + if (this._dragSource == src && this._img == null) { + if (this._dispatchEvent(e, 'contextmenu', src)) { + this._reset(); + } + } + }, DragDropTouch._CTXMENU); + } + } + } + } + _touchmove(e) { + if (this._shouldHandle(e)) { + // see if target wants to handle move + var target = this._getTarget(e); + if (this._dispatchEvent(e, 'mousemove', target)) { + this._lastTouch = e; + e.preventDefault(); + return; + } + // start dragging + if (this._dragSource && !this._img) { + var delta = this._getDelta(e); + if (delta > DragDropTouch._THRESHOLD) { + this._dispatchEvent(e, 'dragstart', this._dragSource); + this._createImage(e); + this._dispatchEvent(e, 'dragenter', target); + } + } + // continue dragging + if (this._img) { + this._lastTouch = e; + e.preventDefault(); // prevent scrolling + if (target != this._lastTarget) { + this._dispatchEvent(this._lastTouch, 'dragleave', this._lastTarget); + this._dispatchEvent(e, 'dragenter', target); + this._lastTarget = target; + } + this._moveImage(e); + this._dispatchEvent(e, 'dragover', target); + } + } + } + _touchend(e) { + if (this._shouldHandle(e)) { + // see if target wants to handle up + if (this._dispatchEvent(this._lastTouch, 'mouseup', e.target)) { + e.preventDefault(); + return; + } + // user clicked the element but didn't drag, so clear the source and simulate a click + if (!this._img) { + this._dragSource = null; + this._dispatchEvent(this._lastTouch, 'click', e.target); + this._lastClick = Date.now(); + } + // finish dragging + this._destroyImage(); + if (this._dragSource) { + if (e.type.indexOf('cancel') < 0) { + this._dispatchEvent(this._lastTouch, 'drop', this._lastTarget); + } + this._dispatchEvent(this._lastTouch, 'dragend', this._dragSource); + this._reset(); + } + } + } + // ** utilities + // ignore events that have been handled or that involve more than one touch + _shouldHandle(e) { + return e && + !e.defaultPrevented && + e.touches && e.touches.length < 2; + } + // clear all members + _reset() { + this._destroyImage(); + this._dragSource = null; + this._lastTouch = null; + this._lastTarget = null; + this._ptDown = null; + this._dataTransfer = new DataTransfer(); + } + // get point for a touch event + _getPoint(e, page) { + if (e && e.touches) { + e = e.touches[0]; + } + wjcCore.assert(e && ('clientX' in e), 'invalid event?'); + if (page == true) { + return new wjcCore.Point(e.pageX, e.pageY); + } + else { + return new wjcCore.Point(e.clientX, e.clientY); + } + } + // get distance between the current touch event and the first one + _getDelta(e) { + var p = this._getPoint(e); + return Math.abs(p.x - this._ptDown.x) + Math.abs(p.y - this._ptDown.y); + } + // get the element at a given touch event + _getTarget(e) { + var pt = this._getPoint(e), el = document.elementFromPoint(pt.x, pt.y); + while (el && getComputedStyle(el).pointerEvents == 'none') { + el = el.parentElement; + } + return el; + } + // create drag image from source element + _createImage(e) { + // just in case... + if (this._img) { + this._destroyImage(); + } + // create drag image from custom element or drag source + var src = this._imgCustom || this._dragSource; + this._img = src.cloneNode(true); + this._copyStyle(src, this._img); + this._img.style.top = this._img.style.left = '-9999px'; + // if creating from drag source, apply offset and opacity + if (!this._imgCustom) { + var rc = src.getBoundingClientRect(), pt = this._getPoint(e); + this._imgOffset = new wjcCore.Point(pt.x - rc.left, pt.y - rc.top); + this._img.style.opacity = DragDropTouch._OPACITY.toString(); + } + // add image to document + this._moveImage(e); + document.body.appendChild(this._img); + } + // dispose of drag image element + _destroyImage() { + if (this._img && this._img.parentElement) { + this._img.parentElement.removeChild(this._img); + } + this._img = null; + this._imgCustom = null; + } + // move the drag image element + _moveImage(e) { + requestAnimationFrame(() => { + var pt = this._getPoint(e, true); + wjcCore.setCss(this._img, { + position: 'absolute', + pointerEvents: 'none', + zIndex: 999999, + left: Math.round(pt.x - this._imgOffset.x), + top: Math.round(pt.y - this._imgOffset.y) + }); + }); + } + // copy properties from an object to another + _copyProps(dst, src, props) { + for (var i = 0; i < props.length; i++) { + var p = props[i]; + dst[p] = src[p]; + } + } + _copyStyle(src, dst) { + // remove potentially troublesome attributes + DragDropTouch._rmvAtts.forEach(function (att) { + dst.removeAttribute(att); + }); + // copy canvas content + if (src instanceof HTMLCanvasElement) { + var cSrc = src, cDst = dst; + cDst.width = cSrc.width; + cDst.height = cSrc.height; + cDst.getContext('2d').drawImage(cSrc, 0, 0); + } + // copy style + var cs = getComputedStyle(src); + for (var i = 0; i < cs.length; i++) { + var key = cs[i]; + dst.style[key] = cs[key]; + } + dst.style.pointerEvents = 'none'; + // and repeat for all children + for (var i = 0; i < src.children.length; i++) { + this._copyStyle(src.children[i], dst.children[i]); + } + } + _dispatchEvent(e, type, target) { + if (e && target) { + var evt = document.createEvent('Event'), t = e.touches ? e.touches[0] : e; + evt.initEvent(type, true, true); + evt.button = 0; + evt.which = evt.buttons = 1; + this._copyProps(evt, e, DragDropTouch._kbdProps); + this._copyProps(evt, t, DragDropTouch._ptProps); + evt.dataTransfer = this._dataTransfer; + target.dispatchEvent(evt); + return evt.defaultPrevented; + } + return false; + } +} +/*private*/ DragDropTouch._instance = new DragDropTouch(); // singleton +// constants +DragDropTouch._THRESHOLD = 5; // pixels to move before drag starts +DragDropTouch._OPACITY = 0.5; // drag image opacity +DragDropTouch._DBLCLICK = 500; // max ms between clicks in a double click +DragDropTouch._CTXMENU = 900; // ms to hold before raising 'contextmenu' event +// copy styles/attributes from drag source to drag image element +DragDropTouch._rmvAtts = 'id,class,style,draggable'.split(','); +// synthesize and dispatch an event +// returns true if the event has been handled (e.preventDefault == true) +DragDropTouch._kbdProps = 'altKey,ctrlKey,metaKey,shiftKey'.split(','); +DragDropTouch._ptProps = 'pageX,pageY,clientX,clientY,screenX,screenY'.split(','); diff --git a/src/routes.js b/src/routes.js index eb71b0f..1ede7b1 100644 --- a/src/routes.js +++ b/src/routes.js @@ -50,6 +50,7 @@ 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 DashboardDND = React.lazy(() => import('./components/wj/App')) const DashboardCustomer = React.lazy(() => import('./views/Dashboard/DashboardCustomer')); const DashboardProject = React.lazy(() => import('./views/Dashboard/DashboardProject')); const DashboardProjectCarousell = React.lazy(() => import('./views/Dashboard/DashboardProjectCarousell')); @@ -60,6 +61,7 @@ const DemoManagement = React.lazy(() => import('./views/SimproV2/Demo')) const routes = [ { path: '/', exact: true, name: 'Home' }, { path: '/dashboard', name: 'DashboardBOD', component: DashboardBOD }, + { path: '/dashboard-dnd', name: 'DashboardBOD', component: DashboardDND }, { path: '/dashboard-customer/:PROJECT_ID/:GANTT_ID/:SCURVE', name: 'DashboardCustomer', component: DashboardCustomer }, { path: '/dashboard-project/:PROJECT_ID/:GANTT_ID/:Header', exact: true, name: 'Dashboard Project', component: DashboardProject }, { path: '/dashboard-perproject', exact: true, name: 'Dashboard Project Carousell', component: DashboardProjectCarousell }, From 6e43a41b4729d1ed5e40df930a47c9d3abe3a064 Mon Sep 17 00:00:00 2001 From: farhantock Date: Wed, 28 Feb 2024 08:51:15 +0700 Subject: [PATCH 03/24] rename folder demo --- src/views/SimproV2/{Demo => DemoRequest}/DialogForm.js | 0 src/views/SimproV2/{Demo => DemoRequest}/index.js | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) rename src/views/SimproV2/{Demo => DemoRequest}/DialogForm.js (100%) rename src/views/SimproV2/{Demo => DemoRequest}/index.js (99%) diff --git a/src/views/SimproV2/Demo/DialogForm.js b/src/views/SimproV2/DemoRequest/DialogForm.js similarity index 100% rename from src/views/SimproV2/Demo/DialogForm.js rename to src/views/SimproV2/DemoRequest/DialogForm.js diff --git a/src/views/SimproV2/Demo/index.js b/src/views/SimproV2/DemoRequest/index.js similarity index 99% rename from src/views/SimproV2/Demo/index.js rename to src/views/SimproV2/DemoRequest/index.js index a3fc1fa..6ad5b0a 100644 --- a/src/views/SimproV2/Demo/index.js +++ b/src/views/SimproV2/DemoRequest/index.js @@ -202,7 +202,7 @@ const ProjectType = ({ params, ...props }) => { if (type === "save") { saveDemo(data); } else if (type === "edit") { - editMaterialR(data); + editDemo(data); } setDataEdit([]) setOpenDialog(false) @@ -221,7 +221,7 @@ const ProjectType = ({ params, ...props }) => { } } - const editMaterialR = async (data) => { + const editDemo = async (data) => { let urlEdit = DEMO_MANAGEMENT_EDIT(data.id) const formData = data const result = await axios.put(urlEdit, formData, HEADER) From c29b72887570da29d1a3b94efca8ff85a82f51ca Mon Sep 17 00:00:00 2001 From: farhantock Date: Wed, 28 Feb 2024 10:19:17 +0700 Subject: [PATCH 04/24] new router and new andpoint --- src/const/ApiConst.js | 61 ++++++++++++++++++------------------------- src/routes.js | 11 +++++--- 2 files changed, 34 insertions(+), 38 deletions(-) diff --git a/src/const/ApiConst.js b/src/const/ApiConst.js index 9c07bd7..7bdc817 100644 --- a/src/const/ApiConst.js +++ b/src/const/ApiConst.js @@ -118,46 +118,15 @@ export const TOKEN_ADW = // export let BASE_OSPRO = "https://ospro-api.ospro.id"; // export let BASE_OSPRO = "https://project-api.ospro.id"; export let BASE_OSPRO = "http://localhost:8444/generic-ospro-backend"; -// export let BASE_OSPRO = "http://103.73.125.81:8444"; // ip public adw 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)=>{ +export let BASE_SIMPRO_LUMEN_FILE_COMPANY = (file, company_name) => { return `${BASE_OSPRO}/assets/${company_name}/file/project/${file}`; } -export let BASE_SIMPRO_LUMEN_IMAGE_COMPANY = (file, company_name)=>{ +export let BASE_SIMPRO_LUMEN_IMAGE_COMPANY = (file, company_name) => { return `${BASE_OSPRO}/assets/${company_name}/image/${file}`; } -// switch (APP_MODE) { -// case 'KIT': -// BASE_OSPRO = "https://kit-api.oslogdev.com" - -// //for KIT server -// // BASE_SIMPRO_LUMEN = `${BASE_OSPRO}/api`; -// // BASE_SIMPRO_LUMEN_IMAGE = `${BASE_SIMPRO_LUMEN}/assets/image`; -// // BASE_SIMPRO_LUMEN_FILE = `${BASE_SIMPRO_LUMEN}/assets/file/project`; - -// break; -// case 'ADW': -// // BASE_OSPRO = "https://adw-api.ospro.id" -// BASE_OSPRO = "http://103.73.125.81:8444"; - -// BASE_SIMPRO_LUMEN = `${BASE_OSPRO}/api`; -// BASE_SIMPRO_LUMEN_IMAGE = `${BASE_OSPRO}/assets/image`; -// BASE_SIMPRO_LUMEN_FILE = `${BASE_OSPRO}/assets/file/project`; - -// break; -// case 'IU': -// BASE_OSPRO = "https://api-iu.ospro.id" - -// BASE_SIMPRO_LUMEN = `${BASE_OSPRO}/api`; -// BASE_SIMPRO_LUMEN_IMAGE = `${BASE_OSPRO}/assets/image`; -// BASE_SIMPRO_LUMEN_FILE = `${BASE_OSPRO}/assets/file/project`; - -// break; -// default: -// BASE_OSPRO = "https://ospro-api.ospro.id" -// } export const USERROLE_ADD = `${BASE_SIMPRO}/user-role/add`; export const USERROLE_SEARCH = `${BASE_SIMPRO}/user-role/search`; @@ -849,8 +818,6 @@ export const MENU_COMPANY_DELETE = (id) => { return `${BASE_SIMPRO_LUMEN}/menu-company/delete/${id}`; }; - - export const DEMO_MANAGEMENT_ADD = `${BASE_SIMPRO_LUMEN}/demo-management/add`; export const DEMO_MANAGEMENT_SEARCH = `${BASE_SIMPRO_LUMEN}/demo-management/search`; export const DEMO_MANAGEMENT_EDIT = (id) => { @@ -863,3 +830,27 @@ export const DEMO_MANAGEMENT_DELETE = (id) => { return `${BASE_SIMPRO_LUMEN}/demo-management/delete/${id}`; }; export const DEMO_MANAGEMENT_LIST = `${BASE_SIMPRO_LUMEN}/demo-management/list`; + +export const REFERRAL_CODE_ADD = `${BASE_SIMPRO_LUMEN}/refferal-code/add`; +export const REFERRAL_CODE_SEARCH = `${BASE_SIMPRO_LUMEN}/refferal-code/search`; +export const REFERRAL_CODE_EDIT = (id) => { + return `${BASE_SIMPRO_LUMEN}/refferal-code/update/${id}`; +}; +export const REFERRAL_CODE_GET_ID = (id) => { + return `${BASE_SIMPRO_LUMEN}/refferal-code/edit/${id}`; +}; +export const REFERRAL_CODE_DELETE = (id) => { + return `${BASE_SIMPRO_LUMEN}/refferal-code/delete/${id}`; +}; + +export const SALES_CONTACT_ADD = `${BASE_SIMPRO_LUMEN}/sales-contact/add`; +export const SALES_CONTACT_SEARCH = `${BASE_SIMPRO_LUMEN}/sales-contact/search`; +export const SALES_CONTACT_EDIT = (id) => { + return `${BASE_SIMPRO_LUMEN}/sales-contact/update/${id}`; +}; +export const SALES_CONTACT_GET_ID = (id) => { + return `${BASE_SIMPRO_LUMEN}/sales-contact/edit/${id}`; +}; +export const SALES_CONTACT_DELETE = (id) => { + return `${BASE_SIMPRO_LUMEN}/sales-contact/delete/${id}`; +}; \ No newline at end of file diff --git a/src/routes.js b/src/routes.js index 6fafb24..ebe60fe 100644 --- a/src/routes.js +++ b/src/routes.js @@ -56,7 +56,9 @@ const DashboardProjectCarousell = React.lazy(() => import('./views/Dashboard/Das const MapMonitoring = React.lazy(() => import('./views/MapMonitoring')); const Settings = React.lazy(() => import('./views/SimproV2/Settings/Desktop')); const CompanyManagement = React.lazy(() => import('./views/Master/MasterCompany')) -const DemoManagement = React.lazy(() => import('./views/SimproV2/Demo')) +const DemoRequest = React.lazy(() => import('./views/SimproV2/DemoRequest')) +const ReferralCode = React.lazy(() => import('./views/SimproV2/ReferralCode')) +const SalesContact = React.lazy(() => import('./views/SimproV2/SalesContact')) const routes = [ { path: '/', exact: true, name: 'Home' }, { path: '/dashboard', name: 'DashboardBOD', component: DashboardBOD }, @@ -119,8 +121,11 @@ const routes = [ { path: '/map-monitoring', exact: true, name: 'Map Monitoring', component: MapMonitoring }, // { path: '/dashboard-project/:ID/:GANTTID', exact: true, name: 'Dashboard Project', component: DashboardProject }, { path: '/settings', exact: true, name: 'Settings', component: Settings }, - { path: '/company-management', exact: true, name: 'Company Management', component: CompanyManagement }, - { path: '/demo-management', exact: true, name: 'Demo Management', component: DemoManagement }, + { path: '/register-management', exact: true, name: 'Register Management', component: CompanyManagement }, + { path: '/demo-request', exact: true, name: 'Request Demo', component: DemoRequest }, + { path: '/referral-code-management', exact: true, name: 'Referral Code Management', component: ReferralCode }, + { path: '/sales-contact', exact: true, name: 'Sales Contact', component: SalesContact }, + ]; export default routes; From 9397736600394ad46436ffd1e6e05c807f8ebcda Mon Sep 17 00:00:00 2001 From: farhantock Date: Wed, 28 Feb 2024 10:19:45 +0700 Subject: [PATCH 05/24] update vocabeelary --- src/const/en.json | 7 +++++++ src/const/id.json | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/src/const/en.json b/src/const/en.json index 4a06ec8..59d3603 100644 --- a/src/const/en.json +++ b/src/const/en.json @@ -1,6 +1,8 @@ { "3days": "3 Days Ago", "7days": "7 Days Ago", + "amount": "Amount", + "allocation": "Allocation", "action": "Action", "add": "Add", "address": "Address", @@ -16,6 +18,8 @@ "cancel": "Cancel", "close": "Close", "color": "Color", + "code": "Code", + "discount": "Discount", "date": "Date", "dateSend": "Send Date", "dateAbsent": "Absent Date", @@ -37,6 +41,7 @@ "demoAdd": "Add Demo", "employeeType": "Employee Type", "edit": "Edit", + "exp": "Expire", "export": "Export", "exportExcel": "Export Excel", "exportPdf": "Export Pdf", @@ -54,6 +59,8 @@ "inputDescription": "Input Description", "inputParentMenu": "Select Parent Menu", "inputAliasMenu": "Input Menu Alias", + "inputAmount": "Input Amount", + "inputCode": "Input Code", "inputOrder": "Input Order", "inputUrl": "Input URL", "inputMsg": "Input Message", diff --git a/src/const/id.json b/src/const/id.json index 4d25734..e8103b5 100644 --- a/src/const/id.json +++ b/src/const/id.json @@ -1,6 +1,8 @@ { "3days": "3 Hari Yang lalu", "7days": "7 Hari Yang lalu", + "amount": "Jumlah", + "allocation": "Kuota", "action": "Aksi", "add": "Tambah", "address": "Alamat", @@ -16,6 +18,8 @@ "cancel": "Batal", "close": "Tutup", "color": "Warna", + "code": "Kode", + "discount": "Diskon", "date": "Tanggal", "dateSend": "Tanggal Kirim", "dateAbsent": "Tanggal Absen", @@ -36,6 +40,7 @@ "divisionAdd": "Tambah Divisi", "demoAdd": "Tambah Demo", "edit": "Ubah", + "exp": "Batas Waktu", "export": "Ekspor", "exportExcel": "Ekspor Excel", "exportPdf": "Ekspor Pdf", @@ -69,6 +74,8 @@ "inputNik": "Masukan NIK (KTP)", "inputMessage": "Masukan Pesan", "inputRole": "Masukan Peran", + "inputAmount": "Masukan Jumlah", + "inputCode": "Masukan Kode", "image": "Gambar", "imageCheck": "Lihat Selfie Presensi", "locIn": "Lokasi Masuk", From 047a774f1607be7b875529194c05a79d641ac1f3 Mon Sep 17 00:00:00 2001 From: farhantock Date: Wed, 28 Feb 2024 10:19:54 +0700 Subject: [PATCH 06/24] remove console log --- src/containers/DefaultLayout/DefaultLayout.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/containers/DefaultLayout/DefaultLayout.js b/src/containers/DefaultLayout/DefaultLayout.js index 30e608b..3f2fbbb 100644 --- a/src/containers/DefaultLayout/DefaultLayout.js +++ b/src/containers/DefaultLayout/DefaultLayout.js @@ -111,8 +111,6 @@ class DefaultLayout extends Component { default: faviconElement.type = 'image/png'; } - console.log('faviconElement.type', faviconElement.type); - console.log('faviconContent', faviconContent); faviconElement.href = faviconContent || `${process.env.PUBLIC_URL}/OSPRO.ico`; From e0dedfd2efc01c9870c4311182b6f110db8dbd39 Mon Sep 17 00:00:00 2001 From: farhantock Date: Wed, 28 Feb 2024 15:57:18 +0700 Subject: [PATCH 07/24] update menu from company menu --- .../Master/MasterRoles/DialogMenuRoles.js | 160 ++++++++++++------ src/views/Master/MasterRoles/index.js | 9 +- 2 files changed, 114 insertions(+), 55 deletions(-) diff --git a/src/views/Master/MasterRoles/DialogMenuRoles.js b/src/views/Master/MasterRoles/DialogMenuRoles.js index b066bc4..569847c 100644 --- a/src/views/Master/MasterRoles/DialogMenuRoles.js +++ b/src/views/Master/MasterRoles/DialogMenuRoles.js @@ -3,7 +3,7 @@ import { Modal, ModalHeader, ModalBody, ModalFooter, Row, Col, Table } from 'rea import { Button, Form, FormGroup, Label, Input } from 'reactstrap'; import 'antd/dist/antd.css'; import axios from 'axios'; -import { MENU_SEARCH } from '../../../const/ApiConst.js'; +import { MENU_COMPANY_SEARCH } from '../../../const/ApiConst.js'; const token = window.localStorage.getItem('token'); @@ -36,6 +36,7 @@ export default class DialogMenuRoles extends Component { stateUpdateAll: false, stateDeleteAll: false, allChecked: true, + company_id: props.company_id || null, } } async componentDidMount() { @@ -64,10 +65,11 @@ export default class DialogMenuRoles extends Component { const payload = { "paging": { "start": 0, "length": -1 }, "columns": [ - { "name": "name", "logic_operator": "ilike", "value": "", "operator": "AND" } + { "name": "company_id", "logic_operator": "=", "value": this.state.company_id, "operator": "AND" } ], "joins": [ - { "name": "t_roles_menu", "column_join": "id", "column_results": ["create", "update", "delete", "read"] } + { "name": "t_roles_menu", "column_join": "menu_id", "column_results": ["create", "update", "delete", "read"] }, + { "name": "m_menu", "column_join": "menu_id", "column_results": ["name"] } ], "orders": { "columns": ["id"], "ascending": false } } @@ -75,11 +77,9 @@ export default class DialogMenuRoles extends Component { const result = await axios - .post(MENU_SEARCH, payload, config) + .post(MENU_COMPANY_SEARCH, payload, config) .then(res => res) .catch((error) => error.response); - - // console.log('78', result); if (result && result.data && result.data.code == 200) { this.setState({ menu: result.data.data }, () => { this.setStateMenu(false); @@ -321,8 +321,7 @@ export default class DialogMenuRoles extends Component { }) - console.log('arrayData', arrayData); - // this.props.closeDialog('save', arrayData); + this.props.closeDialog('save', arrayData); this.setState({ id: 0 }); } @@ -337,31 +336,104 @@ export default class DialogMenuRoles extends Component { this.setState({ stateMenu: copyStateMenu }) } - handleChangeCheckboxRead = (checked, index) => { + handleChangeCheckboxRead = (checked, index, menuItem = null, menuIdxList = []) => { let copyStateRead = [...this.state.stateRead]; copyStateRead[index] = checked; + let menu = this.state.menu; + let checkMenuParent = menu.map((state, index) => { + if (state.parent_menu_id === menuItem.menu_id) { + return state.menu_id + } else { + return null + } + }).filter(index => index !== null); + let stateReadIdx = []; + menuIdxList.forEach((menu, index) => { + checkMenuParent.forEach((check, idx) => { + if (check === menu) { + stateReadIdx.push(index); + } + }); + }); + stateReadIdx.map((stateRead) => { + copyStateRead[stateRead] = checked + }) this.setState({ stateRead: copyStateRead }) } - handleChangeCheckboxCreate = (checked, index) => { + handleChangeCheckboxCreate = (checked, index, menuItem = null, menuIdxList = []) => { let copyStateCreate = [...this.state.stateCreate]; copyStateCreate[index] = checked; - + let menu = this.state.menu; + let checkMenuParent = menu.map((state, index) => { + if (state.parent_menu_id === menuItem.menu_id) { + return state.menu_id + } else { + return null + } + }).filter(index => index !== null); + let stateCreateIdx = []; + menuIdxList.forEach((menu, index) => { + checkMenuParent.forEach((check, idx) => { + if (check === menu) { + stateCreateIdx.push(index); + } + }); + }); + stateCreateIdx.map((stateCreate) => { + copyStateCreate[stateCreate] = checked + }) this.setState({ stateCreate: copyStateCreate }) } - handleChangeCheckboxEdit = (checked, index) => { + handleChangeCheckboxEdit = (checked, index, menuItem = null, menuIdxList = []) => { let copyStateEdit = [...this.state.stateUpdate]; copyStateEdit[index] = checked; - + let menu = this.state.menu; + let checkMenuParent = menu.map((state, index) => { + if (state.parent_menu_id === menuItem.menu_id) { + return state.menu_id + } else { + return null + } + }).filter(index => index !== null); + let stateEditIdx = []; + menuIdxList.forEach((menu, index) => { + checkMenuParent.forEach((check, idx) => { + if (check === menu) { + stateEditIdx.push(index); + } + }); + }); + stateEditIdx.map((stateEdit) => { + copyStateEdit[stateEdit] = checked + }) this.setState({ stateUpdate: copyStateEdit }) } - handleChangeCheckboxDelete = (checked, index) => { + handleChangeCheckboxDelete = (checked, index, menuItem = null, menuIdxList = []) => { let copyStateDelete = [...this.state.stateDelete]; copyStateDelete[index] = checked; - + let menu = this.state.menu; + let checkMenuParent = menu.map((state, index) => { + if (state.parent_menu_id === menuItem.menu_id) { + return state.menu_id + } else { + return null + } + }).filter(index => index !== null); + let stateDeleteIdx = []; + menuIdxList.forEach((menu, index) => { + checkMenuParent.forEach((check, idx) => { + if (check === menu) { + stateDeleteIdx.push(index); + } + }); + }); + stateDeleteIdx.map((stateDelete) => { + copyStateDelete[stateDelete] = checked + }) this.setState({ stateDelete: copyStateDelete }) } @@ -397,61 +469,43 @@ export default class DialogMenuRoles extends Component { this.setState({ stateDelete: copyStateDelete }) } - // renderForm = () => { - // const { menu, stateRead, stateCreate, stateUpdate, stateDelete } = this.state - // console.log('menu', menu); - // return ( - // menu.map((val, index) => { - - // return ( - // - // {val.name} - // this.handleChangeCheckboxRead(e.target.checked, index)} defaultChecked={stateRead[index]} /> - // this.handleChangeCheckboxCreate(e.target.checked, index)} defaultChecked={stateCreate[index]} /> - // this.handleChangeCheckboxEdit(e.target.checked, index)} defaultChecked={stateUpdate[index]} /> - // this.handleChangeCheckboxDelete(e.target.checked, index)} defaultChecked={stateDelete[index]} /> - // - // ) - // }) - // ) - - // } - renderForm = () => { const { menu, stateRead, stateCreate, stateUpdate, stateDelete } = this.state; - + let menuIdxList = [] + let menuIdx = 0 const getChildren = (parentId) => { - return menu.filter(item => item.parent_id === parentId); + return menu.filter(item => item.parent_menu_id === parentId); }; - // Function to render menu items const renderMenu = (parentId, depth = 0) => { const children = getChildren(parentId); return children.map((menuItem, index) => { - const paddingLeft = depth * 20; - const fontWeight = menuItem.parent_id === null ? 'bold' : 'normal'; + const currentIndex = menuIdx; + menuIdxList[currentIndex] = menuItem.menu_id; + menuIdx++ + const paddingLeft = depth * 30; return ( - + - {menuItem.name} - this.handleChangeCheckboxRead(e.target.checked, index)} defaultChecked={stateRead[index]} /> - this.handleChangeCheckboxCreate(e.target.checked, index)} defaultChecked={stateCreate[index]} /> - this.handleChangeCheckboxEdit(e.target.checked, index)} defaultChecked={stateUpdate[index]} /> - this.handleChangeCheckboxDelete(e.target.checked, index)} defaultChecked={stateDelete[index]} /> + {menuItem.join_second_name} + this.handleChangeCheckboxRead(e.target.checked, currentIndex, menuItem, menuIdxList)} defaultChecked={stateRead[currentIndex]} /> + this.handleChangeCheckboxCreate(e.target.checked, currentIndex, menuItem, menuIdxList)} defaultChecked={stateCreate[currentIndex]} /> + this.handleChangeCheckboxEdit(e.target.checked, currentIndex, menuItem, menuIdxList)} defaultChecked={stateUpdate[currentIndex]} /> + this.handleChangeCheckboxDelete(e.target.checked, currentIndex, menuItem, menuIdxList)} defaultChecked={stateDelete[currentIndex]} /> - {renderMenu(menuItem.id, depth + 1)} {/* Recursively render children */} + {renderMenu(menuItem.menu_id, depth + 1)} ); }); }; - - // Render top-level menu items (parents) + menuIdxList = [] + menuIdx = 0 return ( - {this.renderAll()} {/* Render the "All" row */} - {renderMenu(null)} {/* Render the menu items */} + {this.renderAll()} + {renderMenu(null)} ); }; @@ -525,7 +579,7 @@ export default class DialogMenuRoles extends Component { Create - Edite + Edit Delete @@ -546,4 +600,4 @@ export default class DialogMenuRoles extends Component { ) } -} +} \ No newline at end of file diff --git a/src/views/Master/MasterRoles/index.js b/src/views/Master/MasterRoles/index.js index 7b45a97..3ff0663 100644 --- a/src/views/Master/MasterRoles/index.js +++ b/src/views/Master/MasterRoles/index.js @@ -331,10 +331,15 @@ class index extends Component { let promises = [] let result = [] dataArray.map((val, index) => { - if (val.checked === true) { + if (val.read === true) { const formData = { menu_id: val.menu_id, - role_id: val.roles_id + role_id: val.roles_id, + create: val.create, + read: val.read, + update: val.update, + delete: val.delete + } promises.push(axios.post(ROLEMENU_ADD, formData, this.state.config) .then(res => result.push(res))) From 01a9d444aab3bb1978bf675060b8cf86ac61abc3 Mon Sep 17 00:00:00 2001 From: farhantock Date: Wed, 28 Feb 2024 15:57:36 +0700 Subject: [PATCH 08/24] diskon and referral code page --- src/views/SimproV2/ReferralCode/DialogForm.js | 190 +++++++++ src/views/SimproV2/ReferralCode/index.js | 372 ++++++++++++++++++ 2 files changed, 562 insertions(+) create mode 100644 src/views/SimproV2/ReferralCode/DialogForm.js create mode 100644 src/views/SimproV2/ReferralCode/index.js diff --git a/src/views/SimproV2/ReferralCode/DialogForm.js b/src/views/SimproV2/ReferralCode/DialogForm.js new file mode 100644 index 0000000..6e5a46c --- /dev/null +++ b/src/views/SimproV2/ReferralCode/DialogForm.js @@ -0,0 +1,190 @@ +import React, { useEffect, useState } from 'react' +import { + Modal, ModalHeader, ModalBody, ModalFooter, + Button, Form, FormGroup, Label, Input, Col, Row +} from 'reactstrap'; +import { Select, DatePicker } from 'antd'; +import 'antd/dist/antd.css'; +import { useTranslation } from 'react-i18next'; +import "rc-color-picker/assets/index.css"; +import moment from 'moment'; +import { formatNumber } from "../../../const/CustomFunc"; +const { Option } = Select +const DialogForm = ({ openDialog, closeDialog, toggleDialog, typeDialog, dataEdit }) => { + const [id, setId] = useState(0) + const [code, setCode] = useState('') + const [amount, setAmount] = useState(0) + const [description, setDescription] = useState('') + const [exp, setExp] = useState('') + const [type, setType] = useState('') + const [allocation, setAllocation] = useState(0) + const { t } = useTranslation() + + useEffect(() => { + console.log('dataEdit', dataEdit); + if (typeDialog === "Edit") { + setId(dataEdit.id) + setCode(dataEdit.code) + setAmount(dataEdit.amount) + setDescription(dataEdit.description) + setExp(dataEdit.exp) + setType(dataEdit.type) + } else { + handleClear() + } + }, [dataEdit, openDialog]) + + const validation = () => { + if (!code || code === "") { + alert("Code cannot be empty!"); + return true; + } + } + const handleSave = () => { + let data = ''; + const err = validation(); + + if (!err) { + if (typeDialog === "Save") { + data = { + code, + amount, + description, + exp, + type, + allocation + } + console.log('data', data); + closeDialog('save', data); + } else { + data = { + id, + code, + amount, + description, + exp, + type, + allocation + } + console.log('data', data); + closeDialog('edit', data); + } + handleClear() + + } + } + const handleCancel = () => { + closeDialog('cancel', 'none') + handleClear() + } + const handleClear = () => { + setId(0) + setCode('') + setAmount('') + setDescription('') + setExp('') + } + + + const renderForm = () => { + return ( +
+ + + * Wajib diisi. + + + + + + + setCode(e.target.value)} + placeholder={t('inputCode')} + /> + + + + + + setAmount(e.target.value)} + placeholder={t('inputAmount')} + /> + + + + + + + + setExp(date)} + showTime={{ defaultValue: moment('00:00:00', 'HH:mm:ss') }} + /> + + + + + + + setAllocation(e.target.value)} + placeholder={t('input')} + /> + + + + + + setType(e.target.value)} + placeholder={t('inputType')} + /> + + + + + + + + setDescription(e.target.value)} placeholder={t('inputDescription')} /> + + + +
+ ) + } + + + return ( + <> + + {typeDialog == "Save" ? `Add` : "Edit"} {t('Demo')} + + {renderForm()} + + + {' '} + + + + + ) + +} + +export default DialogForm; diff --git a/src/views/SimproV2/ReferralCode/index.js b/src/views/SimproV2/ReferralCode/index.js new file mode 100644 index 0000000..d340325 --- /dev/null +++ b/src/views/SimproV2/ReferralCode/index.js @@ -0,0 +1,372 @@ +import * as XLSX from 'xlsx'; +import DialogForm from './DialogForm'; +import React, { useState, useEffect } from 'react'; +import SweetAlert from 'react-bootstrap-sweetalert'; +import axios from "../../../const/interceptorApi" +import { Card, CardBody, CardHeader, Col, Row, Input, Table } from 'reactstrap'; +import { REFERRAL_CODE_EDIT, REFERRAL_CODE_SEARCH, REFERRAL_CODE_ADD, REFERRAL_CODE_DELETE } from '../../../const/ApiConst'; +import { NotificationContainer, NotificationManager } from 'react-notifications'; +import { Spin, Button, Tooltip } from 'antd'; +import { useTranslation } from 'react-i18next'; +import moment from 'moment'; +import toRupiah from '@develoka/angka-rupiah-js'; + +const ProjectType = ({ 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 [openDialog, setOpenDialog] = useState(false) + const [rowsPerPage, setRowsPerPage] = useState(10) + const [search, setSearch] = useState("") + const [totalPage, setTotalPage] = useState(0) + const [typeDialog, setTypeDialog] = useState('Save') + const [dataDemo, setDataDemo] = useState([]) + const [listCompany, setListCompany] = useState([]) + const [loading, setLoading] = useState(true); + const { t } = useTranslation() + const column = [ + { name: t('code') }, + { name: t('type') }, + { name: t('amount') }, + { name: t('exp') }, + { name: t('allocation') }, + { name: t('description') }, + ].filter(column => column && column.name); + useEffect(() => { + getReferralCode(); + }, [currentPage, rowsPerPage, search]) + + useEffect(() => { + const cekData = dataExport || [] + if (cekData.length > 0) { + exportExcel() + } + }, [dataExport]) + + const getReferralCode = 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': [] + } + + const result = await axios + .post(REFERRAL_CODE_SEARCH, payload, HEADER) + .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 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': [] + } + + const result = await axios + .post(REFERRAL_CODE_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 = {}; + dataRow["Kode"] = val.code; + dataRow["Jumlah"] = val.amount; + dataRow["Kuota"] = val.allocation; + dataRow["Kadaluarsa"] = val.exp; + dataRow["Deskripsi"] = val.deskription; + 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 handleSearch = e => { + const value = e.target.value + setSearch(value); + setCurrentPage(1) + }; + + const handleOpenDialog = (type) => { + setOpenDialog(true) + setTypeDialog(type) + } + + const handleEdit = (data) => { + setDataEdit(data) + handleOpenDialog('Edit'); + } + + const handleDelete = async (id) => { + await setAlertDelete(true) + await setIdDelete(id) + } + + const handleCloseDialog = (type, data) => { + if (type === "save") { + saveReferralCode(data); + } else if (type === "edit") { + editReferralCode(data); + } + setDataEdit([]) + setOpenDialog(false) + } + + const saveReferralCode = async (data) => { + const formData = data + const result = await axios.post(REFERRAL_CODE_ADD, formData, HEADER) + .then(res => res) + .catch((error) => error.response); + if (result && result.data && result.data.code === 200) { + getReferralCode() + NotificationManager.success(`Data berhasil ditambahkan`, 'Success!!'); + } else { + NotificationManager.error(`Data gagal ditambahkan`, 'Failed!!'); + } + } + + const editReferralCode = async (data) => { + let urlEdit = REFERRAL_CODE_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) { + getReferralCode(); + NotificationManager.success(`Data berhasil diubah`, 'Success!!'); + } else { + NotificationManager.error(`Data gagal diubah`, `Failed!!`); + } + } + + const toggleAddDialog = () => { + setOpenDialog(!openDialog) + } + + const onConfirmDelete = async () => { + let url = REFERRAL_CODE_DELETE(idDelete); + const result = await axios.delete(url, HEADER) + .then(res => res) + .catch((error) => error.response); + + if (result && result.data && result.data.code === 200) { + getReferralCode() + 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) + setIdDelete(0) + } + + const onShowSizeChange = (current, pageSize) => { + setRowsPerPage(pageSize) + } + + const onPagination = (current, pageSize) => { + setCurrentPage(current) + } + + const dataNotAvailable = () => { + if (dataTable.length === 0) { + return ( + + {t('noData')} + + ) + } + } + + return ( +
+ + + {t('deleteMsg')} + + toggleAddDialog} + typeDialog={typeDialog} + dataEdit={dataEdit} + clickOpenModal={clickOpenModal} + dataParent={allDataMenu} + /> + + +

{pageName}

+ + + + + + + + + + + + + +
+ + + + + + + {column.map((i, index) => { + return ( + + ) + })} + + + + {dataNotAvailable()} + {dataTable.map((n, index) => { + return ( + + + + + + + + + + ) + })} + +
Aksi{i.name}
+ + handleDelete(n.id)}> + + + handleEdit(n)}> + + {n.code}{n.type}{toRupiah(n.amount)}{n?.exp ? moment(n.exp).format('DD-MM-YYYY HH:mm') : "-"}{n.allocation ? n.allocation : "-"}{n.description ? n.description : "-"}
+
+
+
+
+ ) +} + +export default ProjectType; From 4c6025ebf27c870fdd800d656341bc5ccb67c199 Mon Sep 17 00:00:00 2001 From: farhantock Date: Wed, 28 Feb 2024 16:03:33 +0700 Subject: [PATCH 09/24] contact sales page --- src/views/SimproV2/SalesContact/DialogForm.js | 201 ++++++++++ src/views/SimproV2/SalesContact/index.js | 374 ++++++++++++++++++ 2 files changed, 575 insertions(+) create mode 100644 src/views/SimproV2/SalesContact/DialogForm.js create mode 100644 src/views/SimproV2/SalesContact/index.js diff --git a/src/views/SimproV2/SalesContact/DialogForm.js b/src/views/SimproV2/SalesContact/DialogForm.js new file mode 100644 index 0000000..da508d4 --- /dev/null +++ b/src/views/SimproV2/SalesContact/DialogForm.js @@ -0,0 +1,201 @@ +import React, { useEffect, useState } from 'react' +import { + Modal, ModalHeader, ModalBody, ModalFooter, + Button, Form, FormGroup, Label, Input, Col, Row +} from 'reactstrap'; +import { Select } from 'antd'; +import 'antd/dist/antd.css'; +import { useTranslation } from 'react-i18next'; +import "rc-color-picker/assets/index.css"; +const { Option } = Select +const DialogForm = ({ openDialog, closeDialog, toggleDialog, typeDialog, dataEdit }) => { + const [id, setId] = useState(0) + const [name, setName] = useState('') + const [email, setEmail] = useState('') + const [message, setMessage] = useState('') + const [phoneNumber, setPhoneNumber] = useState('') + const [role, setRole] = useState(null) + const [status, setStatus] = useState('') + const { t } = useTranslation() + + useEffect(() => { + if (typeDialog === "Edit") { + setId(dataEdit.id) + setName(dataEdit.name) + setEmail(dataEdit.email) + setMessage(dataEdit.message) + setPhoneNumber(dataEdit.number_phone) + setStatus(dataEdit.status) + setRole(dataEdit.role) + } else { + setId(0) + setName('') + setEmail('') + setMessage('') + setStatus('') + setRole('') + setPhoneNumber('') + } + }, [dataEdit, openDialog]) + + const validation = () => { + if (!name || name === "") { + alert("Name cannot be empty!"); + return true; + } + } + const handleSave = () => { + let data = ''; + const err = validation(); + + if (!err) { + if (typeDialog === "Save") { + data = { + name, + email, + message, + number_phone: phoneNumber, + role, + status + } + closeDialog('save', data); + } else { + data = { + id, + name, + email, + message, + number_phone: phoneNumber, + role, + status + } + closeDialog('edit', data); + } + setId(0) + setName('') + } + } + const handleCancel = () => { + closeDialog('cancel', 'none') + setId(0) + setName('') + } + const onChangeStatus = (val) => { + setStatus(val) + } + + + const renderForm = () => { + return ( +
+ + + * Wajib diisi. + + + + + + + setName(e.target.value)} + placeholder={t('inputName')} + /> + + + + + + setEmail(e.target.value)} + placeholder={t('inputEmail')} + /> + + + + + + + + setName(e.target.value)} + placeholder={t('inputNoPhone')} + /> + + + + + + setRole(e.target.value)} + placeholder={t('inputRole')} + /> + + + + + + + + + + + + + + + + setMessage(e.target.value)} placeholder={t('inputMessage')} /> + + + +
+ ) + } + + + return ( + <> + + {typeDialog == "Save" ? `Add` : "Edit"} {t('Demo')} + + {renderForm()} + + + {' '} + + + + + ) + +} + +export default DialogForm; diff --git a/src/views/SimproV2/SalesContact/index.js b/src/views/SimproV2/SalesContact/index.js new file mode 100644 index 0000000..a1a3754 --- /dev/null +++ b/src/views/SimproV2/SalesContact/index.js @@ -0,0 +1,374 @@ +import * as XLSX from 'xlsx'; +import DialogForm from './DialogForm'; +import React, { useState, useEffect } from 'react'; +import SweetAlert from 'react-bootstrap-sweetalert'; +import axios from "../../../const/interceptorApi" +import { Card, CardBody, CardHeader, Col, Row, Input, Table } from 'reactstrap'; +import { SALES_CONTACT_EDIT, SALES_CONTACT_SEARCH, SALES_CONTACT_LIST, SALES_CONTACT_GET_ID, SALES_CONTACT_ADD, SALES_CONTACT_DELETE } from '../../../const/ApiConst'; +import { NotificationContainer, NotificationManager } from 'react-notifications'; +import { Spin, Button, Tooltip } from 'antd'; +import { useTranslation } from 'react-i18next'; + + +const ProjectType = ({ 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 [openDialog, setOpenDialog] = useState(false) + const [rowsPerPage, setRowsPerPage] = useState(10) + const [search, setSearch] = useState("") + const [totalPage, setTotalPage] = useState(0) + const [typeDialog, setTypeDialog] = useState('Save') + const [dataDemo, setDataDemo] = useState([]) + const [listCompany, setListCompany] = useState([]) + const [loading, setLoading] = useState(true); + const { t } = useTranslation() + const column = [ + { name: t('name') }, + { name: t('company') }, + { name: t('email') }, + { name: t('roles') }, + { name: t('phoneNumber') }, + { name: t('status') }, + { name: t('description') }, + ].filter(column => column && column.name); + useEffect(() => { + getDataContactSales(); + }, [currentPage, rowsPerPage, search]) + + useEffect(() => { + const cekData = dataExport || [] + if (cekData.length > 0) { + exportExcel() + } + }, [dataExport]) + + const getDataContactSales = 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': [] + } + + const result = await axios + .post(SALES_CONTACT_SEARCH, payload, HEADER) + .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 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': [] + } + + const result = await axios + .post(SALES_CONTACT_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 = {}; + dataRow["Nama"] = val.name; + dataRow["Email"] = val.email; + dataRow["Role"] = val.role; + dataRow["No Telepon"] = val.number_phone; + dataRow["Status"] = val.message; + dataRow["Message"] = val.message; + 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 handleSearch = e => { + const value = e.target.value + setSearch(value); + setCurrentPage(1) + }; + + const handleOpenDialog = (type) => { + setOpenDialog(true) + setTypeDialog(type) + } + + const handleEdit = (data) => { + setDataEdit(data) + handleOpenDialog('Edit'); + } + + const handleDelete = async (id) => { + await setAlertDelete(true) + await setIdDelete(id) + } + + const handleCloseDialog = (type, data) => { + if (type === "save") { + saveContactSales(data); + } else if (type === "edit") { + editContactSales(data); + } + setDataEdit([]) + setOpenDialog(false) + } + + const saveContactSales = async (data) => { + const formData = data + const result = await axios.post(SALES_CONTACT_ADD, formData, HEADER) + .then(res => res) + .catch((error) => error.response); + if (result && result.data && result.data.code === 200) { + getDataContactSales() + NotificationManager.success(`Data berhasil ditambahkan`, 'Success!!'); + } else { + NotificationManager.error(`Data gagal ditambahkan`, 'Failed!!'); + } + } + + const editContactSales = async (data) => { + let urlEdit = SALES_CONTACT_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) { + getDataContactSales(); + NotificationManager.success(`Data berhasil diubah`, 'Success!!'); + } else { + NotificationManager.error(`Data gagal diubah`, `Failed!!`); + } + } + + const toggleAddDialog = () => { + setOpenDialog(!openDialog) + } + + const onConfirmDelete = async () => { + let url = SALES_CONTACT_DELETE(idDelete); + const result = await axios.delete(url, HEADER) + .then(res => res) + .catch((error) => error.response); + + if (result && result.data && result.data.code === 200) { + getDataContactSales() + 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) + setIdDelete(0) + } + + const onShowSizeChange = (current, pageSize) => { + setRowsPerPage(pageSize) + } + + const onPagination = (current, pageSize) => { + setCurrentPage(current) + } + + const dataNotAvailable = () => { + if (dataTable.length === 0) { + return ( + + {t('noData')} + + ) + } + } + + return ( +
+ + + {t('deleteMsg')} + + toggleAddDialog} + typeDialog={typeDialog} + dataEdit={dataEdit} + clickOpenModal={clickOpenModal} + dataParent={allDataMenu} + /> + + +

{pageName}

+ + + + + + + + + + + + + +
+ + + + + + + {column.map((i, index) => { + return ( + + ) + })} + + + + {dataNotAvailable()} + {dataTable.map((n, index) => { + return ( + + + + + + + + + + + ) + })} + +
Aksi{i.name}
+ + handleDelete(n.id)}> + + + handleEdit(n)}> + + {n.name}{n.company_name}{n.email}{n.role}{n.number_phone}{n.status}{n.message}
+
+
+
+
+ ) +} + +export default ProjectType; From 3f361820162d248c108b19dabc2abdb75b51f500 Mon Sep 17 00:00:00 2001 From: farhantock Date: Wed, 28 Feb 2024 16:25:13 +0700 Subject: [PATCH 10/24] Merge branch 'master' of https://git.oslog.id/ibnu/generic-ospro-frontend into Dev-Farhan --- .../Master/MasterRoles/DialogMenuRoles.js | 77 ------------------- 1 file changed, 77 deletions(-) diff --git a/src/views/Master/MasterRoles/DialogMenuRoles.js b/src/views/Master/MasterRoles/DialogMenuRoles.js index 569847c..a00128e 100644 --- a/src/views/Master/MasterRoles/DialogMenuRoles.js +++ b/src/views/Master/MasterRoles/DialogMenuRoles.js @@ -4,10 +4,7 @@ import { Button, Form, FormGroup, Label, Input } from 'reactstrap'; import 'antd/dist/antd.css'; import axios from 'axios'; import { MENU_COMPANY_SEARCH } from '../../../const/ApiConst.js'; - const token = window.localStorage.getItem('token'); - - const config = { headers: { @@ -15,7 +12,6 @@ const config = { "Content-type": `application/json` } }; - export default class DialogMenuRoles extends Component { constructor(props) { super(props) @@ -43,7 +39,6 @@ export default class DialogMenuRoles extends Component { this.props.showDialog(this.showDialog); this.getAllMenu(); } - async componentDidUpdate() { if (this.state.isParentClick === true) { const { idRoles } = this.props @@ -55,12 +50,9 @@ export default class DialogMenuRoles extends Component { this.setState({ isParentClick: false, id: idRoles }); } } - - showDialog = () => { this.setState({ isParentClick: true }); } - getAllMenu = async () => { const payload = { "paging": { "start": 0, "length": -1 }, @@ -73,9 +65,6 @@ export default class DialogMenuRoles extends Component { ], "orders": { "columns": ["id"], "ascending": false } } - - - const result = await axios .post(MENU_COMPANY_SEARCH, payload, config) .then(res => res) @@ -86,11 +75,7 @@ export default class DialogMenuRoles extends Component { }); } else { } - } - - - setStateMenu = edit => { const stateMenu = []; this.state.menu.map((val) => { @@ -104,7 +89,6 @@ export default class DialogMenuRoles extends Component { }) }) } - setStateRead = edit => { const stateRead = []; this.state.menu.map((val) => { @@ -118,7 +102,6 @@ export default class DialogMenuRoles extends Component { }) }) } - setStateCreate = edit => { const stateCreate = []; this.state.menu.map((val) => { @@ -132,7 +115,6 @@ export default class DialogMenuRoles extends Component { }) }) } - setStateUpdate = edit => { const stateUpdate = []; this.state.menu.map((val) => { @@ -146,7 +128,6 @@ export default class DialogMenuRoles extends Component { }) }) } - setStateDelete = edit => { const stateDelete = []; this.state.menu.map((val) => { @@ -160,13 +141,10 @@ export default class DialogMenuRoles extends Component { }) }) } - checkMenuRoles = () => { let copyStateMenu = [...this.state.stateMenu]; - this.props.menuRoles.map((val, indexMenu) => { let index = this.getIndexDataMenu(val.menu_id); - if (index >= 0 || val.read === true || val.create === true || val.update === true || val.delete === true) { copyStateMenu[index] = true; } @@ -180,7 +158,6 @@ export default class DialogMenuRoles extends Component { } }) } - checkReadRoles = () => { let copyStateRead = [...this.state.stateRead]; this.props.menuRoles.map((val) => { @@ -191,7 +168,6 @@ export default class DialogMenuRoles extends Component { copyStateRead[index] = false; } }) - this.setState({ stateRead: [] }, () => { let check = copyStateRead.some(this.checkArray); if (check === false) { @@ -201,10 +177,8 @@ export default class DialogMenuRoles extends Component { } }) } - checkCreateRoles = () => { let copyStateCreate = [...this.state.stateCreate]; - this.props.menuRoles.map((val) => { let index = this.getIndexDataMenu(val.menu_id); if (val.create === true) { @@ -213,7 +187,6 @@ export default class DialogMenuRoles extends Component { copyStateCreate[index] = false; } }) - this.setState({ stateCreate: [] }, () => { let check = copyStateCreate.some(this.checkArray); if (check === false) { @@ -223,10 +196,8 @@ export default class DialogMenuRoles extends Component { } }) } - checkUpdateRoles = () => { let copyStateUpdate = [...this.state.stateUpdate]; - this.props.menuRoles.map((val) => { let index = this.getIndexDataMenu(val.menu_id); if (val.update === true) { @@ -235,7 +206,6 @@ export default class DialogMenuRoles extends Component { copyStateUpdate[index] = false; } }) - this.setState({ stateUpdate: [] }, () => { let check = copyStateUpdate.some(this.checkArray); if (check === false) { @@ -245,10 +215,8 @@ export default class DialogMenuRoles extends Component { } }) } - checkDeleteRoles = () => { let copyStateDelete = [...this.state.stateDelete]; - this.props.menuRoles.map((val) => { let index = this.getIndexDataMenu(val.menu_id); if (val.delete === true) { @@ -257,7 +225,6 @@ export default class DialogMenuRoles extends Component { copyStateDelete[index] = false; } }) - this.setState({ stateDelete: [] }, () => { let check = copyStateDelete.some(this.checkArray); if (check === false) { @@ -267,32 +234,26 @@ export default class DialogMenuRoles extends Component { } }) } - getIndexDataMenu = (id) => { let index = this.state.menu.findIndex(obj => obj.id === id); return index } - getIndexDataCreate = (id) => { let index = this.state.stateCreate.findIndex(obj => obj.id === id); return index } - getIndexDataDelete = (id) => { let index = this.state.stateDelete.findIndex(obj => obj.id === id); return index } - getIndexDataRead = (id) => { let index = this.state.stateRead.findIndex(obj => obj.id === id); return index } - getIndexDataUpdate = (id) => { let index = this.state.stateUpdate.findIndex(obj => obj.id === id); return index } - handleSave = () => { const { stateMenu, @@ -303,9 +264,7 @@ export default class DialogMenuRoles extends Component { menu, id } = this.state - const arrayData = []; - menu.map((val, index) => { let data = { roles_id: id, @@ -315,27 +274,20 @@ export default class DialogMenuRoles extends Component { update: stateUpdate[index], delete: stateDelete[index], checked: stateMenu[index], - } arrayData.push(data); - - }) this.props.closeDialog('save', arrayData); this.setState({ id: 0 }); } - handleCancel = () => { this.props.closeDialog('cancel', 'none') } - handleChangeCheckbox = (checked, index) => { let copyStateMenu = [...this.state.stateMenu]; copyStateMenu[index] = checked; - this.setState({ stateMenu: copyStateMenu }) } - handleChangeCheckboxRead = (checked, index, menuItem = null, menuIdxList = []) => { let copyStateRead = [...this.state.stateRead]; copyStateRead[index] = checked; @@ -358,10 +310,8 @@ export default class DialogMenuRoles extends Component { stateReadIdx.map((stateRead) => { copyStateRead[stateRead] = checked }) - this.setState({ stateRead: copyStateRead }) } - handleChangeCheckboxCreate = (checked, index, menuItem = null, menuIdxList = []) => { let copyStateCreate = [...this.state.stateCreate]; copyStateCreate[index] = checked; @@ -386,7 +336,6 @@ export default class DialogMenuRoles extends Component { }) this.setState({ stateCreate: copyStateCreate }) } - handleChangeCheckboxEdit = (checked, index, menuItem = null, menuIdxList = []) => { let copyStateEdit = [...this.state.stateUpdate]; copyStateEdit[index] = checked; @@ -411,7 +360,6 @@ export default class DialogMenuRoles extends Component { }) this.setState({ stateUpdate: copyStateEdit }) } - handleChangeCheckboxDelete = (checked, index, menuItem = null, menuIdxList = []) => { let copyStateDelete = [...this.state.stateDelete]; copyStateDelete[index] = checked; @@ -436,7 +384,6 @@ export default class DialogMenuRoles extends Component { }) this.setState({ stateDelete: copyStateDelete }) } - handleChangeCheckboxReadAll = (checked) => { let copyStateRead = [...this.state.stateRead]; copyStateRead.map((val, index) => { @@ -444,7 +391,6 @@ export default class DialogMenuRoles extends Component { }) this.setState({ stateRead: copyStateRead }) } - handleChangeCheckboxCreateAll = (checked, index) => { let copyStateCreate = [...this.state.stateCreate]; copyStateCreate.map((val, index) => { @@ -452,7 +398,6 @@ export default class DialogMenuRoles extends Component { }) this.setState({ stateCreate: copyStateCreate }) } - handleChangeCheckboxEditAll = (checked, index) => { let copyStateEdit = [...this.state.stateUpdate]; copyStateEdit.map((val, index) => { @@ -460,7 +405,6 @@ export default class DialogMenuRoles extends Component { }) this.setState({ stateUpdate: copyStateEdit }) } - handleChangeCheckboxDeleteAll = (checked, index) => { let copyStateDelete = [...this.state.stateDelete]; copyStateDelete.map((val, index) => { @@ -468,7 +412,6 @@ export default class DialogMenuRoles extends Component { }) this.setState({ stateDelete: copyStateDelete }) } - renderForm = () => { const { menu, stateRead, stateCreate, stateUpdate, stateDelete } = this.state; let menuIdxList = [] @@ -476,16 +419,13 @@ export default class DialogMenuRoles extends Component { const getChildren = (parentId) => { return menu.filter(item => item.parent_menu_id === parentId); }; - const renderMenu = (parentId, depth = 0) => { const children = getChildren(parentId); - return children.map((menuItem, index) => { const currentIndex = menuIdx; menuIdxList[currentIndex] = menuItem.menu_id; menuIdx++ const paddingLeft = depth * 30; - return ( @@ -509,10 +449,7 @@ export default class DialogMenuRoles extends Component { ); }; - - renderAll = () => { - return ( All @@ -523,45 +460,32 @@ export default class DialogMenuRoles extends Component { ) } - - - - checkArray = (val) => { return val === false; } - handleAllChecked = (checked) => { this.setState({ allChecked: !this.state.allChecked }); if (checked === true) { - let check = this.state.stateMenu.some(this.checkArray); - if (check) { const stateMenu = []; this.state.menu.map((val) => { stateMenu.push(true); }) - this.setState({ stateMenu: [] }, () => { this.setState({ stateMenu: stateMenu }); }) } - - } else { - const stateMenu = []; this.state.menu.map((val) => { stateMenu.push(false); }) - this.setState({ stateMenu: [] }, () => { this.setState({ stateMenu: stateMenu }); }) } } - render() { return ( @@ -584,7 +508,6 @@ export default class DialogMenuRoles extends Component { Delete - From 42e20239fb802dd185676618224916539ef6baa6 Mon Sep 17 00:00:00 2001 From: farhantock Date: Wed, 28 Feb 2024 16:34:56 +0700 Subject: [PATCH 11/24] resolve conflict --- .../Master/MasterRoles/DialogMenuRoles.js | 153 +++--------------- 1 file changed, 26 insertions(+), 127 deletions(-) diff --git a/src/views/Master/MasterRoles/DialogMenuRoles.js b/src/views/Master/MasterRoles/DialogMenuRoles.js index 348f614..a00128e 100644 --- a/src/views/Master/MasterRoles/DialogMenuRoles.js +++ b/src/views/Master/MasterRoles/DialogMenuRoles.js @@ -3,11 +3,8 @@ import { Modal, ModalHeader, ModalBody, ModalFooter, Row, Col, Table } from 'rea import { Button, Form, FormGroup, Label, Input } from 'reactstrap'; import 'antd/dist/antd.css'; import axios from 'axios'; -import { MENU_SEARCH } from '../../../const/ApiConst.js'; - +import { MENU_COMPANY_SEARCH } from '../../../const/ApiConst.js'; const token = window.localStorage.getItem('token'); - - const config = { headers: { @@ -15,7 +12,6 @@ const config = { "Content-type": `application/json` } }; - export default class DialogMenuRoles extends Component { constructor(props) { super(props) @@ -36,13 +32,13 @@ export default class DialogMenuRoles extends Component { stateUpdateAll: false, stateDeleteAll: false, allChecked: true, + company_id: props.company_id || null, } } async componentDidMount() { this.props.showDialog(this.showDialog); this.getAllMenu(); } - async componentDidUpdate() { if (this.state.isParentClick === true) { const { idRoles } = this.props @@ -54,43 +50,32 @@ export default class DialogMenuRoles extends Component { this.setState({ isParentClick: false, id: idRoles }); } } - - showDialog = () => { this.setState({ isParentClick: true }); } - getAllMenu = async () => { const payload = { "paging": { "start": 0, "length": -1 }, "columns": [ - { "name": "name", "logic_operator": "ilike", "value": "", "operator": "AND" } + { "name": "company_id", "logic_operator": "=", "value": this.state.company_id, "operator": "AND" } ], "joins": [ - { "name": "t_roles_menu", "column_join": "id", "column_results": ["create", "update", "delete", "read"] } + { "name": "t_roles_menu", "column_join": "menu_id", "column_results": ["create", "update", "delete", "read"] }, + { "name": "m_menu", "column_join": "menu_id", "column_results": ["name"] } ], "orders": { "columns": ["id"], "ascending": false } } - - - const result = await axios - .post(MENU_SEARCH, payload, config) + .post(MENU_COMPANY_SEARCH, payload, config) .then(res => res) .catch((error) => error.response); - - // console.log('78', result); if (result && result.data && result.data.code == 200) { this.setState({ menu: result.data.data }, () => { this.setStateMenu(false); }); } else { } - } - - - setStateMenu = edit => { const stateMenu = []; this.state.menu.map((val) => { @@ -104,7 +89,6 @@ export default class DialogMenuRoles extends Component { }) }) } - setStateRead = edit => { const stateRead = []; this.state.menu.map((val) => { @@ -118,7 +102,6 @@ export default class DialogMenuRoles extends Component { }) }) } - setStateCreate = edit => { const stateCreate = []; this.state.menu.map((val) => { @@ -132,7 +115,6 @@ export default class DialogMenuRoles extends Component { }) }) } - setStateUpdate = edit => { const stateUpdate = []; this.state.menu.map((val) => { @@ -146,7 +128,6 @@ export default class DialogMenuRoles extends Component { }) }) } - setStateDelete = edit => { const stateDelete = []; this.state.menu.map((val) => { @@ -160,13 +141,10 @@ export default class DialogMenuRoles extends Component { }) }) } - checkMenuRoles = () => { let copyStateMenu = [...this.state.stateMenu]; - this.props.menuRoles.map((val, indexMenu) => { let index = this.getIndexDataMenu(val.menu_id); - if (index >= 0 || val.read === true || val.create === true || val.update === true || val.delete === true) { copyStateMenu[index] = true; } @@ -180,7 +158,6 @@ export default class DialogMenuRoles extends Component { } }) } - checkReadRoles = () => { let copyStateRead = [...this.state.stateRead]; this.props.menuRoles.map((val) => { @@ -191,7 +168,6 @@ export default class DialogMenuRoles extends Component { copyStateRead[index] = false; } }) - this.setState({ stateRead: [] }, () => { let check = copyStateRead.some(this.checkArray); if (check === false) { @@ -201,10 +177,8 @@ export default class DialogMenuRoles extends Component { } }) } - checkCreateRoles = () => { let copyStateCreate = [...this.state.stateCreate]; - this.props.menuRoles.map((val) => { let index = this.getIndexDataMenu(val.menu_id); if (val.create === true) { @@ -213,7 +187,6 @@ export default class DialogMenuRoles extends Component { copyStateCreate[index] = false; } }) - this.setState({ stateCreate: [] }, () => { let check = copyStateCreate.some(this.checkArray); if (check === false) { @@ -223,10 +196,8 @@ export default class DialogMenuRoles extends Component { } }) } - checkUpdateRoles = () => { let copyStateUpdate = [...this.state.stateUpdate]; - this.props.menuRoles.map((val) => { let index = this.getIndexDataMenu(val.menu_id); if (val.update === true) { @@ -235,7 +206,6 @@ export default class DialogMenuRoles extends Component { copyStateUpdate[index] = false; } }) - this.setState({ stateUpdate: [] }, () => { let check = copyStateUpdate.some(this.checkArray); if (check === false) { @@ -245,10 +215,8 @@ export default class DialogMenuRoles extends Component { } }) } - checkDeleteRoles = () => { let copyStateDelete = [...this.state.stateDelete]; - this.props.menuRoles.map((val) => { let index = this.getIndexDataMenu(val.menu_id); if (val.delete === true) { @@ -257,7 +225,6 @@ export default class DialogMenuRoles extends Component { copyStateDelete[index] = false; } }) - this.setState({ stateDelete: [] }, () => { let check = copyStateDelete.some(this.checkArray); if (check === false) { @@ -267,32 +234,26 @@ export default class DialogMenuRoles extends Component { } }) } - getIndexDataMenu = (id) => { let index = this.state.menu.findIndex(obj => obj.id === id); return index } - getIndexDataCreate = (id) => { let index = this.state.stateCreate.findIndex(obj => obj.id === id); return index } - getIndexDataDelete = (id) => { let index = this.state.stateDelete.findIndex(obj => obj.id === id); return index } - getIndexDataRead = (id) => { let index = this.state.stateRead.findIndex(obj => obj.id === id); return index } - getIndexDataUpdate = (id) => { let index = this.state.stateUpdate.findIndex(obj => obj.id === id); return index } - handleSave = () => { const { stateMenu, @@ -303,9 +264,7 @@ export default class DialogMenuRoles extends Component { menu, id } = this.state - const arrayData = []; - menu.map((val, index) => { let data = { roles_id: id, @@ -315,35 +274,27 @@ export default class DialogMenuRoles extends Component { update: stateUpdate[index], delete: stateDelete[index], checked: stateMenu[index], - } arrayData.push(data); - - }) - console.log('arrayData', arrayData); - // this.props.closeDialog('save', arrayData); + this.props.closeDialog('save', arrayData); this.setState({ id: 0 }); } - handleCancel = () => { this.props.closeDialog('cancel', 'none') } - handleChangeCheckbox = (checked, index) => { let copyStateMenu = [...this.state.stateMenu]; copyStateMenu[index] = checked; - this.setState({ stateMenu: copyStateMenu }) } - handleChangeCheckboxRead = (checked, index, menuItem = null, menuIdxList = []) => { let copyStateRead = [...this.state.stateRead]; copyStateRead[index] = checked; let menu = this.state.menu; let checkMenuParent = menu.map((state, index) => { - if (state.parent_id === menuItem.id) { - return state.id + if (state.parent_menu_id === menuItem.menu_id) { + return state.menu_id } else { return null } @@ -359,17 +310,15 @@ export default class DialogMenuRoles extends Component { stateReadIdx.map((stateRead) => { copyStateRead[stateRead] = checked }) - this.setState({ stateRead: copyStateRead }) } - handleChangeCheckboxCreate = (checked, index, menuItem = null, menuIdxList = []) => { let copyStateCreate = [...this.state.stateCreate]; copyStateCreate[index] = checked; let menu = this.state.menu; let checkMenuParent = menu.map((state, index) => { - if (state.parent_id === menuItem.id) { - return state.id + if (state.parent_menu_id === menuItem.menu_id) { + return state.menu_id } else { return null } @@ -387,14 +336,13 @@ export default class DialogMenuRoles extends Component { }) this.setState({ stateCreate: copyStateCreate }) } - handleChangeCheckboxEdit = (checked, index, menuItem = null, menuIdxList = []) => { let copyStateEdit = [...this.state.stateUpdate]; copyStateEdit[index] = checked; let menu = this.state.menu; let checkMenuParent = menu.map((state, index) => { - if (state.parent_id === menuItem.id) { - return state.id + if (state.parent_menu_id === menuItem.menu_id) { + return state.menu_id } else { return null } @@ -412,14 +360,13 @@ export default class DialogMenuRoles extends Component { }) this.setState({ stateUpdate: copyStateEdit }) } - handleChangeCheckboxDelete = (checked, index, menuItem = null, menuIdxList = []) => { let copyStateDelete = [...this.state.stateDelete]; copyStateDelete[index] = checked; let menu = this.state.menu; let checkMenuParent = menu.map((state, index) => { - if (state.parent_id === menuItem.id) { - return state.id + if (state.parent_menu_id === menuItem.menu_id) { + return state.menu_id } else { return null } @@ -437,7 +384,6 @@ export default class DialogMenuRoles extends Component { }) this.setState({ stateDelete: copyStateDelete }) } - handleChangeCheckboxReadAll = (checked) => { let copyStateRead = [...this.state.stateRead]; copyStateRead.map((val, index) => { @@ -445,7 +391,6 @@ export default class DialogMenuRoles extends Component { }) this.setState({ stateRead: copyStateRead }) } - handleChangeCheckboxCreateAll = (checked, index) => { let copyStateCreate = [...this.state.stateCreate]; copyStateCreate.map((val, index) => { @@ -453,7 +398,6 @@ export default class DialogMenuRoles extends Component { }) this.setState({ stateCreate: copyStateCreate }) } - handleChangeCheckboxEditAll = (checked, index) => { let copyStateEdit = [...this.state.stateUpdate]; copyStateEdit.map((val, index) => { @@ -461,7 +405,6 @@ export default class DialogMenuRoles extends Component { }) this.setState({ stateUpdate: copyStateEdit }) } - handleChangeCheckboxDeleteAll = (checked, index) => { let copyStateDelete = [...this.state.stateDelete]; copyStateDelete.map((val, index) => { @@ -469,74 +412,44 @@ export default class DialogMenuRoles extends Component { }) this.setState({ stateDelete: copyStateDelete }) } - - // renderForm = () => { - // const { menu, stateRead, stateCreate, stateUpdate, stateDelete } = this.state - // console.log('menu', menu); - // return ( - // menu.map((val, index) => { - - // return ( - // - // {val.name} - // this.handleChangeCheckboxRead(e.target.checked, index)} defaultChecked={stateRead[index]} /> - // this.handleChangeCheckboxCreate(e.target.checked, index)} defaultChecked={stateCreate[index]} /> - // this.handleChangeCheckboxEdit(e.target.checked, index)} defaultChecked={stateUpdate[index]} /> - // this.handleChangeCheckboxDelete(e.target.checked, index)} defaultChecked={stateDelete[index]} /> - // - // ) - // }) - // ) - - // } - renderForm = () => { const { menu, stateRead, stateCreate, stateUpdate, stateDelete } = this.state; let menuIdxList = [] let menuIdx = 0 const getChildren = (parentId) => { - return menu.filter(item => item.parent_id === parentId); + return menu.filter(item => item.parent_menu_id === parentId); }; - - // Function to render menu items const renderMenu = (parentId, depth = 0) => { const children = getChildren(parentId); - return children.map((menuItem, index) => { - const currentIndex = menuIdx; // Capture current index - menuIdxList[currentIndex] = menuItem.id; + const currentIndex = menuIdx; + menuIdxList[currentIndex] = menuItem.menu_id; menuIdx++ - const paddingLeft = depth * 20; - const fontWeight = menuItem.parent_id === null ? 'bold' : 'normal'; - + const paddingLeft = depth * 30; return ( - + - {menuItem.name} + {menuItem.join_second_name} this.handleChangeCheckboxRead(e.target.checked, currentIndex, menuItem, menuIdxList)} defaultChecked={stateRead[currentIndex]} /> this.handleChangeCheckboxCreate(e.target.checked, currentIndex, menuItem, menuIdxList)} defaultChecked={stateCreate[currentIndex]} /> this.handleChangeCheckboxEdit(e.target.checked, currentIndex, menuItem, menuIdxList)} defaultChecked={stateUpdate[currentIndex]} /> this.handleChangeCheckboxDelete(e.target.checked, currentIndex, menuItem, menuIdxList)} defaultChecked={stateDelete[currentIndex]} /> - {renderMenu(menuItem.id, depth + 1)} {/* Recursively render children */} + {renderMenu(menuItem.menu_id, depth + 1)} ); }); }; menuIdxList = [] menuIdx = 0 - // Render top-level menu items (parents) return ( - {this.renderAll()} {/* Render the "All" row */} - {renderMenu(null)} {/* Render the menu items */} + {this.renderAll()} + {renderMenu(null)} ); }; - - renderAll = () => { - return ( All @@ -547,45 +460,32 @@ export default class DialogMenuRoles extends Component { ) } - - - - checkArray = (val) => { return val === false; } - handleAllChecked = (checked) => { this.setState({ allChecked: !this.state.allChecked }); if (checked === true) { - let check = this.state.stateMenu.some(this.checkArray); - if (check) { const stateMenu = []; this.state.menu.map((val) => { stateMenu.push(true); }) - this.setState({ stateMenu: [] }, () => { this.setState({ stateMenu: stateMenu }); }) } - - } else { - const stateMenu = []; this.state.menu.map((val) => { stateMenu.push(false); }) - this.setState({ stateMenu: [] }, () => { this.setState({ stateMenu: stateMenu }); }) } } - render() { return ( @@ -603,12 +503,11 @@ export default class DialogMenuRoles extends Component { Create - Edite + Edit Delete - @@ -624,4 +523,4 @@ export default class DialogMenuRoles extends Component { ) } -} +} \ No newline at end of file From c41dc74a64bc52d2604b911da66d49c245e77f12 Mon Sep 17 00:00:00 2001 From: wahyu Date: Mon, 4 Mar 2024 13:30:34 +0700 Subject: [PATCH 12/24] dynamic dashboard 1st iteration --- src/routes.js | 2 + src/views/Dashboard/DashboardDyna.js | 261 +++++++++++++++++++++++++++ 2 files changed, 263 insertions(+) create mode 100644 src/views/Dashboard/DashboardDyna.js diff --git a/src/routes.js b/src/routes.js index 1ede7b1..267fa07 100644 --- a/src/routes.js +++ b/src/routes.js @@ -50,6 +50,7 @@ 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 DashboardDyna = React.lazy(() => import('./views/Dashboard/DashboardDyna')); const DashboardDND = React.lazy(() => import('./components/wj/App')) const DashboardCustomer = React.lazy(() => import('./views/Dashboard/DashboardCustomer')); const DashboardProject = React.lazy(() => import('./views/Dashboard/DashboardProject')); @@ -61,6 +62,7 @@ const DemoManagement = React.lazy(() => import('./views/SimproV2/Demo')) const routes = [ { path: '/', exact: true, name: 'Home' }, { path: '/dashboard', name: 'DashboardBOD', component: DashboardBOD }, + { path: '/dashboard-dyna', name: 'DashboardBOD', component: DashboardDyna }, { path: '/dashboard-dnd', name: 'DashboardBOD', component: DashboardDND }, { path: '/dashboard-customer/:PROJECT_ID/:GANTT_ID/:SCURVE', name: 'DashboardCustomer', component: DashboardCustomer }, { path: '/dashboard-project/:PROJECT_ID/:GANTT_ID/:Header', exact: true, name: 'Dashboard Project', component: DashboardProject }, diff --git a/src/views/Dashboard/DashboardDyna.js b/src/views/Dashboard/DashboardDyna.js new file mode 100644 index 0000000..113729c --- /dev/null +++ b/src/views/Dashboard/DashboardDyna.js @@ -0,0 +1,261 @@ +import React, { useState } from 'react'; +import { Row, Col, Button } from 'antd'; +import { RightOutlined, LeftOutlined } from '@ant-design/icons'; +import { Widgets } from 'react-awesome-query-builder'; + +const DashboardDyna = () => { + const [templates, setTemplates] = useState([]); + const [widgets, setWidgets] = useState([]); + const [templateColSpan, setTemplateColSpan] = useState(5); + const [widgetColSpan, setWidgetColSpan] = useState(5); + const [dashboardColSpan, setDashboardColSpan] = useState(14); + + const addWidget = (widget) => { + const lastIndex = templates.length - 1; + if (lastIndex >= 0) { + let id = templates[lastIndex].id; + let updatedTemplate = checkTemplate(id, [...widgets, widget]); + let updatedTemplates = [...templates]; + updatedTemplates[lastIndex] = updatedTemplate; + setTemplates(updatedTemplates); + } + setWidgets([...widgets, widget]); + }; + + + const addTemplate = (template) => { + const lastIndex = templates.length - 1; + if (templates.length == 0 || templates[lastIndex].widgetCount <= widgets.length) { + setTemplates([...templates, checkTemplate(template)]); + setWidgets([]); + } else { + alert("Please select a template first") + } + }; + + const clearTemplates = () => { + setTemplates([]); + clearWidgets() + }; + + const clearWidgets = () => { + setWidgets([]); + }; + + const checkTemplate = (template, widgets = null) => { + let component, widgetCount, id = template; + switch (template) { + case 0: + component = ( + + +
+ {widgets ? widgets[0] : null} +
+ +
+ ); + widgetCount = 1; + break; + case 1: + component = ( + + +
+ {widgets ? widgets[0] : null} +
+ +
+ {widgets ? widgets[1] : null} +
+ +
+ {widgets ? widgets[2] : null} +
+ +
+ {widgets ? widgets[3] : null} +
+
+ ); + widgetCount = 4; + break; + case 2: + component = ( + + +
+ {widgets ? widgets[0] : null} +
+ +
+ {widgets ? widgets[1] : null} +
+
+ ); + widgetCount = 2; + break; + case 3: + component = ( + + +
+ {widgets ? widgets[0] : null} +
+ +
+ {widgets ? widgets[1] : null} +
+
+ ); + widgetCount = 2; + break; + default: + break; + } + return { component, widgetCount, id }; + }; + + const toggleTemplateColumn = () => { + if (templateColSpan === 1) { + setTemplateColSpan(5); + setDashboardColSpan(dashboardColSpan - 4); + } else { + setTemplateColSpan(1); + setDashboardColSpan(dashboardColSpan + 4); + } + }; + + const toggleWidgetColumn = () => { + if (widgetColSpan === 1) { + setWidgetColSpan(5); + setDashboardColSpan(dashboardColSpan - 4); + } else { + setWidgetColSpan(1); + setDashboardColSpan(dashboardColSpan + 4); + } + }; + + return ( +
+ + +
+
+
+ {templateColSpan === 1 ? + + : + <> +
Template
+ + addTemplate(0)} style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 10 }}> + +
+ + + addTemplate(1)} style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 10 }}> + +
+ + +
+ + +
+ + +
+ + + addTemplate(2)} style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 10 }}> + +
+ + +
+ + + addTemplate(3)} style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 10 }}> + +
+ + +
+ + + + + } +
+
+
+ + +
+
+
+ {widgetColSpan === 1 ? + + : + <> +
Widget
+ + addWidget(0)} style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 10 }}> + +
+ + +
Pie Chart Project By Role
+ + + addWidget(1)} style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 10 }}> + +
+ + +
Pie Chart Project By Role
+ + + addWidget(2)} style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 10 }}> + +
+ + +
Pie Chart Project By Role
+ + + addWidget(3)} style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 10 }}> + +
+ + +
Pie Chart Project By Role
+ + + + + } +
+
+
+ + +
+
+
+
Dashboard
+ {templates.length > 0 ? templates.map((template) => { + return template.component; + }) : null} + +
+
+
+ +
+
+ ); +}; + +export default DashboardDyna; From 8b6759640336cf3a792e457824733dfc88651295 Mon Sep 17 00:00:00 2001 From: wahyu Date: Tue, 5 Mar 2024 10:23:28 +0700 Subject: [PATCH 13/24] update dynamic dashboard --- src/views/Dashboard/DashboardDyna.js | 195 ++++++++++++++++----------- 1 file changed, 115 insertions(+), 80 deletions(-) diff --git a/src/views/Dashboard/DashboardDyna.js b/src/views/Dashboard/DashboardDyna.js index 113729c..2443392 100644 --- a/src/views/Dashboard/DashboardDyna.js +++ b/src/views/Dashboard/DashboardDyna.js @@ -1,5 +1,5 @@ import React, { useState } from 'react'; -import { Row, Col, Button } from 'antd'; +import { Row, Col, Button, Modal } from 'antd'; import { RightOutlined, LeftOutlined } from '@ant-design/icons'; import { Widgets } from 'react-awesome-query-builder'; @@ -8,29 +8,46 @@ const DashboardDyna = () => { const [widgets, setWidgets] = useState([]); const [templateColSpan, setTemplateColSpan] = useState(5); const [widgetColSpan, setWidgetColSpan] = useState(5); - const [dashboardColSpan, setDashboardColSpan] = useState(14); + const [dashboardColSpan, setDashboardColSpan] = useState(19); + const [isModalOpen, setIsModalOpen] = useState(false); + const [templateIndex, setTemplateIndex] = useState(0); + const [columnIndex, setColumnIndex] = useState(0); const addWidget = (widget) => { + // Make a copy of the current widgets state + const newWidgets = [...widgets]; + if (!newWidgets[templateIndex]) { + newWidgets[templateIndex] = [] + } + + // Make sure the templateIndex and columnIndex are within bounds + // if (templateIndex <= newWidgets.length && columnIndex <= newWidgets[templateIndex].length) { + // Make a copy of the nested array at templateIndex + const nestedArray = [...newWidgets[templateIndex]]; + + // Update the value at columnIndex + nestedArray[columnIndex] = widget; + + // Update the nested array in the newWidgets array + newWidgets[templateIndex] = nestedArray; + + // Update the state with the newWidgets array + setWidgets(newWidgets); + // } const lastIndex = templates.length - 1; if (lastIndex >= 0) { - let id = templates[lastIndex].id; - let updatedTemplate = checkTemplate(id, [...widgets, widget]); + let id = templates[templateIndex].id; + let updatedTemplate = checkTemplate(id, newWidgets, true); let updatedTemplates = [...templates]; - updatedTemplates[lastIndex] = updatedTemplate; + updatedTemplates[templateIndex] = updatedTemplate; setTemplates(updatedTemplates); } - setWidgets([...widgets, widget]); }; const addTemplate = (template) => { const lastIndex = templates.length - 1; - if (templates.length == 0 || templates[lastIndex].widgetCount <= widgets.length) { - setTemplates([...templates, checkTemplate(template)]); - setWidgets([]); - } else { - alert("Please select a template first") - } + setTemplates([...templates, checkTemplate(template)]); }; const clearTemplates = () => { @@ -42,15 +59,33 @@ const DashboardDyna = () => { setWidgets([]); }; - const checkTemplate = (template, widgets = null) => { + const showModal = (template = 0, column = 0) => { + setTemplateIndex(template); + setColumnIndex(column); + setIsModalOpen(true); + }; + + const handleOk = () => { + setIsModalOpen(false); + }; + + const handleCancel = () => { + setIsModalOpen(false); + }; + + const checkTemplate = (template, widgets = null, add_widget = false) => { let component, widgetCount, id = template; + let templateIndex = templates.length; + if (add_widget) { + templateIndex-- + } switch (template) { case 0: component = ( - + showModal(templateIndex, 0)}>
- {widgets ? widgets[0] : null} + {widgets ? widgets[templateIndex][0] : null}
@@ -60,21 +95,21 @@ const DashboardDyna = () => { case 1: component = ( - + showModal(templateIndex, 0)}>
- {widgets ? widgets[0] : null} + {widgets ? widgets[templateIndex][0] : null}
- + showModal(templateIndex, 1)}>
- {widgets ? widgets[1] : null} + {widgets ? widgets[templateIndex][1] : null}
- + showModal(templateIndex, 2)}>
- {widgets ? widgets[2] : null} + {widgets ? widgets[templateIndex][2] : null}
- + showModal(templateIndex, 3)}>
- {widgets ? widgets[3] : null} + {widgets ? widgets[templateIndex][3] : null}
); @@ -83,13 +118,13 @@ const DashboardDyna = () => { case 2: component = ( - + showModal(templateIndex, 0)}>
- {widgets ? widgets[0] : null} + {widgets ? widgets[templateIndex][0] : null}
- + showModal(templateIndex, 1)}>
- {widgets ? widgets[1] : null} + {widgets ? widgets[templateIndex][1] : null}
); @@ -98,13 +133,13 @@ const DashboardDyna = () => { case 3: component = ( - + showModal(templateIndex, 0)}>
- {widgets ? widgets[0] : null} + {widgets ? widgets[templateIndex][0] : null}
- + showModal(templateIndex, 1)}>
- {widgets ? widgets[1] : null} + {widgets ? widgets[templateIndex][1] : null}
); @@ -191,55 +226,6 @@ const DashboardDyna = () => {
- -
-
-
- {widgetColSpan === 1 ? - - : - <> -
Widget
- - addWidget(0)} style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 10 }}> - -
- - -
Pie Chart Project By Role
- - - addWidget(1)} style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 10 }}> - -
- - -
Pie Chart Project By Role
- - - addWidget(2)} style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 10 }}> - -
- - -
Pie Chart Project By Role
- - - addWidget(3)} style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 10 }}> - -
- - -
Pie Chart Project By Role
- - - - - } -
-
-
-
@@ -254,7 +240,56 @@ const DashboardDyna = () => {
+ +
+
+
+ {widgetColSpan === 1 ? + + : + <> +
Widget
+ + addWidget(0)} style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 10 }}> + +
+ + +
Pie Chart Project By Role
+ + + addWidget(1)} style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 10 }}> + +
+ + +
Pie Chart Project By Role
+ + + addWidget(2)} style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 10 }}> + +
+ + +
Pie Chart Project By Role
+ + + addWidget(3)} style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 10 }}> + +
+ + +
Pie Chart Project By Role
+ + + + } +
+
+
+
+ ); }; From 5d2e9f052f929f01d9cd4363a08b86954ed94854 Mon Sep 17 00:00:00 2001 From: khaidralirahman Date: Tue, 5 Mar 2024 14:45:38 +0700 Subject: [PATCH 14/24] customfunc --- src/const/CustomFunc.js | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/const/CustomFunc.js b/src/const/CustomFunc.js index 808b6d4..bf29083 100644 --- a/src/const/CustomFunc.js +++ b/src/const/CustomFunc.js @@ -164,6 +164,46 @@ export const QUERY_BUILDER_FIELD_SALES = }, } +export const checkActMenup = (menuPath, actProp) => { + let foundObj; + let entireObj = JSON.parse(localStorage.getItem("menu_login")); + JSON.stringify(entireObj, (_, nestedValue) => { + if (nestedValue && nestedValue.url === menuPath) { + foundObj = nestedValue; + } + return nestedValue; + }); + + let output = false; + let actValue = actProp.trim().toLowerCase() + switch (actValue) { + case "create": + if (foundObj[actValue] === true) { + output = true; + } else { + output = false; + + } + case "read": + if (foundObj[actValue] === true) { + output = true; + } + case "update": + if (foundObj[actValue] === true) { + output = true; + } + case "delete": + if (foundObj[actValue] === true) { + output = true; + } + // default: + // output = false + } + + return output; + // return foundObj[actProp]; +}; + export const QUERY_BUILDER_FIELD_CUSTOMER = { "properties->name": { From 1ad73d642add306bf44825da0cbe1acb7644094c Mon Sep 17 00:00:00 2001 From: khaidralirahman Date: Tue, 5 Mar 2024 14:46:28 +0700 Subject: [PATCH 15/24] roles --- src/views/Master/MasterRoles/index.js | 33 +++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/src/views/Master/MasterRoles/index.js b/src/views/Master/MasterRoles/index.js index 3ff0663..3d1137d 100644 --- a/src/views/Master/MasterRoles/index.js +++ b/src/views/Master/MasterRoles/index.js @@ -10,6 +10,7 @@ import { NotificationContainer, NotificationManager } from 'react-notifications' import { Pagination, Tooltip, Table } from 'antd'; import { ROLE_ADD, ROLE_SEARCH, ROLE_EDIT, ROLE_DELETE, ROLEMENU_ADD, ROLEMENU_SEARCH, ROLEMENU_DELETE_ROLE } from '../../../const/ApiConst.js'; import { withTranslation } from 'react-i18next'; +import { checkActMenup } from '../../../const/CustomFunc'; const LENGTH_DATA = 10 @@ -66,15 +67,30 @@ class index extends Component { className: 'nowrap', render: (text, record) => <> - this.handleMenuRoles(text.id)}> + { + checkActMenup('/roles', 'update') ? + this.handleMenuRoles(text.id)}> + : + null + } - this.handleDelete(text.id)}> + { + checkActMenup('/roles', 'delete') ? + this.handleDelete(text.id)}> + : + null + } - this.handleEdit(text)}> + { + checkActMenup('/roles', 'update') ? + this.handleEdit(text)}> + : + null + } , }, @@ -503,10 +519,17 @@ class index extends Component { - + { + checkActMenup('/roles', 'create') ? + + + : + null + } - +
From 1e3560135cc676680f0ab131d5e925ff2ef81fff Mon Sep 17 00:00:00 2001 From: khaidralirahman Date: Tue, 5 Mar 2024 14:46:56 +0700 Subject: [PATCH 16/24] menu --- src/views/Master/MenuCompany/index.js | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/views/Master/MenuCompany/index.js b/src/views/Master/MenuCompany/index.js index e3eb03b..e3e71a7 100644 --- a/src/views/Master/MenuCompany/index.js +++ b/src/views/Master/MenuCompany/index.js @@ -10,6 +10,8 @@ import { MENU_ADD, MENU_SEARCH, MENU_EDIT, MENU_DELETE, MENU_LIST, MENU_COMPANY_ import { NotificationContainer, NotificationManager } from 'react-notifications'; import { Pagination, Tooltip, Table } from 'antd'; import { useTranslation } from 'react-i18next'; +import { checkActMenup } from '../../../const/CustomFunc.js'; +import { useLocation } from "react-router-dom"; const token = window.localStorage.getItem('token'); const column = [ { name: "Nama" }, @@ -34,6 +36,7 @@ const Index = ({ params, ...props }) => { hierarchy = props.hierarchy; user_name = props.user_name; } + const location = useLocation() const [alertDelete, setAlertDelete] = useState(false) const [allDataMenu, setAllDataMenu] = useState([]) const [clickOpenModal, setClickOpenModal] = useState(false) @@ -340,10 +343,20 @@ const Index = ({ params, ...props }) => { key: 'x', render: (text, record) => <> - handleDelete(text.id)}> + { + checkActMenup(location.pathname, 'delete') ? + handleDelete(text.id)}> + : + null + } - handleEdit(text)}> + { + checkActMenup(location.pathname, 'update') ? + handleEdit(text)}> + : + null + } , }, From fe462877f049afc3ac3d1fc44d00c78aecc94395 Mon Sep 17 00:00:00 2001 From: khaidralirahman Date: Tue, 5 Mar 2024 14:47:16 +0700 Subject: [PATCH 17/24] project phase --- src/views/Master/ProjectPhase/index.js | 28 ++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/views/Master/ProjectPhase/index.js b/src/views/Master/ProjectPhase/index.js index 9effc9b..708e91a 100644 --- a/src/views/Master/ProjectPhase/index.js +++ b/src/views/Master/ProjectPhase/index.js @@ -9,6 +9,14 @@ 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'; +import { + formatNumber, + formatRupiah, + formatThousand, + renderFormatRupiah, + checkActMenup, +} from "../../../const/CustomFunc"; +import { useLocation } from "react-router-dom"; const ProjectPhase = ({ params, ...props }) => { @@ -26,6 +34,7 @@ const ProjectPhase = ({ params, ...props }) => { user_name = props.user_name; } + const location = useLocation(); const HEADER = { headers: { "Content-Type": "application/json", @@ -350,10 +359,20 @@ const ProjectPhase = ({ params, ...props }) => { className: 'nowrap', render: (text, record) => <> - handleDelete(text.id)}> + { + checkActMenup(location.pathname, 'delete') ? + handleDelete(text.id)}> + : + null + } - handleEdit(text)}> + { + checkActMenup(location.pathname, 'update') ? + handleEdit(text)}> + : + null + } {" "} , }, @@ -429,7 +448,12 @@ const ProjectPhase = ({ params, ...props }) => { + { + checkActMenup(location.pathname, 'create') ? + : + null + } From 08428e7d696a7a5b08ca234755afbe87f3428786 Mon Sep 17 00:00:00 2001 From: khaidralirahman Date: Tue, 5 Mar 2024 14:47:36 +0700 Subject: [PATCH 18/24] project role --- src/views/Master/RoleProject/index.js | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/views/Master/RoleProject/index.js b/src/views/Master/RoleProject/index.js index d5b095b..8ca9848 100644 --- a/src/views/Master/RoleProject/index.js +++ b/src/views/Master/RoleProject/index.js @@ -9,6 +9,7 @@ import { NotificationContainer, NotificationManager } from 'react-notifications' import { PROJECT_ROLE_ADD, PROJECT_ROLE_SEARCH, PROJECT_ROLE_EDIT, PROJECT_ROLE_DELETE } from '../../../const/ApiConst.js'; import { Pagination, Tooltip, Table } from 'antd'; import { withTranslation } from 'react-i18next'; +import { checkActMenup } from '../../../const/CustomFunc'; const LENGTH_DATA = 10 class index extends Component { constructor(props) { @@ -63,10 +64,20 @@ class index extends Component { className: 'nowrap', render: (text, record) => <> - this.handleDelete(text.id)}> + { + checkActMenup('/project-role', 'delete') ? + this.handleDelete(text.id)}> + : + null + } - this.handleEdit(text)}> + { + checkActMenup('/project-role', 'update') ? + this.handleEdit(text)}> + : + null + } , }, @@ -403,7 +414,13 @@ class index extends Component { - + { + checkActMenup('/roles', 'create') ? + + : + null + } From c1f8a08d6d9021f0d4765543bbc9d859e80ace1e Mon Sep 17 00:00:00 2001 From: khaidralirahman Date: Tue, 5 Mar 2024 14:47:54 +0700 Subject: [PATCH 19/24] checklist 3 --- src/views/SimproV2/ChecklistK3/index.js | 30 ++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/src/views/SimproV2/ChecklistK3/index.js b/src/views/SimproV2/ChecklistK3/index.js index e0632b6..758f833 100644 --- a/src/views/SimproV2/ChecklistK3/index.js +++ b/src/views/SimproV2/ChecklistK3/index.js @@ -10,6 +10,14 @@ import { CHECKLIST_K3_ADD, CHECKLIST_K3_EDIT, CHECKLIST_K3_DELETE, CHECKLIST_K3_SEARCH, COMPANY_MANAGEMENT_LIST } from '../../../const/ApiConst'; import { useTranslation } from 'react-i18next'; +import { useLocation } from "react-router-dom"; +import { + formatNumber, + formatRupiah, + formatThousand, + renderFormatRupiah, + checkActMenup, +} from "../../../const/CustomFunc"; const token = window.localStorage.getItem('token'); const config = { @@ -34,6 +42,7 @@ const ChecklistK3 = ({ params, ...props }) => { hierarchy = props.hierarchy; user_name = props.user_name; } + const location = useLocation(); const HEADER = { headers: { "Content-Type": "application/json", @@ -363,7 +372,12 @@ const ChecklistK3 = ({ params, ...props }) => { - + { + checkActMenup(location.pathname, 'create') ? + + : + null + } @@ -390,10 +404,20 @@ const ChecklistK3 = ({ params, ...props }) => { - handleDelete(n.id)}> + { + checkActMenup(location.pathname, 'delete') ? + handleDelete(n.id)}> + : + null + } - handleEdit(n)}> + { + checkActMenup(location.pathname, 'edit') ? + handleEdit(n)}> + : + null + } {role_name === 'Super Admin' && From 9120cc187bfd2d13242ff7b0432b12a964368fc9 Mon Sep 17 00:00:00 2001 From: khaidralirahman Date: Tue, 5 Mar 2024 14:48:16 +0700 Subject: [PATCH 20/24] project information --- src/views/SimproV2/CreatedProyek/index.js | 41 +++++++++++++++++------ 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/src/views/SimproV2/CreatedProyek/index.js b/src/views/SimproV2/CreatedProyek/index.js index 8a25b0e..d9c86b4 100644 --- a/src/views/SimproV2/CreatedProyek/index.js +++ b/src/views/SimproV2/CreatedProyek/index.js @@ -61,9 +61,6 @@ import { IMAGE_GET_BY_ID, IMAGE_DELETE, } from "../../../const/ApiConst"; -import { - formatThousand -} from "../../../const/CustomFunc"; import moment from "moment"; // import DialogFormResource from './DialogFormResource'; import DialogFormMaterial from "./DataRequestMaterial"; @@ -72,6 +69,13 @@ import DialogDocument from "./DialogDocument"; import DialogInitDocument from "./DialogInitDocument"; import DialogGantt from "./DialogGantt"; import DialogHierarchy from "./DialogHierarchy"; +import { + formatNumber, + formatRupiah, + formatThousand, + renderFormatRupiah, + checkActMenup, +} from "../../../const/CustomFunc"; // import DialogAsignHr from './AsignHrProject'; import AssignHrProject from "./AsignHrProject"; import AssignCustProject from "./AsignCustProject"; @@ -80,7 +84,7 @@ import ViewProject from "./ViewProject"; import ReportAnalysis from "./ReportAnalysis"; import { Icon } from "@iconify/react"; // import SubProyekComp from './SubProyekComp'; -import { Link, useHistory, withRouter } from "react-router-dom"; +import { Link, useHistory, withRouter, useLocation } from "react-router-dom"; import { t } from "i18next"; const url = ""; @@ -100,6 +104,7 @@ const CreatedProyek = ({ params, ...props }) => { hierarchy = props.hierarchy; user_name = props.user_name; } + const location = useLocation(); const history = useHistory(); const HEADER = { headers: { @@ -1727,10 +1732,17 @@ const CreatedProyek = ({ params, ...props }) => { content={popupMenu(text, record)} trigger="click" > + { + checkActMenup(location.pathname, 'read') ? + : + null + } + { + checkActMenup(location.pathname, 'update') ? { )} + : + null + } ), }, @@ -2106,12 +2121,18 @@ const CreatedProyek = ({ params, ...props }) => { {parseInt(role_id) == 44 ? null : ( // role kustomer - + { + checkActMenup(location.pathname, 'create') ? + + : + null + } )} From dedc56f2beae66d9ec1f90966977e2fe00765892 Mon Sep 17 00:00:00 2001 From: khaidralirahman Date: Tue, 5 Mar 2024 14:48:29 +0700 Subject: [PATCH 21/24] divisi --- src/views/SimproV2/Divisi/index.js | 37 ++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/src/views/SimproV2/Divisi/index.js b/src/views/SimproV2/Divisi/index.js index 68b1825..d423c8f 100644 --- a/src/views/SimproV2/Divisi/index.js +++ b/src/views/SimproV2/Divisi/index.js @@ -8,6 +8,14 @@ import { DIVISI_LIST, DIVISI_ADD, DIVISI_EDIT, DIVISI_DELETE, DIVISI_SEARCH, COM import { NotificationContainer, NotificationManager } from 'react-notifications'; import { Pagination, Button, Tooltip, Table, Spin } from 'antd'; import { useTranslation } from 'react-i18next'; +import { useLocation } from "react-router-dom"; +import { + formatNumber, + formatRupiah, + formatThousand, + renderFormatRupiah, + checkActMenup, +} from "../../../const/CustomFunc"; const ProjectType = ({ params, ...props }) => { @@ -24,7 +32,7 @@ const ProjectType = ({ params, ...props }) => { hierarchy = props.hierarchy; user_name = props.user_name; } - + const location = useLocation(); const HEADER = { headers: { "Content-Type": "application/json", @@ -400,13 +408,29 @@ const ProjectType = ({ params, ...props }) => { render: (text, record) => <> - handleDelete(text.id)}> + { + checkActMenup(location.pathname, 'delete') ? + handleDelete(text.id)}> + : + null + } + - handleAddChild(text)}> + { + checkActMenup(location.pathname, 'create') ? + handleAddChild(text)}> + : + null + } - handleEdit(text)}> + { + checkActMenup(location.pathname, 'update') ? + handleEdit(text)}> + : + null + } , @@ -511,7 +535,12 @@ const ProjectType = ({ params, ...props }) => { + { + checkActMenup(location.pathname, 'create') ? + : + null + } From 9ad1234db2cef26c0459d5a46dcd3df50e91be83 Mon Sep 17 00:00:00 2001 From: khaidralirahman Date: Tue, 5 Mar 2024 14:48:45 +0700 Subject: [PATCH 22/24] project type --- src/views/SimproV2/ProjectType/index.js | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/views/SimproV2/ProjectType/index.js b/src/views/SimproV2/ProjectType/index.js index 5d5b37c..0e23258 100644 --- a/src/views/SimproV2/ProjectType/index.js +++ b/src/views/SimproV2/ProjectType/index.js @@ -11,6 +11,8 @@ import { NotificationContainer, NotificationManager } from 'react-notifications' import { PROJECT_TYPE_ADD, PROJECT_TYPE_EDIT, PROJECT_TYPE_DELETE, PROJECT_TYPE_SEARCH, COMPANY_MANAGEMENT_LIST } from '../../../const/ApiConst'; import { Pagination, Button, Tooltip, Table } from 'antd'; import { useTranslation } from 'react-i18next'; +import { checkActMenup } from '../../../const/CustomFunc.js'; +import { useLocation } from "react-router-dom"; const ProjectType = ({ params, ...props }) => { @@ -27,6 +29,7 @@ const ProjectType = ({ params, ...props }) => { hierarchy = props.hierarchy; user_name = props.user_name; } + const location = useLocation() const HEADER = { headers: { "Content-Type": "application/json", @@ -353,10 +356,20 @@ const ProjectType = ({ params, ...props }) => { className: 'nowrap', render: (text, record) => <> - handleDelete(text.id)}> + { + checkActMenup(location.pathname, 'delete') ? + handleDelete(text.id)}> + : + null + } - handleEdit(text)}> + { + checkActMenup(location.pathname, 'update') ? + handleEdit(text)}> + : + null + } {" "} handleDialogIg(text.id)}> @@ -430,7 +443,12 @@ const ProjectType = ({ params, ...props }) => { + { + checkActMenup(location.pathname, 'create') ? + : + null + } From db8397684a42dcbf5902c82aa8e082e8346c5de8 Mon Sep 17 00:00:00 2001 From: khaidralirahman Date: Tue, 5 Mar 2024 14:49:00 +0700 Subject: [PATCH 23/24] HR --- src/views/SimproV2/ResourceWorker/index.js | 36 +++++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/src/views/SimproV2/ResourceWorker/index.js b/src/views/SimproV2/ResourceWorker/index.js index e042b07..9219541 100644 --- a/src/views/SimproV2/ResourceWorker/index.js +++ b/src/views/SimproV2/ResourceWorker/index.js @@ -8,11 +8,19 @@ import moment from 'moment' import { Card, CardBody, CardHeader, Col, Row, Input } from 'reactstrap'; import { DownloadOutlined } from '@ant-design/icons'; import { NotificationContainer, NotificationManager } from 'react-notifications'; +import { useLocation } from "react-router-dom"; import { Pagination, Table, Button, Tooltip, Spin } from 'antd'; import { USER_ADD, USER_SEARCH, USER_EDIT, USER_DELETE, ROLE_SEARCH, DIVISI_SEARCH, USER_SHIFT_ADD, COMPANY_MANAGEMENT_LIST } from '../../../const/ApiConst'; import { useTranslation } from 'react-i18next'; +import { + formatNumber, + formatRupiah, + formatThousand, + renderFormatRupiah, + checkActMenup, +} from "../../../const/CustomFunc"; const ResourceWorker = ({ params, ...props }) => { let role_id = 0, user_id = 0, isLogin = false, token = '', company_id = 0, all_project = null, role_name = '', hierarchy = [], user_name = ''; @@ -28,7 +36,7 @@ const ResourceWorker = ({ params, ...props }) => { hierarchy = props.hierarchy; user_name = props.user_name; } - + const location = useLocation(); const HEADER = { headers: { "Content-Type": "application/json", @@ -547,14 +555,29 @@ const ResourceWorker = ({ params, ...props }) => { key: 'x', render: (text, record) => <> - + { + checkActMenup(location.pathname, 'update') ? + handleEdit(text)} className="fa fa-edit"> + : + null + } - + { + checkActMenup(location.pathname, 'delete') ? + handleDelete(text.id)} className="fa fa-trash"> + : + null + } - + { + checkActMenup(location.pathname, 'update') ? + handleSetWorker(text)} className="fa fa-key"> + : + null + } , }, @@ -638,7 +661,12 @@ const ResourceWorker = ({ params, ...props }) => { + { + checkActMenup(location.pathname, 'create') ? + : + null + } From 03fd2fe6f2dbc60b6a360c1f95d9a6a7f45a4c70 Mon Sep 17 00:00:00 2001 From: khaidralirahman Date: Tue, 5 Mar 2024 14:49:24 +0700 Subject: [PATCH 24/24] UOM --- src/views/SimproV2/Satuan/index.js | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/src/views/SimproV2/Satuan/index.js b/src/views/SimproV2/Satuan/index.js index ab81794..89cfad8 100644 --- a/src/views/SimproV2/Satuan/index.js +++ b/src/views/SimproV2/Satuan/index.js @@ -8,6 +8,14 @@ import { NotificationContainer, NotificationManager } from 'react-notifications' import { Pagination, Button, Tooltip } from 'antd'; import { SATUAN_ADD, SATUAN_EDIT, SATUAN_DELETE, SATUAN_SEARCH, COMPANY_MANAGEMENT_LIST } from '../../../const/ApiConst'; import { useTranslation } from 'react-i18next'; +import { useLocation } from "react-router-dom"; +import { + formatNumber, + formatRupiah, + formatThousand, + renderFormatRupiah, + checkActMenup, +} from "../../../const/CustomFunc"; const Satuan = ({ params, ...props }) => { @@ -24,7 +32,7 @@ const Satuan = ({ params, ...props }) => { hierarchy = props.hierarchy; user_name = props.user_name; } - + const location = useLocation(); const HEADER = { headers: { "Content-Type": "application/json", @@ -352,7 +360,12 @@ const Satuan = ({ params, ...props }) => { + { + checkActMenup(location.pathname, 'create') ? + : + null + } @@ -380,11 +393,21 @@ const Satuan = ({ params, ...props }) => { - handleDelete(n.id)}> + { + checkActMenup(location.pathname, 'delete') ? + handleDelete(n.id)}> + : + null + } - handleEdit(n)}> + { + checkActMenup(location.pathname, 'update') ? + handleEdit(n)}> + : + null + } {role_name === 'Super Admin' &&