feat: change ui table
parent
f8b94547a1
commit
6964ca5764
|
@ -20,7 +20,8 @@
|
||||||
"react-icons": "^5.3.0",
|
"react-icons": "^5.3.0",
|
||||||
"react-intl": "^6.7.0",
|
"react-intl": "^6.7.0",
|
||||||
"react-router-dom": "^6.26.2",
|
"react-router-dom": "^6.26.2",
|
||||||
"recoil": "^0.7.7"
|
"recoil": "^0.7.7",
|
||||||
|
"uuid": "^10.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.9.0",
|
"@eslint/js": "^9.9.0",
|
||||||
|
@ -29,6 +30,7 @@
|
||||||
"@types/node": "^22.7.4",
|
"@types/node": "^22.7.4",
|
||||||
"@types/react": "^18.3.3",
|
"@types/react": "^18.3.3",
|
||||||
"@types/react-dom": "^18.3.0",
|
"@types/react-dom": "^18.3.0",
|
||||||
|
"@types/uuid": "^10.0.0",
|
||||||
"@vitejs/plugin-react-swc": "^3.5.0",
|
"@vitejs/plugin-react-swc": "^3.5.0",
|
||||||
"autoprefixer": "^10.4.20",
|
"autoprefixer": "^10.4.20",
|
||||||
"eslint": "^9.9.0",
|
"eslint": "^9.9.0",
|
||||||
|
@ -1564,6 +1566,12 @@
|
||||||
"@types/react": "*"
|
"@types/react": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/uuid": {
|
||||||
|
"version": "10.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz",
|
||||||
|
"integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||||
"version": "8.8.0",
|
"version": "8.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.8.0.tgz",
|
||||||
|
@ -5229,6 +5237,18 @@
|
||||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/uuid": {
|
||||||
|
"version": "10.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz",
|
||||||
|
"integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==",
|
||||||
|
"funding": [
|
||||||
|
"https://github.com/sponsors/broofa",
|
||||||
|
"https://github.com/sponsors/ctavan"
|
||||||
|
],
|
||||||
|
"bin": {
|
||||||
|
"uuid": "dist/bin/uuid"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "5.4.8",
|
"version": "5.4.8",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.8.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.8.tgz",
|
||||||
|
|
|
@ -22,7 +22,8 @@
|
||||||
"react-icons": "^5.3.0",
|
"react-icons": "^5.3.0",
|
||||||
"react-intl": "^6.7.0",
|
"react-intl": "^6.7.0",
|
||||||
"react-router-dom": "^6.26.2",
|
"react-router-dom": "^6.26.2",
|
||||||
"recoil": "^0.7.7"
|
"recoil": "^0.7.7",
|
||||||
|
"uuid": "^10.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.9.0",
|
"@eslint/js": "^9.9.0",
|
||||||
|
@ -31,6 +32,7 @@
|
||||||
"@types/node": "^22.7.4",
|
"@types/node": "^22.7.4",
|
||||||
"@types/react": "^18.3.3",
|
"@types/react": "^18.3.3",
|
||||||
"@types/react-dom": "^18.3.0",
|
"@types/react-dom": "^18.3.0",
|
||||||
|
"@types/uuid": "^10.0.0",
|
||||||
"@vitejs/plugin-react-swc": "^3.5.0",
|
"@vitejs/plugin-react-swc": "^3.5.0",
|
||||||
"autoprefixer": "^10.4.20",
|
"autoprefixer": "^10.4.20",
|
||||||
"eslint": "^9.9.0",
|
"eslint": "^9.9.0",
|
||||||
|
|
|
@ -2,14 +2,18 @@ import axios from 'axios';
|
||||||
import AdminLayout from './layout';
|
import AdminLayout from './layout';
|
||||||
import { API_URL, currencyFormatter } from '@pos/base';
|
import { API_URL, currencyFormatter } from '@pos/base';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { Card, Col, DatePicker, Row, Table, Typography } from 'antd';
|
import { Card, Col, DatePicker, notification, Row, Table, Typography } from 'antd';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { filter } from 'lodash';
|
import lodash, { filter } from 'lodash';
|
||||||
|
import { v4 } from 'uuid';
|
||||||
|
|
||||||
export default function Admin() {
|
export default function Admin() {
|
||||||
const [dataItem, setDataItem] = useState<any[]>([]);
|
const [dataItem, setDataItem] = useState<any[]>([]);
|
||||||
const [dataItemMaster, setDataItemMaster] = useState<any[]>([]);
|
const [dataItemMaster, setDataItemMaster] = useState<any[]>([]);
|
||||||
|
|
||||||
|
const [dataItemKeys, setDataItemKeys] = useState<any[]>([]);
|
||||||
|
const [dataItemMasterKeys, setDataItemMasterKeys] = useState<any[]>([]);
|
||||||
|
|
||||||
const [loadingDataItem, setLoadingDataItem] = useState<boolean>(false);
|
const [loadingDataItem, setLoadingDataItem] = useState<boolean>(false);
|
||||||
const [loadingDataItemMaster, setLoadingDataItemMaster] = useState<boolean>(false);
|
const [loadingDataItemMaster, setLoadingDataItemMaster] = useState<boolean>(false);
|
||||||
const [filterDate, setFilerDate] = useState(dayjs());
|
const [filterDate, setFilerDate] = useState(dayjs());
|
||||||
|
@ -20,10 +24,31 @@ export default function Admin() {
|
||||||
.get(API_URL.REPORT_SUMMARY_INCOME_ITEM, { params: params })
|
.get(API_URL.REPORT_SUMMARY_INCOME_ITEM, { params: params })
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
const data = resp.data.data;
|
const data = resp.data.data;
|
||||||
setDataItem(data);
|
|
||||||
|
const groupedData = lodash(data)
|
||||||
|
.groupBy('item_owner') // Group by item_owner
|
||||||
|
.map((items, owner) => ({
|
||||||
|
// Map over each group to sum values and keep children
|
||||||
|
title: owner,
|
||||||
|
tr_item__qty: lodash.sumBy(items, (item) => Number(item.tr_item__qty)), // Convert to number
|
||||||
|
tr_item__total_net_price: lodash.sumBy(items, (item) => Number(item.tr_item__total_net_price)), // Convert to number
|
||||||
|
children: items.map((item) => {
|
||||||
|
return { ...item, title: item.tr_item__item_name };
|
||||||
|
}), // Include the original data as children
|
||||||
|
}))
|
||||||
|
.value()
|
||||||
|
.map((item) => {
|
||||||
|
return {
|
||||||
|
key: v4(),
|
||||||
|
...item,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
setDataItemKeys(groupedData.map((item) => item.key));
|
||||||
|
setDataItem(groupedData);
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.log(err);
|
notification.error({ message: err?.message });
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
setLoadingDataItem(false);
|
setLoadingDataItem(false);
|
||||||
|
@ -36,10 +61,38 @@ export default function Admin() {
|
||||||
.get(API_URL.REPORT_SUMMARY_INCOME_ITEM_MASTER, { params: params })
|
.get(API_URL.REPORT_SUMMARY_INCOME_ITEM_MASTER, { params: params })
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
const data = resp.data.data;
|
const data = resp.data.data;
|
||||||
setDataItemMaster(data);
|
const groupedData = lodash(data)
|
||||||
|
.groupBy('item_owner') // Group by item_owner
|
||||||
|
.map((items, owner) => ({
|
||||||
|
// Map over each group to sum values and keep children
|
||||||
|
title: owner,
|
||||||
|
tr_item__qty: lodash.sumBy(items, (item) => Number(item.tr_item__qty)), // Convert to number
|
||||||
|
tr_item_bundling__total_net_price: lodash.sumBy(items, (item) =>
|
||||||
|
Number(item.tr_item_bundling__total_net_price),
|
||||||
|
), // Convert to number
|
||||||
|
children: items.map((item) => {
|
||||||
|
let title = '';
|
||||||
|
if (item.tr_item_bundling__item_name) {
|
||||||
|
title = `${item.tr_item_bundling__item_name} / ${item.tr_item__item_name}`;
|
||||||
|
} else {
|
||||||
|
title = item.tr_item__item_name;
|
||||||
|
}
|
||||||
|
return { ...item, title: title };
|
||||||
|
}), // Include the original data as children
|
||||||
|
}))
|
||||||
|
.value()
|
||||||
|
.map((item) => {
|
||||||
|
return {
|
||||||
|
key: v4(),
|
||||||
|
...item,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
setDataItemMasterKeys(groupedData.map((item) => item.key));
|
||||||
|
setDataItemMaster(groupedData);
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.log(err);
|
notification.error({ message: err?.message });
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
setLoadingDataItemMaster(false);
|
setLoadingDataItemMaster(false);
|
||||||
|
@ -61,12 +114,29 @@ export default function Admin() {
|
||||||
<AdminLayout>
|
<AdminLayout>
|
||||||
<Row>
|
<Row>
|
||||||
<Col xl={8} lg={8} md={12} span={24}>
|
<Col xl={8} lg={8} md={12} span={24}>
|
||||||
<DatePicker value={filterDate} style={{ width: '100%' }} format={'DD-MM-YYYY'} onChange={setFilerDate} />
|
<DatePicker
|
||||||
|
size="large"
|
||||||
|
popupStyle={{ fontSize: 18 }}
|
||||||
|
allowClear={false}
|
||||||
|
value={filterDate}
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
format={'DD-MM-YYYY'}
|
||||||
|
onChange={setFilerDate}
|
||||||
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
<div style={{ marginBottom: 20 }}></div>
|
<div style={{ marginBottom: 20 }}></div>
|
||||||
|
|
||||||
<Card title={'PENDAPATAN PER ITEM'}>
|
<Card
|
||||||
|
title={
|
||||||
|
<div style={{ paddingTop: 10, paddingBottom: 10 }}>
|
||||||
|
<div style={{ fontSize: 16, fontWeight: 600 }}>Pendapatan Per Item</div>
|
||||||
|
<div
|
||||||
|
style={{ fontWeight: 400, fontSize: 12, color: 'grey' }}
|
||||||
|
>{`Laporan pendapatan item per tanggal ${filterDate.format('DD-MM-YYYY')}`}</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
<Table
|
<Table
|
||||||
bordered
|
bordered
|
||||||
size="small"
|
size="small"
|
||||||
|
@ -74,22 +144,34 @@ export default function Admin() {
|
||||||
pagination={false}
|
pagination={false}
|
||||||
loading={loadingDataItem}
|
loading={loadingDataItem}
|
||||||
scroll={{ x: 'max-width', y: 350 }}
|
scroll={{ x: 'max-width', y: 350 }}
|
||||||
|
rowKey={(child) => child.key} // Make sure each child row has a unique key
|
||||||
|
expandable={{ expandedRowKeys: dataItemKeys, showExpandColumn: false }}
|
||||||
|
rowClassName={(row) => (row.key ? 'row-group' : '')}
|
||||||
|
rowHoverable={false}
|
||||||
columns={[
|
columns={[
|
||||||
{ dataIndex: 'main__payment_date', title: 'Date', width: 100 },
|
{ key: 'title', dataIndex: 'title', title: 'TITLE', width: 170 },
|
||||||
{ dataIndex: 'item_owner', title: 'Ownership', width: 100 },
|
{ key: 'tr_item__qty', dataIndex: 'tr_item__qty', title: 'PAX', width: 70 },
|
||||||
{ dataIndex: 'tr_item__item_name', title: 'Item', width: 150 },
|
|
||||||
{ dataIndex: 'tr_item__qty', title: 'PAX', width: 70 },
|
|
||||||
{
|
{
|
||||||
|
key: 'tr_item__total_net_price',
|
||||||
dataIndex: 'tr_item__total_net_price',
|
dataIndex: 'tr_item__total_net_price',
|
||||||
title: 'Revenue',
|
title: 'REVENUE',
|
||||||
width: 150,
|
width: 120,
|
||||||
render: (value) => currencyFormatter({ value }),
|
render: (value) => currencyFormatter({ value }),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
<div style={{ marginBottom: 20 }}></div>
|
<div style={{ marginBottom: 20 }}></div>
|
||||||
<Card title="PENDAPATAN PER ITEM MASTER">
|
<Card
|
||||||
|
title={
|
||||||
|
<div style={{ paddingTop: 10, paddingBottom: 10 }}>
|
||||||
|
<div style={{ fontSize: 16, fontWeight: 600 }}>Pendapatan Per Item Master</div>
|
||||||
|
<div
|
||||||
|
style={{ fontWeight: 400, fontSize: 12, color: 'grey' }}
|
||||||
|
>{`Laporan pendapatan item master per tanggal ${filterDate.format('DD-MM-YYYY')}`}</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
<Table
|
<Table
|
||||||
bordered
|
bordered
|
||||||
size="small"
|
size="small"
|
||||||
|
@ -97,21 +179,19 @@ export default function Admin() {
|
||||||
pagination={false}
|
pagination={false}
|
||||||
loading={loadingDataItemMaster}
|
loading={loadingDataItemMaster}
|
||||||
scroll={{ x: 'max-width', y: 350 }}
|
scroll={{ x: 'max-width', y: 350 }}
|
||||||
|
rowKey={(child) => child.key} // Make sure each child row has a unique key
|
||||||
|
expandable={{ expandedRowKeys: dataItemMasterKeys, showExpandColumn: false }}
|
||||||
|
rowClassName={(row) => (row.key ? 'row-group' : '')}
|
||||||
|
rowHoverable={false}
|
||||||
columns={[
|
columns={[
|
||||||
{ dataIndex: 'main__payment_date', title: 'Date', width: 100 },
|
{ key: 'title', dataIndex: 'title', title: 'TITLE', width: 170 },
|
||||||
{ dataIndex: 'item_owner', title: 'Ownership', width: 100 },
|
|
||||||
{
|
{ key: 'tr_item__qty', dataIndex: 'tr_item__qty', title: 'PAX', width: 70 },
|
||||||
dataIndex: 'tr_item_bundling__item_name',
|
|
||||||
title: 'Item Bundling',
|
|
||||||
width: 150,
|
|
||||||
render: (item) => item ?? '-',
|
|
||||||
},
|
|
||||||
{ dataIndex: 'tr_item__item_name', title: 'Item', width: 150 },
|
|
||||||
{ dataIndex: 'tr_item__qty', title: 'PAX', width: 70 },
|
|
||||||
{
|
{
|
||||||
|
key: 'tr_item_bundling__total_net_price',
|
||||||
dataIndex: 'tr_item_bundling__total_net_price',
|
dataIndex: 'tr_item_bundling__total_net_price',
|
||||||
title: 'Revenue',
|
title: 'REVENUE',
|
||||||
width: 150,
|
width: 120,
|
||||||
render: (value) => currencyFormatter({ value }),
|
render: (value) => currencyFormatter({ value }),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import { Button, Flex, Layout, Popconfirm, Tooltip, Typography } from 'antd';
|
import { Avatar, Flex, Image, Layout, Popconfirm, Tooltip } from 'antd';
|
||||||
import { ReactNode, useState } from 'react';
|
import { ReactNode, useState } from 'react';
|
||||||
import { Content, Header } from 'antd/es/layout/layout';
|
import { Content, Header } from 'antd/es/layout/layout';
|
||||||
import { API_URL, handleLogout, IconWrapper } from '@pos/base';
|
import { API_URL, handleLogout } from '@pos/base';
|
||||||
import { FaUser } from 'react-icons/fa';
|
import { FaUser } from 'react-icons/fa';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
import Logo from '../../../base/presentation/assets/images/we-logo.png';
|
||||||
|
|
||||||
interface AdminLayoutProps {
|
interface AdminLayoutProps {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
|
@ -41,7 +42,8 @@ export default function AdminLayout(props: AdminLayoutProps) {
|
||||||
>
|
>
|
||||||
<Flex style={{ width: '100%' }} align="center">
|
<Flex style={{ width: '100%' }} align="center">
|
||||||
<Flex>
|
<Flex>
|
||||||
<div style={{ fontWeight: 800, fontSize: 16 }}>LAPORAN</div>
|
<Image src={Logo} width={50} preview={false} />
|
||||||
|
{/* <div style={{ fontWeight: 800, fontSize: 16 }}>WE POS</div> */}
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex style={{ marginLeft: 'auto' }}>
|
<Flex style={{ marginLeft: 'auto' }}>
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
|
@ -50,7 +52,9 @@ export default function AdminLayout(props: AdminLayoutProps) {
|
||||||
onConfirm={() => handleClickLogout()}
|
onConfirm={() => handleClickLogout()}
|
||||||
>
|
>
|
||||||
<Tooltip title="Logout" placement="bottom">
|
<Tooltip title="Logout" placement="bottom">
|
||||||
<FaUser style={{ fontSize: 16 }} />
|
<Avatar size={35}>
|
||||||
|
<FaUser style={{ fontSize: 14 }} />
|
||||||
|
</Avatar>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
|
@ -12,3 +12,9 @@ html {
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.row-group {
|
||||||
|
background: #00b8be;
|
||||||
|
color: #fff;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
|
@ -18,8 +18,8 @@ export const LIGHT: ThemeConfig = {
|
||||||
padding: 4,
|
padding: 4,
|
||||||
paddingXS: 4,
|
paddingXS: 4,
|
||||||
headerBorderRadius: 4,
|
headerBorderRadius: 4,
|
||||||
// headerBg: 'rgb(1,23,110)',
|
headerBg: '#006cb2',
|
||||||
// headerColor: 'rgba(255,255,255,0.88)',
|
headerColor: '#fff',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,7 +4,6 @@ import axios from 'axios';
|
||||||
|
|
||||||
async function credentialChecker() {
|
async function credentialChecker() {
|
||||||
const token = await StorageAccessToken.get();
|
const token = await StorageAccessToken.get();
|
||||||
console.log(token);
|
|
||||||
if (token) {
|
if (token) {
|
||||||
const [, body] = token.split('.');
|
const [, body] = token.split('.');
|
||||||
const bodyToken = JSON.parse(window.atob(body));
|
const bodyToken = JSON.parse(window.atob(body));
|
||||||
|
|
Loading…
Reference in New Issue