From 6964ca5764f3bc91f35ff2c6b7eb730f23fffd31 Mon Sep 17 00:00:00 2001 From: Firman Ramdhani <33869609+firmanramdhani@users.noreply.github.com> Date: Wed, 2 Oct 2024 19:07:08 +0700 Subject: [PATCH] feat: change ui table --- package-lock.json | 22 ++- package.json | 4 +- src/apps/admin/index.tsx | 134 ++++++++++++++---- src/apps/admin/layout/index.tsx | 12 +- src/base/presentation/assets/styles/main.less | 6 + .../presentation/assets/themes/light-theme.ts | 4 +- src/utils/credential-checker.ts | 1 - 7 files changed, 147 insertions(+), 36 deletions(-) diff --git a/package-lock.json b/package-lock.json index 940eaf6..5d2dce0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index 13ab3ff..0b9aeed 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/apps/admin/index.tsx b/src/apps/admin/index.tsx index 6826c0a..2009112 100644 --- a/src/apps/admin/index.tsx +++ b/src/apps/admin/index.tsx @@ -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([]); const [dataItemMaster, setDataItemMaster] = useState([]); + const [dataItemKeys, setDataItemKeys] = useState([]); + const [dataItemMasterKeys, setDataItemMasterKeys] = useState([]); + const [loadingDataItem, setLoadingDataItem] = useState(false); const [loadingDataItemMaster, setLoadingDataItemMaster] = useState(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() { - +
- + +
Pendapatan Per Item
+
{`Laporan pendapatan item per tanggal ${filterDate.format('DD-MM-YYYY')}`}
+ + } + > 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 }), }, ]} />
- + +
Pendapatan Per Item Master
+
{`Laporan pendapatan item master per tanggal ${filterDate.format('DD-MM-YYYY')}`}
+ + } + >
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 }), }, ]} diff --git a/src/apps/admin/layout/index.tsx b/src/apps/admin/layout/index.tsx index 12763fa..d1a4d39 100644 --- a/src/apps/admin/layout/index.tsx +++ b/src/apps/admin/layout/index.tsx @@ -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) { > -
LAPORAN
+ + {/*
WE POS
*/}
handleClickLogout()} > - + + + diff --git a/src/base/presentation/assets/styles/main.less b/src/base/presentation/assets/styles/main.less index 0cd27cc..38fd935 100644 --- a/src/base/presentation/assets/styles/main.less +++ b/src/base/presentation/assets/styles/main.less @@ -12,3 +12,9 @@ html { body { margin: 0; } + +.row-group { + background: #00b8be; + color: #fff; + font-weight: 600; +} diff --git a/src/base/presentation/assets/themes/light-theme.ts b/src/base/presentation/assets/themes/light-theme.ts index 7b1b3b5..9c9fcdf 100644 --- a/src/base/presentation/assets/themes/light-theme.ts +++ b/src/base/presentation/assets/themes/light-theme.ts @@ -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', }, }, }; diff --git a/src/utils/credential-checker.ts b/src/utils/credential-checker.ts index c878c48..cd5269f 100644 --- a/src/utils/credential-checker.ts +++ b/src/utils/credential-checker.ts @@ -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));