feat: change ui table

main
Firman Ramdhani 2024-10-02 19:07:08 +07:00
parent f8b94547a1
commit 6964ca5764
7 changed files with 147 additions and 36 deletions

22
package-lock.json generated
View File

@ -20,7 +20,8 @@
"react-icons": "^5.3.0",
"react-intl": "^6.7.0",
"react-router-dom": "^6.26.2",
"recoil": "^0.7.7"
"recoil": "^0.7.7",
"uuid": "^10.0.0"
},
"devDependencies": {
"@eslint/js": "^9.9.0",
@ -29,6 +30,7 @@
"@types/node": "^22.7.4",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@types/uuid": "^10.0.0",
"@vitejs/plugin-react-swc": "^3.5.0",
"autoprefixer": "^10.4.20",
"eslint": "^9.9.0",
@ -1564,6 +1566,12 @@
"@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": {
"version": "8.8.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.8.0.tgz",
@ -5229,6 +5237,18 @@
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
"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": {
"version": "5.4.8",
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.8.tgz",

View File

@ -22,7 +22,8 @@
"react-icons": "^5.3.0",
"react-intl": "^6.7.0",
"react-router-dom": "^6.26.2",
"recoil": "^0.7.7"
"recoil": "^0.7.7",
"uuid": "^10.0.0"
},
"devDependencies": {
"@eslint/js": "^9.9.0",
@ -31,6 +32,7 @@
"@types/node": "^22.7.4",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@types/uuid": "^10.0.0",
"@vitejs/plugin-react-swc": "^3.5.0",
"autoprefixer": "^10.4.20",
"eslint": "^9.9.0",

View File

@ -2,14 +2,18 @@ import axios from 'axios';
import AdminLayout from './layout';
import { API_URL, currencyFormatter } from '@pos/base';
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 { filter } from 'lodash';
import lodash, { filter } from 'lodash';
import { v4 } from 'uuid';
export default function Admin() {
const [dataItem, setDataItem] = useState<any[]>([]);
const [dataItemMaster, setDataItemMaster] = useState<any[]>([]);
const [dataItemKeys, setDataItemKeys] = useState<any[]>([]);
const [dataItemMasterKeys, setDataItemMasterKeys] = useState<any[]>([]);
const [loadingDataItem, setLoadingDataItem] = useState<boolean>(false);
const [loadingDataItemMaster, setLoadingDataItemMaster] = useState<boolean>(false);
const [filterDate, setFilerDate] = useState(dayjs());
@ -20,10 +24,31 @@ export default function Admin() {
.get(API_URL.REPORT_SUMMARY_INCOME_ITEM, { params: params })
.then((resp) => {
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) => {
console.log(err);
notification.error({ message: err?.message });
})
.finally(() => {
setLoadingDataItem(false);
@ -36,10 +61,38 @@ export default function Admin() {
.get(API_URL.REPORT_SUMMARY_INCOME_ITEM_MASTER, { params: params })
.then((resp) => {
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) => {
console.log(err);
notification.error({ message: err?.message });
})
.finally(() => {
setLoadingDataItemMaster(false);
@ -61,12 +114,29 @@ export default function Admin() {
<AdminLayout>
<Row>
<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>
</Row>
<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
bordered
size="small"
@ -74,22 +144,34 @@ export default function Admin() {
pagination={false}
loading={loadingDataItem}
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={[
{ dataIndex: 'main__payment_date', title: 'Date', width: 100 },
{ dataIndex: 'item_owner', title: 'Ownership', width: 100 },
{ dataIndex: 'tr_item__item_name', title: 'Item', width: 150 },
{ dataIndex: 'tr_item__qty', title: 'PAX', width: 70 },
{ key: 'title', dataIndex: 'title', title: 'TITLE', width: 170 },
{ key: 'tr_item__qty', dataIndex: 'tr_item__qty', title: 'PAX', width: 70 },
{
key: 'tr_item__total_net_price',
dataIndex: 'tr_item__total_net_price',
title: 'Revenue',
width: 150,
title: 'REVENUE',
width: 120,
render: (value) => currencyFormatter({ value }),
},
]}
/>
</Card>
<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
bordered
size="small"
@ -97,21 +179,19 @@ export default function Admin() {
pagination={false}
loading={loadingDataItemMaster}
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={[
{ dataIndex: 'main__payment_date', title: 'Date', width: 100 },
{ dataIndex: 'item_owner', title: 'Ownership', width: 100 },
{
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: 'title', dataIndex: 'title', title: 'TITLE', width: 170 },
{ key: 'tr_item__qty', dataIndex: 'tr_item__qty', title: 'PAX', width: 70 },
{
key: 'tr_item_bundling__total_net_price',
dataIndex: 'tr_item_bundling__total_net_price',
title: 'Revenue',
width: 150,
title: 'REVENUE',
width: 120,
render: (value) => currencyFormatter({ value }),
},
]}

View File

@ -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 { 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 axios from 'axios';
import Logo from '../../../base/presentation/assets/images/we-logo.png';
interface AdminLayoutProps {
children: ReactNode;
@ -41,7 +42,8 @@ export default function AdminLayout(props: AdminLayoutProps) {
>
<Flex style={{ width: '100%' }} align="center">
<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 style={{ marginLeft: 'auto' }}>
<Popconfirm
@ -50,7 +52,9 @@ export default function AdminLayout(props: AdminLayoutProps) {
onConfirm={() => handleClickLogout()}
>
<Tooltip title="Logout" placement="bottom">
<FaUser style={{ fontSize: 16 }} />
<Avatar size={35}>
<FaUser style={{ fontSize: 14 }} />
</Avatar>
</Tooltip>
</Popconfirm>
</Flex>

View File

@ -12,3 +12,9 @@ html {
body {
margin: 0;
}
.row-group {
background: #00b8be;
color: #fff;
font-weight: 600;
}

View File

@ -18,8 +18,8 @@ export const LIGHT: ThemeConfig = {
padding: 4,
paddingXS: 4,
headerBorderRadius: 4,
// headerBg: 'rgb(1,23,110)',
// headerColor: 'rgba(255,255,255,0.88)',
headerBg: '#006cb2',
headerColor: '#fff',
},
},
};

View File

@ -4,7 +4,6 @@ import axios from 'axios';
async function credentialChecker() {
const token = await StorageAccessToken.get();
console.log(token);
if (token) {
const [, body] = token.split('.');
const bodyToken = JSON.parse(window.atob(body));