diff --git a/auto-bid-admin/src/components/admin/admin-modal.tsx b/auto-bid-admin/src/components/admin/admin-modal.tsx index 0fa57c2..c65a52f 100644 --- a/auto-bid-admin/src/components/admin/admin-modal.tsx +++ b/auto-bid-admin/src/components/admin/admin-modal.tsx @@ -1,10 +1,10 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import { Badge, Box, Button, Modal, ModalProps, PasswordInput, Text, TextInput, Tooltip } from '@mantine/core'; +import { Badge, Box, Button, LoadingOverlay, Modal, ModalProps, PasswordInput, Text, TextInput, Tooltip } from '@mantine/core'; import { useForm, zodResolver } from '@mantine/form'; import { useDisclosure } from '@mantine/hooks'; import { IconPlus } from '@tabler/icons-react'; import _ from 'lodash'; -import { useEffect, useRef } from 'react'; +import { useEffect, useRef, useState } from 'react'; import { z } from 'zod'; import { createAdmin, updateAdmin } from '../../apis/admin'; import { useConfirmStore } from '../../lib/zustand/use-confirm'; @@ -40,6 +40,8 @@ export default function AdminModal({ data, onUpdated, ...props }: IAdminModelPro validate: zodResolver(data ? updateSchema : createSchema), }); + const [loading, setLoading] = useState(false); + const [opened, { open, close }] = useDisclosure(false); const { deletePermission, setPermissions, permissions, basePermission } = usePermissionStore(); @@ -54,7 +56,9 @@ export default function AdminModal({ data, onUpdated, ...props }: IAdminModelPro title: 'Update ?', message: `This account will be update`, handleOk: async () => { + setLoading(true); const result = await updateAdmin(values); + setLoading(false); if (!result) return; @@ -72,7 +76,10 @@ export default function AdminModal({ data, onUpdated, ...props }: IAdminModelPro } else { const { confirmPassword, ...newValues } = values; + setLoading(true); + const result = await createAdmin(newValues as Omit); + setLoading(false); if (!result) return; @@ -110,6 +117,7 @@ export default function AdminModal({ data, onUpdated, ...props }: IAdminModelPro return ( + + ); } diff --git a/auto-bid-admin/src/components/admin/grant-new-password-modal.tsx b/auto-bid-admin/src/components/admin/grant-new-password-modal.tsx index 61a10e2..20cd805 100644 --- a/auto-bid-admin/src/components/admin/grant-new-password-modal.tsx +++ b/auto-bid-admin/src/components/admin/grant-new-password-modal.tsx @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import { Button, Modal, ModalProps, PasswordInput } from '@mantine/core'; +import { Button, LoadingOverlay, Modal, ModalProps, PasswordInput } from '@mantine/core'; import { useForm, zodResolver } from '@mantine/form'; -import { useEffect } from 'react'; +import { useEffect, useState } from 'react'; import { z } from 'zod'; import { grantNewPasswordAdmin } from '../../apis/admin'; import { useConfirmStore } from '../../lib/zustand/use-confirm'; @@ -26,6 +26,8 @@ export default function GrantNewPasswordModal({ data, onUpdated, ...props }: IAd validate: zodResolver(schema), }); + const [loading, setLoading] = useState(false); + const { setConfirm } = useConfirmStore(); const handleSubmit = async (values: typeof form.values) => { @@ -34,10 +36,12 @@ export default function GrantNewPasswordModal({ data, onUpdated, ...props }: IAd title: 'Update ?', message: `This account will be update`, handleOk: async () => { + setLoading(true); const result = await grantNewPasswordAdmin({ id: data.id, password: values.password, }); + setLoading(false); if (!result) return; @@ -73,6 +77,7 @@ export default function GrantNewPasswordModal({ data, onUpdated, ...props }: IAd return ( + + ); } diff --git a/auto-bid-admin/src/components/bid/bid-modal.tsx b/auto-bid-admin/src/components/bid/bid-modal.tsx index 856eb5a..141dcdd 100644 --- a/auto-bid-admin/src/components/bid/bid-modal.tsx +++ b/auto-bid-admin/src/components/bid/bid-modal.tsx @@ -1,8 +1,8 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import { Button, Modal, ModalProps, NumberInput, TextInput } from '@mantine/core'; +import { Button, LoadingOverlay, Modal, ModalProps, NumberInput, TextInput } from '@mantine/core'; import { useForm, zodResolver } from '@mantine/form'; import _ from 'lodash'; -import { useEffect, useRef } from 'react'; +import { useEffect, useRef, useState } from 'react'; import { z } from 'zod'; import { createBid, updateBid } from '../../apis/bid'; import { useConfirmStore } from '../../lib/zustand/use-confirm'; @@ -28,13 +28,17 @@ export default function BidModal({ data, onUpdated, ...props }: IBidModelProps) const { setConfirm } = useConfirmStore(); + const [loading, setLoading] = useState(false); + const handleSubmit = async (values: typeof form.values) => { if (data) { setConfirm({ title: 'Update ?', message: `This product will be update`, handleOk: async () => { + setLoading(true); const result = await updateBid(values); + setLoading(false); if (!result) return; @@ -52,8 +56,11 @@ export default function BidModal({ data, onUpdated, ...props }: IBidModelProps) } else { const { url, max_price, plus_price } = values; + setLoading(true); const result = await createBid({ url, max_price, plus_price } as IBid); + setLoading(false); + if (!result) return; props.onClose(); @@ -83,6 +90,7 @@ export default function BidModal({ data, onUpdated, ...props }: IBidModelProps) return ( + + ); } diff --git a/auto-bid-admin/src/components/bid/show-histories-bid-grays-api-modal.tsx b/auto-bid-admin/src/components/bid/show-histories-bid-grays-api-modal.tsx index 62917e5..04ad1ee 100644 --- a/auto-bid-admin/src/components/bid/show-histories-bid-grays-api-modal.tsx +++ b/auto-bid-admin/src/components/bid/show-histories-bid-grays-api-modal.tsx @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import { Modal, ModalProps, Table } from '@mantine/core'; +import { LoadingOverlay, Modal, ModalProps, Table } from '@mantine/core'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { getDetailBidHistories } from '../../apis/bid-histories'; import { extractNumber } from '../../lib/table/ultils'; @@ -14,6 +14,8 @@ export interface IShowHistoriesBidGraysApiModalProps extends ModalProps { export default function ShowHistoriesBidGraysApiModal({ data, onUpdated, ...props }: IShowHistoriesBidGraysApiModalProps) { const [histories, setHistories] = useState[]>([]); + const [loading, setLoading] = useState(false); + const rows = useMemo(() => { return histories.map((element) => ( @@ -33,7 +35,9 @@ export default function ShowHistoriesBidGraysApiModal({ data, onUpdated, ...prop return; } + setLoading(true); const response = await getDetailBidHistories(data?.lot_id); + setLoading(false); if (response.data && response.data) { setHistories(response.data); @@ -45,7 +49,7 @@ export default function ShowHistoriesBidGraysApiModal({ data, onUpdated, ...prop }, [handleCallApi]); return ( - BIDDING HISTORY} centered> + BIDDING HISTORY} centered> @@ -68,6 +72,8 @@ export default function ShowHistoriesBidGraysApiModal({ data, onUpdated, ...prop )}
+ +
); } diff --git a/auto-bid-admin/src/components/user-menu.tsx b/auto-bid-admin/src/components/user-menu.tsx index 61975c1..8ca1640 100644 --- a/auto-bid-admin/src/components/user-menu.tsx +++ b/auto-bid-admin/src/components/user-menu.tsx @@ -1,4 +1,4 @@ -import { Avatar, Button, Menu, Modal, PasswordInput } from '@mantine/core'; +import { Avatar, Button, LoadingOverlay, Menu, Modal, PasswordInput } from '@mantine/core'; import { useForm, zodResolver } from '@mantine/form'; import { useDisclosure } from '@mantine/hooks'; import { IconLogout, IconSettings, IconUser } from '@tabler/icons-react'; @@ -7,6 +7,7 @@ import { z } from 'zod'; import { changePassword, logout } from '../apis/auth'; import { useConfirmStore } from '../lib/zustand/use-confirm'; import Links from '../system/links'; +import { useState } from 'react'; const schema = z .object({ @@ -24,6 +25,8 @@ export default function UserMenu() { const { setConfirm } = useConfirmStore(); + const [loading, setLoading] = useState(false); + const navigate = useNavigate(); const form = useForm({ initialValues: { @@ -59,11 +62,14 @@ export default function UserMenu() { message: 'This account will change password !', okButton: { value: 'Sure' }, handleOk: async () => { + setLoading(true); const data = await changePassword({ newPassword: values.newPassword, password: values.currentPassword, }); + setLoading(false); + if (data && data.data) { navigate(Links.LOGIN); close(); @@ -95,7 +101,7 @@ export default function UserMenu() { - +
@@ -104,6 +110,8 @@ export default function UserMenu() { Change + +
); diff --git a/auto-bid-admin/src/components/web-bid/web-account-modal.tsx b/auto-bid-admin/src/components/web-bid/web-account-modal.tsx index a0eae98..f2a35d2 100644 --- a/auto-bid-admin/src/components/web-bid/web-account-modal.tsx +++ b/auto-bid-admin/src/components/web-bid/web-account-modal.tsx @@ -1,8 +1,8 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import { Button, Modal, ModalProps, PasswordInput, TextInput } from '@mantine/core'; +import { Button, LoadingOverlay, Modal, ModalProps, PasswordInput, TextInput } from '@mantine/core'; import { useForm, zodResolver } from '@mantine/form'; import _ from 'lodash'; -import { useEffect, useRef } from 'react'; +import { useEffect, useRef, useState } from 'react'; import { z } from 'zod'; import { updateWebBid } from '../../apis/web-bid'; import { useConfirmStore } from '../../lib/zustand/use-confirm'; @@ -22,6 +22,8 @@ export default function WebAccountModal({ data, onUpdated, ...props }: IWebBidMo validate: zodResolver(schema), }); + const [loading, setLoading] = useState(false); + const prevData = useRef(data); const { setConfirm } = useConfirmStore(); @@ -32,7 +34,9 @@ export default function WebAccountModal({ data, onUpdated, ...props }: IWebBidMo title: 'Update ?', message: `This account will be update`, handleOk: async () => { + setLoading(true); const result = await updateWebBid(values); + setLoading(false); if (!result) return; @@ -48,7 +52,9 @@ export default function WebAccountModal({ data, onUpdated, ...props }: IWebBidMo }, }); } else { + setLoading(true); const result = await updateWebBid(values); + setLoading(false); if (!result) return; @@ -79,6 +85,7 @@ export default function WebAccountModal({ data, onUpdated, ...props }: IWebBidMo return ( + + ); } diff --git a/auto-bid-admin/src/components/web-bid/web-bid-modal.tsx b/auto-bid-admin/src/components/web-bid/web-bid-modal.tsx index 30f0897..a9eee58 100644 --- a/auto-bid-admin/src/components/web-bid/web-bid-modal.tsx +++ b/auto-bid-admin/src/components/web-bid/web-bid-modal.tsx @@ -1,8 +1,8 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import { Button, Modal, ModalProps, TextInput } from '@mantine/core'; +import { Button, LoadingOverlay, Modal, ModalProps, TextInput } from '@mantine/core'; import { useForm, zodResolver } from '@mantine/form'; import _ from 'lodash'; -import { useEffect, useRef } from 'react'; +import { useEffect, useRef, useState } from 'react'; import { z } from 'zod'; import { createWebBid, updateWebBid } from '../../apis/web-bid'; import { useConfirmStore } from '../../lib/zustand/use-confirm'; @@ -22,6 +22,8 @@ export default function WebBidModal({ data, onUpdated, ...props }: IWebBidModelP validate: zodResolver(z.object(schema)), }); + const [loading, setLoading] = useState(false); + const prevData = useRef(data); const { setConfirm } = useConfirmStore(); @@ -32,7 +34,9 @@ export default function WebBidModal({ data, onUpdated, ...props }: IWebBidModelP title: 'Update ?', message: `This web will be update`, handleOk: async () => { + setLoading(true); const result = await updateWebBid(values); + setLoading(false); if (!result) return; @@ -50,7 +54,9 @@ export default function WebBidModal({ data, onUpdated, ...props }: IWebBidModelP } else { const { url, origin_url } = values; + setLoading(true); const result = await createWebBid({ url, origin_url } as IWebBid); + setLoading(false); if (!result) return; @@ -88,6 +94,7 @@ export default function WebBidModal({ data, onUpdated, ...props }: IWebBidModelP return ( + + ); } diff --git a/auto-bid-server/src/migrations/1742778498009-create-admin-table.ts b/auto-bid-server/src/migrations/1742778498009-create-admin-table.ts index 3d82f86..20c08de 100644 --- a/auto-bid-server/src/migrations/1742778498009-create-admin-table.ts +++ b/auto-bid-server/src/migrations/1742778498009-create-admin-table.ts @@ -2,6 +2,11 @@ import { MigrationInterface, QueryRunner } from 'typeorm'; export class CreateAdminTable1742778498009 implements MigrationInterface { public async up(queryRunner: QueryRunner): Promise { + // { + // 'username': 'admin', + // 'password': 'Admin@123' + // } + await queryRunner.query(` INSERT INTO admins (email, username, password, is_system_account) VALUES ('admin@gmail.com', 'admin', '$2b$10$eF7K4Msw32e5ZC2cU78KgOqxMJygQcPDt5xXZP29inBBIV9KEsoyO', 1); diff --git a/auto-bid-server/src/modules/admins/entities/admin.entity.ts b/auto-bid-server/src/modules/admins/entities/admin.entity.ts index 745bd7f..7644bb2 100644 --- a/auto-bid-server/src/modules/admins/entities/admin.entity.ts +++ b/auto-bid-server/src/modules/admins/entities/admin.entity.ts @@ -24,6 +24,9 @@ export default class Admin extends Timestamp { @Column({ type: 'boolean', default: false }) is_system_account: boolean; - @ManyToMany(() => Permission, (permission) => permission.admins) + @ManyToMany(() => Permission, (permission) => permission.admins, { + cascade: true, + onDelete: 'CASCADE', + }) permissions: Permission[]; } diff --git a/auto-bid-tool/system/browser.js b/auto-bid-tool/system/browser.js index 66c230e..95d3a7d 100644 --- a/auto-bid-tool/system/browser.js +++ b/auto-bid-tool/system/browser.js @@ -5,19 +5,32 @@ import StealthPlugin from 'puppeteer-extra-plugin-stealth'; puppeteer.use(StealthPlugin()); const browser = await puppeteer.launch({ - headless: process.env.ENVIRONMENT === 'prod' ? true : false, + headless: process.env.ENVIRONMENT === 'prod' ? 'new' : false, // userDataDir: CONSTANTS.PROFILE_PATH, // Thư mục lưu profile timeout: 60000, args: [ '--no-sandbox', '--disable-setuid-sandbox', - '--disable-backgrounding-occluded-windows', - '--disable-renderer-backgrounding', - '--disable-ipc-flooding-protection', - '--disable-features=CalculateNativeWinOcclusion,AudioServiceOutOfProcess', - '--disable-background-timer-throttling', + '--disable-dev-shm-usage', + '--disable-gpu', + '--disable-software-rasterizer', + '--disable-background-networking', + '--disable-sync', + '--mute-audio', + '--no-first-run', + '--no-default-browser-check', '--ignore-certificate-errors', '--start-maximized', + '--disable-site-isolation-trials', // Tắt sandbox riêng cho từng site + '--memory-pressure-off', // Tắt cơ chế bảo vệ bộ nhớ + '--disk-cache-size=0', // Không dùng cache để giảm bộ nhớ + '--enable-low-end-device-mode', // Kích hoạt chế độ tiết kiệm RAM + '--disable-best-effort-tasks', // Tắt tác vụ không quan trọng + '--disable-accelerated-2d-canvas', // Không dùng GPU để vẽ canvas + '--disable-threaded-animation', // Giảm animation chạy trên nhiều thread + '--disable-threaded-scrolling', // Tắt cuộn trang đa luồng + '--disable-logging', // Tắt log debug + '--blink-settings=imagesEnabled=false', // Không tải hình ảnh ], });