update template mail
This commit is contained in:
parent
43dcfc78bb
commit
aea4169a50
|
|
@ -17,11 +17,13 @@ export default function DeleteRowAction({
|
||||||
setConfirm({
|
setConfirm({
|
||||||
handleOk: async () => {
|
handleOk: async () => {
|
||||||
const result = await deletesBid(chooses);
|
const result = await deletesBid(chooses);
|
||||||
|
|
||||||
|
console.log({ result });
|
||||||
if (!result) return;
|
if (!result) return;
|
||||||
onDeleted?.();
|
onDeleted?.();
|
||||||
},
|
},
|
||||||
title: 'Delete',
|
title: "Delete",
|
||||||
message: `This action will remove ${chooses.length} products.`
|
message: `This action will remove ${chooses.length} products.`,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,2 @@
|
||||||
export { default as ShowHistoriesModal } from './show-histories-modal';
|
export { default as ShowHistoriesModal } from "./show-histories-modal";
|
||||||
export { default as ShowHistoriesBidGraysApiModal } from './show-histories-bid-grays-api-modal';
|
export { default as BidModal } from "./bid-modal";
|
||||||
export { default as ShowHistoriesBidPicklesApiModal } from './show-histories-bid-pickles-api-modal';
|
|
||||||
export { default as BidModal } from './bid-modal';
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||||
|
|
||||||
|
import Table from "../../../lib/table/table";
|
||||||
|
import { IColumn } from "../../../lib/table/type";
|
||||||
|
import { formatTime } from "../../../utils";
|
||||||
|
export interface IGraysHistoriesViewProps {
|
||||||
|
histories: Record<string, string>[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function AllbidsHistoriesView({
|
||||||
|
histories,
|
||||||
|
}: IGraysHistoriesViewProps) {
|
||||||
|
type BidHistoryEntry = {
|
||||||
|
row_id: number;
|
||||||
|
date: string; // ISO datetime string
|
||||||
|
amount: number; // Số tiền đặt giá
|
||||||
|
proxyamount: number; // Giá proxy tối đa
|
||||||
|
bidQty: number; // Số lượng
|
||||||
|
flashBuy: boolean; // Mua ngay
|
||||||
|
proxyBid: boolean; // Có phải đấu giá tự động
|
||||||
|
instantBid: boolean; // Đặt giá ngay
|
||||||
|
userName: string; // Tên người dùng
|
||||||
|
bidBidderID: number; // ID người đặt giá
|
||||||
|
$$hashKey?: string; // Khóa nội bộ Angular (không cần thiết, có thể bỏ hoặc để optional)
|
||||||
|
};
|
||||||
|
|
||||||
|
const columns: IColumn<BidHistoryEntry>[] = [
|
||||||
|
{
|
||||||
|
title: "Username",
|
||||||
|
key: "userName",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Amount",
|
||||||
|
key: "amount",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Proxy amount",
|
||||||
|
key: "proxyamount",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Bid Qty",
|
||||||
|
key: "bidQty",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Bid at",
|
||||||
|
key: "date",
|
||||||
|
renderRow(row) {
|
||||||
|
return <span>{formatTime(row.date)}</span>;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Table
|
||||||
|
striped
|
||||||
|
highlightOnHover
|
||||||
|
withTableBorder
|
||||||
|
withColumnBorders
|
||||||
|
styleDefaultHead={{
|
||||||
|
justifyContent: "flex-start",
|
||||||
|
width: "fit-content",
|
||||||
|
}}
|
||||||
|
showFilter={false}
|
||||||
|
showActions={false}
|
||||||
|
showChooses={false}
|
||||||
|
columns={columns}
|
||||||
|
rowKey="row_id"
|
||||||
|
rows={histories as unknown as BidHistoryEntry[]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,80 @@
|
||||||
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||||
|
import { useMemo } from "react";
|
||||||
|
import { extractNumber, formatTime } from "../../../utils";
|
||||||
|
import Table from "../../../lib/table/table";
|
||||||
|
import { IColumn } from "../../../lib/table/type";
|
||||||
|
|
||||||
|
export interface IGraysHistoriesViewProps {
|
||||||
|
histories: Record<string, string>[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function GraysHistoriesView({
|
||||||
|
histories,
|
||||||
|
}: IGraysHistoriesViewProps) {
|
||||||
|
type BidHistoryEntry = {
|
||||||
|
row_id: number;
|
||||||
|
Price: string;
|
||||||
|
Quantity: number;
|
||||||
|
WinningQuantity: number;
|
||||||
|
UserShortAddress: string;
|
||||||
|
UserInitials: string;
|
||||||
|
OriginalDate: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const columns: IColumn<BidHistoryEntry>[] = [
|
||||||
|
{
|
||||||
|
title: "Bidding Details",
|
||||||
|
key: "UserInitials",
|
||||||
|
renderRow(row) {
|
||||||
|
return (
|
||||||
|
<span>{`${row["UserInitials"]} - ${row["UserShortAddress"]}`}</span>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Bid Time",
|
||||||
|
key: "OriginalDate",
|
||||||
|
renderRow(row) {
|
||||||
|
return (
|
||||||
|
<span>
|
||||||
|
{formatTime(
|
||||||
|
new Date(extractNumber(row["OriginalDate"]) || 0).toUTCString(),
|
||||||
|
"HH:mm:ss DD/MM/YYYY"
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Bid Price",
|
||||||
|
key: "Price",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Bid Qty",
|
||||||
|
key: "Quantity",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Win Qty",
|
||||||
|
key: "WinningQuantity",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Table
|
||||||
|
striped
|
||||||
|
highlightOnHover
|
||||||
|
withTableBorder
|
||||||
|
withColumnBorders
|
||||||
|
styleDefaultHead={{
|
||||||
|
justifyContent: "flex-start",
|
||||||
|
width: "fit-content",
|
||||||
|
}}
|
||||||
|
showFilter={false}
|
||||||
|
showActions={false}
|
||||||
|
showChooses={false}
|
||||||
|
columns={columns}
|
||||||
|
rowKey="row_id"
|
||||||
|
rows={histories as unknown as BidHistoryEntry[]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||||
|
import { useMemo } from "react";
|
||||||
|
import { formatTime } from "../../../utils";
|
||||||
|
import { IColumn } from "../../../lib/table/type";
|
||||||
|
import Table from "../../../lib/table/table";
|
||||||
|
|
||||||
|
export interface IGraysHistoriesViewProps {
|
||||||
|
histories: Record<string, string>[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function PicklesHistoriesView({
|
||||||
|
histories,
|
||||||
|
}: IGraysHistoriesViewProps) {
|
||||||
|
// const rows = useMemo(() => {
|
||||||
|
// return histories.map((element, index) => (
|
||||||
|
// <Table.Tr key={index}>
|
||||||
|
// <Table.Td>{element["bidderAnonName"]}</Table.Td>
|
||||||
|
// <Table.Td>{element["actualBid"]}</Table.Td>
|
||||||
|
// <Table.Td>
|
||||||
|
// {formatTime(
|
||||||
|
// new Date(element["bidTimeInMilliSeconds"]).toUTCString(),
|
||||||
|
// "HH:mm:ss DD/MM/YYYY"
|
||||||
|
// )}
|
||||||
|
// </Table.Td>
|
||||||
|
// </Table.Tr>
|
||||||
|
// ));
|
||||||
|
// }, [histories]);
|
||||||
|
|
||||||
|
type BidHistoryEntry = {
|
||||||
|
row_id: number;
|
||||||
|
bidderAnonName: string;
|
||||||
|
actualBid: number;
|
||||||
|
bidTimeInMilliSeconds: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const columns: IColumn<BidHistoryEntry>[] = [
|
||||||
|
{
|
||||||
|
title: "Bidder name",
|
||||||
|
key: "bidderAnonName",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Actual bid",
|
||||||
|
key: "actualBid",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Time",
|
||||||
|
key: "bidTimeInMilliSeconds",
|
||||||
|
renderRow(row) {
|
||||||
|
return (
|
||||||
|
<span>
|
||||||
|
{formatTime(
|
||||||
|
new Date(row["bidTimeInMilliSeconds"]).toUTCString(),
|
||||||
|
"HH:mm:ss DD/MM/YYYY"
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Table
|
||||||
|
striped
|
||||||
|
highlightOnHover
|
||||||
|
withTableBorder
|
||||||
|
withColumnBorders
|
||||||
|
styleDefaultHead={{
|
||||||
|
justifyContent: "flex-start",
|
||||||
|
width: "fit-content",
|
||||||
|
}}
|
||||||
|
showFilter={false}
|
||||||
|
showActions={false}
|
||||||
|
showChooses={false}
|
||||||
|
columns={columns}
|
||||||
|
rowKey="row_id"
|
||||||
|
rows={histories as unknown as BidHistoryEntry[]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,81 @@
|
||||||
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||||
|
import { LoadingOverlay, Modal, ModalProps } from "@mantine/core";
|
||||||
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
|
import { getDetailBidHistories } from "../../../apis/bid-histories";
|
||||||
|
import { IBid } from "../../../system/type";
|
||||||
|
import GraysHistoriesView from "./grays-histories-view";
|
||||||
|
import constants from "../../../constant";
|
||||||
|
import PicklesHistoriesView from "./pickles-histories-view";
|
||||||
|
import AllbidsHistoriesView from "./allbids-histories-view";
|
||||||
|
|
||||||
|
export interface IShowHistoriesApiModalProps extends ModalProps {
|
||||||
|
data: IBid | null;
|
||||||
|
onUpdated?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function ShowHistoriesApiModal({
|
||||||
|
data,
|
||||||
|
onUpdated,
|
||||||
|
...props
|
||||||
|
}: IShowHistoriesApiModalProps) {
|
||||||
|
const [histories, setHistories] = useState<Record<string, string>[]>([]);
|
||||||
|
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
const handleCallApi = useCallback(async () => {
|
||||||
|
if (!data?.lot_id) {
|
||||||
|
setHistories([]);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
const response = await getDetailBidHistories(data?.lot_id);
|
||||||
|
setLoading(false);
|
||||||
|
|
||||||
|
if (response.data && response.data) {
|
||||||
|
const values = (response.data as Record<string, string>[]).map(
|
||||||
|
(item, index) => {
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
row_id: String(index),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
setHistories(values);
|
||||||
|
}
|
||||||
|
}, [data]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
handleCallApi();
|
||||||
|
}, [handleCallApi]);
|
||||||
|
|
||||||
|
const generateView = useMemo(() => {
|
||||||
|
switch (data?.web_bid.origin_url) {
|
||||||
|
case constants.grays:
|
||||||
|
return <GraysHistoriesView histories={histories} />;
|
||||||
|
case constants.pickles:
|
||||||
|
return <PicklesHistoriesView histories={histories} />;
|
||||||
|
case constants.allbids:
|
||||||
|
return <AllbidsHistoriesView histories={histories} />;
|
||||||
|
}
|
||||||
|
}, [data?.web_bid.origin_url, histories]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
className="relative"
|
||||||
|
{...props}
|
||||||
|
size="xl"
|
||||||
|
title={<span className="text-xl font-bold">BIDDING HISTORY</span>}
|
||||||
|
centered
|
||||||
|
>
|
||||||
|
{generateView}
|
||||||
|
<LoadingOverlay
|
||||||
|
visible={loading}
|
||||||
|
zIndex={1000}
|
||||||
|
overlayProps={{ blur: 2 }}
|
||||||
|
/>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -1,78 +0,0 @@
|
||||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
||||||
import { LoadingOverlay, Modal, ModalProps, Table } from '@mantine/core';
|
|
||||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
||||||
import { getDetailBidHistories } from '../../apis/bid-histories';
|
|
||||||
import { IBid } from '../../system/type';
|
|
||||||
import { extractNumber, formatTime } from '../../utils';
|
|
||||||
|
|
||||||
export interface IShowHistoriesBidGraysApiModalProps extends ModalProps {
|
|
||||||
data: IBid | null;
|
|
||||||
onUpdated?: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function ShowHistoriesBidGraysApiModal({ data, onUpdated, ...props }: IShowHistoriesBidGraysApiModalProps) {
|
|
||||||
const [histories, setHistories] = useState<Record<string, string>[]>([]);
|
|
||||||
|
|
||||||
const [loading, setLoading] = useState(false);
|
|
||||||
|
|
||||||
const rows = useMemo(() => {
|
|
||||||
return histories.map((element, index) => (
|
|
||||||
<Table.Tr key={index}>
|
|
||||||
<Table.Td>{`${element['UserInitials']} - ${element['UserShortAddress']}`}</Table.Td>
|
|
||||||
<Table.Td>{formatTime(new Date(extractNumber(element['OriginalDate']) || 0).toUTCString(), 'HH:mm:ss DD/MM/YYYY')}</Table.Td>
|
|
||||||
<Table.Td>{`AU $${element['Price']}`}</Table.Td>
|
|
||||||
<Table.Td>{`${element['Quantity']}`}</Table.Td>
|
|
||||||
<Table.Td>{`${element['WinningQuantity']}`}</Table.Td>
|
|
||||||
</Table.Tr>
|
|
||||||
));
|
|
||||||
}, [histories]);
|
|
||||||
|
|
||||||
const handleCallApi = useCallback(async () => {
|
|
||||||
if (!data?.lot_id) {
|
|
||||||
setHistories([]);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setLoading(true);
|
|
||||||
const response = await getDetailBidHistories(data?.lot_id);
|
|
||||||
setLoading(false);
|
|
||||||
|
|
||||||
if (response.data && response.data) {
|
|
||||||
setHistories(response.data);
|
|
||||||
}
|
|
||||||
}, [data]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
handleCallApi();
|
|
||||||
}, [handleCallApi]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal className="relative" {...props} size="xl" title={<span className="text-xl font-bold">BIDDING HISTORY</span>} centered>
|
|
||||||
<Table striped highlightOnHover withTableBorder withColumnBorders>
|
|
||||||
<Table.Thead>
|
|
||||||
<Table.Tr>
|
|
||||||
<Table.Th>Bidding Details</Table.Th>
|
|
||||||
<Table.Th>Bid Time</Table.Th>
|
|
||||||
<Table.Th>Bid Price</Table.Th>
|
|
||||||
<Table.Th>Bid Qty</Table.Th>
|
|
||||||
<Table.Th>Win Qty</Table.Th>
|
|
||||||
</Table.Tr>
|
|
||||||
</Table.Thead>
|
|
||||||
<Table.Tbody>
|
|
||||||
{histories.length <= 0 ? (
|
|
||||||
<Table.Tr>
|
|
||||||
<Table.Td colSpan={5} className="text-center">
|
|
||||||
None
|
|
||||||
</Table.Td>
|
|
||||||
</Table.Tr>
|
|
||||||
) : (
|
|
||||||
rows
|
|
||||||
)}
|
|
||||||
</Table.Tbody>
|
|
||||||
</Table>
|
|
||||||
|
|
||||||
<LoadingOverlay visible={loading} zIndex={1000} overlayProps={{ blur: 2 }} />
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,8 +1,13 @@
|
||||||
const constants = {
|
const constants = {
|
||||||
grays: 'https://www.grays.com',
|
grays: "https://www.grays.com",
|
||||||
pickles:'https://www.pickles.com.au'
|
pickles: "https://www.pickles.com.au",
|
||||||
}
|
allbids: "https://www.allbids.com.au",
|
||||||
|
};
|
||||||
|
|
||||||
export const haveHistories = [constants.grays, constants.pickles]
|
export const haveHistories = [
|
||||||
|
constants.grays,
|
||||||
|
constants.pickles,
|
||||||
|
constants.allbids,
|
||||||
|
];
|
||||||
|
|
||||||
export default constants
|
export default constants;
|
||||||
|
|
|
||||||
|
|
@ -21,22 +21,18 @@ import {
|
||||||
} from "@tabler/icons-react";
|
} from "@tabler/icons-react";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import { useMemo, useRef, useState } from "react";
|
import { useMemo, useRef, useState } from "react";
|
||||||
import { deleteBid, deletesBid, getBids, toggleBid } from "../apis/bid";
|
import { deleteBid, getBids, toggleBid } from "../apis/bid";
|
||||||
import {
|
import { BidModal, ShowHistoriesModal } from "../components/bid";
|
||||||
BidModal,
|
import DeleteRowAction from "../components/bid/delete-row-action";
|
||||||
ShowHistoriesBidGraysApiModal,
|
import { haveHistories } from "../constant";
|
||||||
ShowHistoriesBidPicklesApiModal,
|
|
||||||
ShowHistoriesModal,
|
|
||||||
} from "../components/bid";
|
|
||||||
import constants, { haveHistories } from "../constant";
|
|
||||||
import Table from "../lib/table/table";
|
import Table from "../lib/table/table";
|
||||||
import { IColumn, TRefTableFn } from "../lib/table/type";
|
import { IColumn, TRefTableFn } from "../lib/table/type";
|
||||||
|
import { useChoosesStore } from "../lib/zustand/use-chooses-store";
|
||||||
import { useConfirmStore } from "../lib/zustand/use-confirm";
|
import { useConfirmStore } from "../lib/zustand/use-confirm";
|
||||||
import { mappingStatusColors } from "../system/constants";
|
import { mappingStatusColors } from "../system/constants";
|
||||||
import { IBid } from "../system/type";
|
import { IBid } from "../system/type";
|
||||||
import { extractDomainSmart, formatTime } from "../utils";
|
import { extractDomainSmart, formatTime } from "../utils";
|
||||||
import DeleteRowAction from "../components/bid/delete-row-action";
|
import ShowHistoriesApiModal from "../components/bid/show-histories-api/show-histories-api-modal";
|
||||||
import { useChoosesStore } from "../lib/zustand/use-chooses-store";
|
|
||||||
|
|
||||||
export default function Bids() {
|
export default function Bids() {
|
||||||
const refTableFn: TRefTableFn<IBid> = useRef({});
|
const refTableFn: TRefTableFn<IBid> = useRef({});
|
||||||
|
|
@ -47,12 +43,9 @@ export default function Bids() {
|
||||||
|
|
||||||
const { setChooses } = useChoosesStore();
|
const { setChooses } = useChoosesStore();
|
||||||
|
|
||||||
const [openedHistories, historiesModel] = useDisclosure(false);
|
const [openedHistories, historiesModal] = useDisclosure(false);
|
||||||
const [openedHistoriesGraysApi, historiesGraysApiModel] =
|
|
||||||
useDisclosure(false);
|
|
||||||
|
|
||||||
const [openedHistoriesPicklesApi, historiesPicklesApiModel] =
|
const [openedHistoriesView, openedHistoriesViewModal] = useDisclosure(false);
|
||||||
useDisclosure(false);
|
|
||||||
const [openedBid, bidModal] = useDisclosure(false);
|
const [openedBid, bidModal] = useDisclosure(false);
|
||||||
|
|
||||||
const columns: IColumn<IBid>[] = [
|
const columns: IColumn<IBid>[] = [
|
||||||
|
|
@ -84,7 +77,9 @@ export default function Bids() {
|
||||||
title: "Web",
|
title: "Web",
|
||||||
typeFilter: "none",
|
typeFilter: "none",
|
||||||
renderRow(row) {
|
renderRow(row) {
|
||||||
return <span>{extractDomainSmart(row.web_bid.origin_url)}</span>;
|
return (
|
||||||
|
<span>{extractDomainSmart(row.web_bid?.origin_url) || "None"}</span>
|
||||||
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -238,28 +233,6 @@ export default function Bids() {
|
||||||
}}
|
}}
|
||||||
actionsOptions={{
|
actionsOptions={{
|
||||||
showMainAction: false,
|
showMainAction: false,
|
||||||
actions: [
|
|
||||||
{
|
|
||||||
key: "delete",
|
|
||||||
title: "Delete",
|
|
||||||
callback: (data) => {
|
|
||||||
if (!data.length) return;
|
|
||||||
setConfirm({
|
|
||||||
title: "Delete",
|
|
||||||
message: `${data.length} will be delete`,
|
|
||||||
handleOk: async () => {
|
|
||||||
const result = await deletesBid(data);
|
|
||||||
|
|
||||||
if (!result) return;
|
|
||||||
if (refTableFn.current.fetchData) {
|
|
||||||
refTableFn.current.fetchData();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
disabled: (data) => data.length <= 0,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
leftActionSession: (
|
leftActionSession: (
|
||||||
<Box className="flex items-end gap-2">
|
<Box className="flex items-end gap-2">
|
||||||
<Button
|
<Button
|
||||||
|
|
@ -270,7 +243,11 @@ export default function Bids() {
|
||||||
Add
|
Add
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<DeleteRowAction onDeleted={refTableFn.current?.fetchData} />
|
<DeleteRowAction
|
||||||
|
onDeleted={() => {
|
||||||
|
refTableFn.current?.fetchData?.();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
),
|
),
|
||||||
}}
|
}}
|
||||||
|
|
@ -329,29 +306,17 @@ export default function Bids() {
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setClickData(row);
|
setClickData(row);
|
||||||
historiesModel.open();
|
historiesModal.open();
|
||||||
}}
|
}}
|
||||||
leftSection={<IconHistory size={14} />}
|
leftSection={<IconHistory size={14} />}
|
||||||
>
|
>
|
||||||
Histories
|
Histories
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
{haveHistories.includes(row?.web_bid.origin_url) && (
|
{haveHistories.includes(row?.web_bid?.origin_url) && (
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setClickData(row);
|
setClickData(row);
|
||||||
switch (row.web_bid.origin_url) {
|
openedHistoriesViewModal.open();
|
||||||
case constants.grays: {
|
|
||||||
historiesGraysApiModel.open();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case constants.pickles: {
|
|
||||||
historiesPicklesApiModel.open();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
historiesGraysApiModel.open();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
leftSection={<IconHammer size={14} />}
|
leftSection={<IconHammer size={14} />}
|
||||||
>
|
>
|
||||||
|
|
@ -405,7 +370,7 @@ export default function Bids() {
|
||||||
<ShowHistoriesModal
|
<ShowHistoriesModal
|
||||||
opened={openedHistories}
|
opened={openedHistories}
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
historiesModel.close();
|
historiesModal.close();
|
||||||
setClickData(null);
|
setClickData(null);
|
||||||
}}
|
}}
|
||||||
data={clickData}
|
data={clickData}
|
||||||
|
|
@ -426,28 +391,9 @@ export default function Bids() {
|
||||||
}}
|
}}
|
||||||
data={clickData}
|
data={clickData}
|
||||||
/>
|
/>
|
||||||
{/* Grays */}
|
|
||||||
{openedHistoriesGraysApi && (
|
|
||||||
<ShowHistoriesBidGraysApiModal
|
|
||||||
onUpdated={() => {
|
|
||||||
if (refTableFn.current?.fetchData) {
|
|
||||||
refTableFn.current.fetchData();
|
|
||||||
}
|
|
||||||
|
|
||||||
setClickData(null);
|
{openedHistoriesView && (
|
||||||
}}
|
<ShowHistoriesApiModal
|
||||||
opened={openedHistoriesGraysApi}
|
|
||||||
onClose={() => {
|
|
||||||
historiesGraysApiModel.close();
|
|
||||||
|
|
||||||
setClickData(null);
|
|
||||||
}}
|
|
||||||
data={clickData}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{openedHistoriesPicklesApi && (
|
|
||||||
<ShowHistoriesBidPicklesApiModal
|
|
||||||
onUpdated={() => {
|
onUpdated={() => {
|
||||||
if (refTableFn.current?.fetchData) {
|
if (refTableFn.current?.fetchData) {
|
||||||
refTableFn.current.fetchData();
|
refTableFn.current.fetchData();
|
||||||
|
|
@ -457,7 +403,7 @@ export default function Bids() {
|
||||||
}}
|
}}
|
||||||
opened={true}
|
opened={true}
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
historiesPicklesApiModel.close();
|
openedHistoriesViewModal.close();
|
||||||
setClickData(null);
|
setClickData(null);
|
||||||
}}
|
}}
|
||||||
data={clickData}
|
data={clickData}
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
{"createdAt":1747812172479}
|
{"createdAt":1747970028717}
|
||||||
|
|
@ -26,7 +26,6 @@
|
||||||
"axios": "^1.8.3",
|
"axios": "^1.8.3",
|
||||||
"bcrypt": "^5.1.1",
|
"bcrypt": "^5.1.1",
|
||||||
"bull": "^4.16.5",
|
"bull": "^4.16.5",
|
||||||
"cheerio": "^1.0.0",
|
|
||||||
"class-transformer": "^0.5.1",
|
"class-transformer": "^0.5.1",
|
||||||
"class-validator": "^0.14.1",
|
"class-validator": "^0.14.1",
|
||||||
"cookie": "^1.0.2",
|
"cookie": "^1.0.2",
|
||||||
|
|
@ -4661,7 +4660,8 @@
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
|
||||||
"integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
|
"integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
|
||||||
"license": "ISC"
|
"license": "ISC",
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"node_modules/brace-expansion": {
|
"node_modules/brace-expansion": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
|
|
@ -4968,36 +4968,12 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/cheerio": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0.tgz",
|
|
||||||
"integrity": "sha512-quS9HgjQpdaXOvsZz82Oz7uxtXiy6UIsIQcpBj7HRw2M63Skasm9qlDocAM7jNuaxdhpPU7c4kJN+gA5MCu4ww==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"cheerio-select": "^2.1.0",
|
|
||||||
"dom-serializer": "^2.0.0",
|
|
||||||
"domhandler": "^5.0.3",
|
|
||||||
"domutils": "^3.1.0",
|
|
||||||
"encoding-sniffer": "^0.2.0",
|
|
||||||
"htmlparser2": "^9.1.0",
|
|
||||||
"parse5": "^7.1.2",
|
|
||||||
"parse5-htmlparser2-tree-adapter": "^7.0.0",
|
|
||||||
"parse5-parser-stream": "^7.1.2",
|
|
||||||
"undici": "^6.19.5",
|
|
||||||
"whatwg-mimetype": "^4.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18.17"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/cheeriojs/cheerio?sponsor=1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/cheerio-select": {
|
"node_modules/cheerio-select": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz",
|
||||||
"integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==",
|
"integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==",
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
|
"optional": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"boolbase": "^1.0.0",
|
"boolbase": "^1.0.0",
|
||||||
"css-select": "^5.1.0",
|
"css-select": "^5.1.0",
|
||||||
|
|
@ -5582,6 +5558,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz",
|
||||||
"integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==",
|
"integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==",
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
|
"optional": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"boolbase": "^1.0.0",
|
"boolbase": "^1.0.0",
|
||||||
"css-what": "^6.1.0",
|
"css-what": "^6.1.0",
|
||||||
|
|
@ -5598,6 +5575,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz",
|
||||||
"integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==",
|
"integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==",
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
|
"optional": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 6"
|
"node": ">= 6"
|
||||||
},
|
},
|
||||||
|
|
@ -5873,6 +5851,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
|
||||||
"integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
|
"integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"domelementtype": "^2.3.0",
|
"domelementtype": "^2.3.0",
|
||||||
"domhandler": "^5.0.2",
|
"domhandler": "^5.0.2",
|
||||||
|
|
@ -5892,13 +5871,15 @@
|
||||||
"url": "https://github.com/sponsors/fb55"
|
"url": "https://github.com/sponsors/fb55"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "BSD-2-Clause"
|
"license": "BSD-2-Clause",
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"node_modules/domhandler": {
|
"node_modules/domhandler": {
|
||||||
"version": "5.0.3",
|
"version": "5.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
|
||||||
"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
|
"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
|
"optional": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"domelementtype": "^2.3.0"
|
"domelementtype": "^2.3.0"
|
||||||
},
|
},
|
||||||
|
|
@ -5914,6 +5895,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz",
|
||||||
"integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==",
|
"integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==",
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
|
"optional": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"dom-serializer": "^2.0.0",
|
"dom-serializer": "^2.0.0",
|
||||||
"domelementtype": "^2.3.0",
|
"domelementtype": "^2.3.0",
|
||||||
|
|
@ -6100,31 +6082,6 @@
|
||||||
"node": ">=8.10.0"
|
"node": ">=8.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/encoding-sniffer": {
|
|
||||||
"version": "0.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.0.tgz",
|
|
||||||
"integrity": "sha512-ju7Wq1kg04I3HtiYIOrUrdfdDvkyO9s5XM8QAj/bN61Yo/Vb4vgJxy5vi4Yxk01gWHbrofpPtpxM8bKger9jhg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"iconv-lite": "^0.6.3",
|
|
||||||
"whatwg-encoding": "^3.1.1"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/fb55/encoding-sniffer?sponsor=1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/encoding-sniffer/node_modules/iconv-lite": {
|
|
||||||
"version": "0.6.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
|
||||||
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/encoding/node_modules/iconv-lite": {
|
"node_modules/encoding/node_modules/iconv-lite": {
|
||||||
"version": "0.6.3",
|
"version": "0.6.3",
|
||||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||||
|
|
@ -6211,6 +6168,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
||||||
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
|
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
|
"optional": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.12"
|
"node": ">=0.12"
|
||||||
},
|
},
|
||||||
|
|
@ -7844,6 +7802,7 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"domelementtype": "^2.3.0",
|
"domelementtype": "^2.3.0",
|
||||||
"domhandler": "^5.0.3",
|
"domhandler": "^5.0.3",
|
||||||
|
|
@ -10921,6 +10880,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
|
||||||
"integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
|
"integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
|
"optional": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"boolbase": "^1.0.0"
|
"boolbase": "^1.0.0"
|
||||||
},
|
},
|
||||||
|
|
@ -11224,6 +11184,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz",
|
||||||
"integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==",
|
"integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"entities": "^6.0.0"
|
"entities": "^6.0.0"
|
||||||
},
|
},
|
||||||
|
|
@ -11236,6 +11197,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz",
|
||||||
"integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==",
|
"integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"domhandler": "^5.0.3",
|
"domhandler": "^5.0.3",
|
||||||
"parse5": "^7.0.0"
|
"parse5": "^7.0.0"
|
||||||
|
|
@ -11244,23 +11206,12 @@
|
||||||
"url": "https://github.com/inikulin/parse5?sponsor=1"
|
"url": "https://github.com/inikulin/parse5?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/parse5-parser-stream": {
|
|
||||||
"version": "7.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz",
|
|
||||||
"integrity": "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"parse5": "^7.0.0"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/inikulin/parse5?sponsor=1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/parse5/node_modules/entities": {
|
"node_modules/parse5/node_modules/entities": {
|
||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/entities/-/entities-6.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/entities/-/entities-6.0.0.tgz",
|
||||||
"integrity": "sha512-aKstq2TDOndCn4diEyp9Uq/Flu2i1GlLkc6XIDQSDMuaFE3OPW5OphLCyQ5SpSJZTb4reN+kTcYru5yIfXoRPw==",
|
"integrity": "sha512-aKstq2TDOndCn4diEyp9Uq/Flu2i1GlLkc6XIDQSDMuaFE3OPW5OphLCyQ5SpSJZTb4reN+kTcYru5yIfXoRPw==",
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
|
"optional": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.12"
|
"node": ">=0.12"
|
||||||
},
|
},
|
||||||
|
|
@ -14245,15 +14196,6 @@
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/undici": {
|
|
||||||
"version": "6.21.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/undici/-/undici-6.21.3.tgz",
|
|
||||||
"integrity": "sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18.17"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/undici-types": {
|
"node_modules/undici-types": {
|
||||||
"version": "6.19.8",
|
"version": "6.19.8",
|
||||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
|
||||||
|
|
@ -14726,39 +14668,6 @@
|
||||||
"url": "https://opencollective.com/webpack"
|
"url": "https://opencollective.com/webpack"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/whatwg-encoding": {
|
|
||||||
"version": "3.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz",
|
|
||||||
"integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"iconv-lite": "0.6.3"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/whatwg-encoding/node_modules/iconv-lite": {
|
|
||||||
"version": "0.6.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
|
||||||
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/whatwg-mimetype": {
|
|
||||||
"version": "4.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz",
|
|
||||||
"integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/whatwg-url": {
|
"node_modules/whatwg-url": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,6 @@
|
||||||
"axios": "^1.8.3",
|
"axios": "^1.8.3",
|
||||||
"bcrypt": "^5.1.1",
|
"bcrypt": "^5.1.1",
|
||||||
"bull": "^4.16.5",
|
"bull": "^4.16.5",
|
||||||
"cheerio": "^1.0.0",
|
|
||||||
"class-transformer": "^0.5.1",
|
"class-transformer": "^0.5.1",
|
||||||
"class-validator": "^0.14.1",
|
"class-validator": "^0.14.1",
|
||||||
"cookie": "^1.0.2",
|
"cookie": "^1.0.2",
|
||||||
|
|
|
||||||
|
|
@ -1,59 +1,51 @@
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import AppResponse from 'src/response/app-response';
|
import AppResponse from 'src/response/app-response';
|
||||||
import { Bid } from '../entities/bid.entity';
|
import { Bid } from '../entities/bid.entity';
|
||||||
import { BidsService } from '../services/bids.service';
|
import { BidsService } from '../services/bids.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class GraysApi {
|
export class AllBidsApi {
|
||||||
|
constructor(private readonly bidsService: BidsService) {}
|
||||||
|
|
||||||
constructor(private readonly bidsService: BidsService){}
|
async getHistoriesBid(lot_id: Bid['lot_id']) {
|
||||||
|
const bid = await this.bidsService.bidsRepo.findOne({
|
||||||
|
where: { lot_id },
|
||||||
async getHistoriesBid(lot_id: Bid['lot_id']) {
|
relations: { web_bid: true },
|
||||||
|
});
|
||||||
|
|
||||||
const bid= await this.bidsService.bidsRepo.findOne({where: {lot_id, }, relations: {web_bid: true}})
|
try {
|
||||||
|
switch (bid.web_bid.origin_url) {
|
||||||
|
// GRAYS
|
||||||
try {
|
case 'https://www.grays.com': {
|
||||||
|
const response = await axios({
|
||||||
switch(bid.web_bid.origin_url){
|
url: `https://www.grays.com/api/LotInfo/GetBiddingHistory?lotId=${lot_id}¤cyCode=AUD`,
|
||||||
|
});
|
||||||
// GRAYS
|
|
||||||
case 'https://www.grays.com': {
|
if (response.data && response.data?.Bids) {
|
||||||
const response = await axios({
|
return AppResponse.toResponse(response.data.Bids);
|
||||||
url: `https://www.grays.com/api/LotInfo/GetBiddingHistory?lotId=${lot_id}¤cyCode=AUD`,
|
}
|
||||||
});
|
|
||||||
|
return AppResponse.toResponse([]);
|
||||||
if (response.data && response.data?.Bids) {
|
}
|
||||||
return AppResponse.toResponse(response.data.Bids);
|
|
||||||
}
|
// PICKLES
|
||||||
|
case 'https://www.pickles.com.au': {
|
||||||
return AppResponse.toResponse([])
|
const response = await axios({
|
||||||
}
|
url: `https://www.pickles.com.au/PWR-Web/services/api/bidHistoryService/bidHistory?item=${lot_id}`,
|
||||||
|
});
|
||||||
// PICKLES
|
|
||||||
case 'https://www.pickles.com.au': {
|
if (response.data) {
|
||||||
|
return AppResponse.toResponse(response.data.Bids);
|
||||||
const response = await axios({
|
}
|
||||||
url: `https://www.pickles.com.au/PWR-Web/services/api/bidHistoryService/bidHistory?item=${lot_id}`,
|
|
||||||
});
|
return AppResponse.toResponse([]);
|
||||||
|
}
|
||||||
if (response.data) {
|
default:
|
||||||
return AppResponse.toResponse(response.data.Bids);
|
return AppResponse.toResponse([]);
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
return AppResponse.toResponse([])
|
return AppResponse.toResponse([]);
|
||||||
}
|
}
|
||||||
default:
|
}
|
||||||
return AppResponse.toResponse([])
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
return AppResponse.toResponse([]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import axios from 'axios';
|
||||||
|
import AppResponse from 'src/response/app-response';
|
||||||
|
import { Bid } from '../entities/bid.entity';
|
||||||
|
import { BidsService } from '../services/bids.service';
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class AuctionHistoresApi {
|
||||||
|
constructor(private readonly bidsService: BidsService) {}
|
||||||
|
|
||||||
|
async getHistoriesBid(lot_id: Bid['lot_id']) {
|
||||||
|
const bid = await this.bidsService.bidsRepo.findOne({
|
||||||
|
where: { lot_id },
|
||||||
|
relations: { web_bid: true, metadata: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
switch (bid.web_bid.origin_url) {
|
||||||
|
// GRAYS
|
||||||
|
case 'https://www.grays.com': {
|
||||||
|
const response = await axios({
|
||||||
|
url: `https://www.grays.com/api/LotInfo/GetBiddingHistory?lotId=${lot_id}¤cyCode=AUD`,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.data && response.data?.Bids) {
|
||||||
|
return AppResponse.toResponse(response.data.Bids);
|
||||||
|
}
|
||||||
|
|
||||||
|
return AppResponse.toResponse([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// PICKLES
|
||||||
|
case 'https://www.pickles.com.au': {
|
||||||
|
const response = await axios({
|
||||||
|
url: `https://www.pickles.com.au/PWR-Web/services/api/bidHistoryService/bidHistory?item=${lot_id}`,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.data) {
|
||||||
|
return AppResponse.toResponse(response.data.Bids);
|
||||||
|
}
|
||||||
|
|
||||||
|
return AppResponse.toResponse([]);
|
||||||
|
}
|
||||||
|
// ALLBIDS
|
||||||
|
case 'https://www.allbids.com.au': {
|
||||||
|
const data = bid.metadata.find(
|
||||||
|
(meta) => meta.key_name === 'competor_histories',
|
||||||
|
)?.value;
|
||||||
|
|
||||||
|
const sorted = _.orderBy(data, ['amount'], ['desc']);
|
||||||
|
|
||||||
|
return AppResponse.toResponse(sorted || []);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return AppResponse.toResponse([]);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return AppResponse.toResponse([]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,7 +3,7 @@ import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
import { AdminsModule } from '../admins/admins.module';
|
import { AdminsModule } from '../admins/admins.module';
|
||||||
import { NotificationModule } from '../notification/notification.module';
|
import { NotificationModule } from '../notification/notification.module';
|
||||||
import { BotTelegramApi } from './apis/bot-telegram.api';
|
import { BotTelegramApi } from './apis/bot-telegram.api';
|
||||||
import { GraysApi } from './apis/grays.api';
|
import { AuctionHistoresApi } from './apis/auction-histories.api';
|
||||||
import { AdminBidHistoriesController } from './controllers/admin/admin-bid-histories.controller';
|
import { AdminBidHistoriesController } from './controllers/admin/admin-bid-histories.controller';
|
||||||
import { AdminBidsController } from './controllers/admin/admin-bids.controller';
|
import { AdminBidsController } from './controllers/admin/admin-bids.controller';
|
||||||
import { AdminOutBidLogsController } from './controllers/admin/admin-out-bid-logs.controller';
|
import { AdminOutBidLogsController } from './controllers/admin/admin-out-bid-logs.controller';
|
||||||
|
|
@ -31,6 +31,8 @@ import { TasksService } from './services/tasks.servise';
|
||||||
import { ConfigsService } from './services/configs.service';
|
import { ConfigsService } from './services/configs.service';
|
||||||
import { Config } from './entities/configs.entity';
|
import { Config } from './entities/configs.entity';
|
||||||
import { AdminConfigsController } from './controllers/admin/admin-configs.controller';
|
import { AdminConfigsController } from './controllers/admin/admin-configs.controller';
|
||||||
|
import { BidMetadatasService } from './services/bid-metadatas.service';
|
||||||
|
import { BidMetadata } from './entities/bid-metadata.entity';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
|
|
@ -41,6 +43,7 @@ import { AdminConfigsController } from './controllers/admin/admin-configs.contro
|
||||||
WebBid,
|
WebBid,
|
||||||
SendMessageHistory,
|
SendMessageHistory,
|
||||||
Config,
|
Config,
|
||||||
|
BidMetadata,
|
||||||
]),
|
]),
|
||||||
// AuthModule,
|
// AuthModule,
|
||||||
AdminsModule,
|
AdminsModule,
|
||||||
|
|
@ -66,12 +69,13 @@ import { AdminConfigsController } from './controllers/admin/admin-configs.contro
|
||||||
OutBidLogsService,
|
OutBidLogsService,
|
||||||
WebBidsService,
|
WebBidsService,
|
||||||
BotTelegramApi,
|
BotTelegramApi,
|
||||||
GraysApi,
|
AuctionHistoresApi,
|
||||||
SendMessageHistoriesService,
|
SendMessageHistoriesService,
|
||||||
ImapService,
|
ImapService,
|
||||||
DashboardService,
|
DashboardService,
|
||||||
TasksService,
|
TasksService,
|
||||||
ConfigsService,
|
ConfigsService,
|
||||||
|
BidMetadatasService,
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
BotTelegramApi,
|
BotTelegramApi,
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,13 @@ import { CreateBidDto } from '../../dto/bid/create-bid.dto';
|
||||||
import { BidHistoriesService } from '../../services/bid-histories.service';
|
import { BidHistoriesService } from '../../services/bid-histories.service';
|
||||||
import { CreateBidHistoryDto } from '../../dto/bid-history/create-bid-history.dto';
|
import { CreateBidHistoryDto } from '../../dto/bid-history/create-bid-history.dto';
|
||||||
import { Bid } from '../../entities/bid.entity';
|
import { Bid } from '../../entities/bid.entity';
|
||||||
import { GraysApi } from '../../apis/grays.api';
|
import { AuctionHistoresApi } from '../../apis/auction-histories.api';
|
||||||
|
|
||||||
@Controller('admin/bid-histories')
|
@Controller('admin/bid-histories')
|
||||||
export class AdminBidHistoriesController {
|
export class AdminBidHistoriesController {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly bidHistoriesService: BidHistoriesService,
|
private readonly bidHistoriesService: BidHistoriesService,
|
||||||
private readonly graysApi: GraysApi,
|
private readonly auctionHistoresApi: AuctionHistoresApi,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@Post()
|
@Post()
|
||||||
|
|
@ -20,6 +20,6 @@ export class AdminBidHistoriesController {
|
||||||
|
|
||||||
@Get('detail/:lot_id')
|
@Get('detail/:lot_id')
|
||||||
async getBidHistories(@Param('lot_id') lot_id: Bid['lot_id']) {
|
async getBidHistories(@Param('lot_id') lot_id: Bid['lot_id']) {
|
||||||
return await this.graysApi.getHistoriesBid(lot_id);
|
return await this.auctionHistoresApi.getHistoriesBid(lot_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { IsNumber, IsOptional, IsString } from 'class-validator';
|
import { IsNumber, IsObject, IsOptional, IsString } from 'class-validator';
|
||||||
|
|
||||||
export class ClientUpdateBidDto {
|
export class ClientUpdateBidDto {
|
||||||
@IsString()
|
@IsString()
|
||||||
|
|
@ -24,4 +24,8 @@ export class ClientUpdateBidDto {
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
reserve_price: number;
|
reserve_price: number;
|
||||||
|
|
||||||
|
@IsObject()
|
||||||
|
@IsOptional()
|
||||||
|
metadata: Record<string, any>;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
import {
|
||||||
|
Column,
|
||||||
|
Entity,
|
||||||
|
ManyToOne,
|
||||||
|
PrimaryGeneratedColumn,
|
||||||
|
Unique,
|
||||||
|
} from 'typeorm';
|
||||||
|
import { Bid } from './bid.entity';
|
||||||
|
import { Timestamp } from './timestamp';
|
||||||
|
|
||||||
|
@Entity('bid_metadata')
|
||||||
|
@Unique(['key_name', 'bid'])
|
||||||
|
export class BidMetadata extends Timestamp {
|
||||||
|
@PrimaryGeneratedColumn('increment')
|
||||||
|
id: number;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
key_name: string;
|
||||||
|
|
||||||
|
@Column({ type: 'json' })
|
||||||
|
value: string;
|
||||||
|
|
||||||
|
@ManyToOne(() => Bid, (bid) => bid.metadata, { onDelete: 'CASCADE' })
|
||||||
|
bid: Bid;
|
||||||
|
}
|
||||||
|
|
@ -9,6 +9,7 @@ import { Timestamp } from './timestamp';
|
||||||
import { BidHistory } from './bid-history.entity';
|
import { BidHistory } from './bid-history.entity';
|
||||||
import { WebBid } from './wed-bid.entity';
|
import { WebBid } from './wed-bid.entity';
|
||||||
import { SendMessageHistory } from './send-message-histories.entity';
|
import { SendMessageHistory } from './send-message-histories.entity';
|
||||||
|
import { BidMetadata } from './bid-metadata.entity';
|
||||||
|
|
||||||
@Entity('bids')
|
@Entity('bids')
|
||||||
export class Bid extends Timestamp {
|
export class Bid extends Timestamp {
|
||||||
|
|
@ -69,4 +70,7 @@ export class Bid extends Timestamp {
|
||||||
|
|
||||||
@ManyToOne(() => WebBid, (web) => web.children, { onDelete: 'CASCADE' })
|
@ManyToOne(() => WebBid, (web) => web.children, { onDelete: 'CASCADE' })
|
||||||
web_bid: WebBid;
|
web_bid: WebBid;
|
||||||
|
|
||||||
|
@OneToMany(() => BidMetadata, (metadata) => metadata.bid)
|
||||||
|
metadata: BidMetadata[];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
import { Repository } from 'typeorm';
|
||||||
|
import { BidMetadata } from '../entities/bid-metadata.entity';
|
||||||
|
import { Bid } from '../entities/bid.entity';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class BidMetadatasService {
|
||||||
|
constructor(
|
||||||
|
@InjectRepository(BidMetadata)
|
||||||
|
readonly bidMetadataRepo: Repository<BidMetadata>,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async upsert(data: Record<string, any>, bid: Bid) {
|
||||||
|
const existingMetadata = await this.bidMetadataRepo.find({
|
||||||
|
where: { bid: { id: bid.id } },
|
||||||
|
});
|
||||||
|
|
||||||
|
const existingMap = new Map(
|
||||||
|
existingMetadata.map((item) => [item.key_name, item]),
|
||||||
|
);
|
||||||
|
|
||||||
|
const toSave: BidMetadata[] = [];
|
||||||
|
|
||||||
|
for (const [key, value] of Object.entries(data)) {
|
||||||
|
const existing = existingMap.get(key);
|
||||||
|
if (existing) {
|
||||||
|
existing.value = value;
|
||||||
|
toSave.push(existing);
|
||||||
|
} else {
|
||||||
|
toSave.push(
|
||||||
|
this.bidMetadataRepo.create({
|
||||||
|
key_name: key,
|
||||||
|
value,
|
||||||
|
bid: { id: bid.id },
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.bidMetadataRepo.save(toSave);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -32,6 +32,7 @@ import { ImageCompressionPipe } from '../pipes/image-compression-pipe';
|
||||||
import { Constant } from '../utils/constant';
|
import { Constant } from '../utils/constant';
|
||||||
import { Event } from '../utils/events';
|
import { Event } from '../utils/events';
|
||||||
import { WebBidsService } from './web-bids.service';
|
import { WebBidsService } from './web-bids.service';
|
||||||
|
import { BidMetadatasService } from './bid-metadatas.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class BidsService {
|
export class BidsService {
|
||||||
|
|
@ -41,8 +42,9 @@ export class BidsService {
|
||||||
@InjectRepository(BidHistory)
|
@InjectRepository(BidHistory)
|
||||||
readonly bidHistoriesRepo: Repository<BidHistory>,
|
readonly bidHistoriesRepo: Repository<BidHistory>,
|
||||||
private readonly webBidsService: WebBidsService,
|
private readonly webBidsService: WebBidsService,
|
||||||
private eventEmitter: EventEmitter2,
|
private readonly eventEmitter: EventEmitter2,
|
||||||
private notificationService: NotificationService,
|
private readonly notificationService: NotificationService,
|
||||||
|
private readonly bidMetadatasService: BidMetadatasService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async index(query: PaginateQuery) {
|
async index(query: PaginateQuery) {
|
||||||
|
|
@ -208,12 +210,12 @@ export class BidsService {
|
||||||
|
|
||||||
async clientUpdate(
|
async clientUpdate(
|
||||||
id: Bid['id'],
|
id: Bid['id'],
|
||||||
{ close_time, model, ...data }: ClientUpdateBidDto, // Nhận dữ liệu cập nhật
|
{ close_time, model, metadata, ...data }: ClientUpdateBidDto, // Nhận dữ liệu cập nhật
|
||||||
) {
|
) {
|
||||||
// Tìm kiếm phiên đấu giá trong database theo id
|
// Tìm kiếm phiên đấu giá trong database theo id
|
||||||
const bid = await this.bidsRepo.findOne({
|
const bid = await this.bidsRepo.findOne({
|
||||||
where: { id },
|
where: { id },
|
||||||
relations: { histories: true, web_bid: true },
|
relations: { histories: true, web_bid: true, metadata: true },
|
||||||
order: {
|
order: {
|
||||||
histories: {
|
histories: {
|
||||||
price: 'DESC',
|
price: 'DESC',
|
||||||
|
|
@ -288,6 +290,10 @@ export class BidsService {
|
||||||
updated_at: new Date(), // Cập nhật timestamp
|
updated_at: new Date(), // Cập nhật timestamp
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (metadata) {
|
||||||
|
await this.bidMetadatasService.upsert(metadata, bid);
|
||||||
|
}
|
||||||
|
|
||||||
// Phát sự kiện cập nhật toàn bộ danh sách đấu giá
|
// Phát sự kiện cập nhật toàn bộ danh sách đấu giá
|
||||||
this.emitAllBidEvent();
|
this.emitAllBidEvent();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ import { Queue } from 'bull';
|
||||||
export class MailsService {
|
export class MailsService {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly mailerService: MailerService,
|
private readonly mailerService: MailerService,
|
||||||
|
|
||||||
@InjectQueue('mail-queue') private mailQueue: Queue,
|
@InjectQueue('mail-queue') private mailQueue: Queue,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
|
@ -52,6 +53,8 @@ export class MailsService {
|
||||||
}
|
}
|
||||||
|
|
||||||
generateProductTableHTML(products: ScrapItem[]): string {
|
generateProductTableHTML(products: ScrapItem[]): string {
|
||||||
|
const from = process.env.MAIL_USER || 'no-reply@example.com';
|
||||||
|
|
||||||
if (!products.length) {
|
if (!products.length) {
|
||||||
return `
|
return `
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
|
|
@ -64,6 +67,7 @@ export class MailsService {
|
||||||
<body style="font-family: sans-serif; background: #f8f9fa; padding: 20px;">
|
<body style="font-family: sans-serif; background: #f8f9fa; padding: 20px;">
|
||||||
<h2 style="text-align: center; color: #333;">Product Listing</h2>
|
<h2 style="text-align: center; color: #333;">Product Listing</h2>
|
||||||
<p style="text-align: center; color: #666;">No matching products found for your keywords today.</p>
|
<p style="text-align: center; color: #666;">No matching products found for your keywords today.</p>
|
||||||
|
<p style="text-align: center; color: #999; font-size: 12px; margin-top: 40px;">From: ${from}</p>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
`;
|
`;
|
||||||
|
|
@ -109,6 +113,7 @@ export class MailsService {
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
<p style="text-align: center; color: #999; font-size: 12px; margin-top: 40px;">From: ${from}</p>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
`;
|
`;
|
||||||
|
|
@ -122,6 +127,7 @@ export class MailsService {
|
||||||
const max = `$${bid.max_price}`;
|
const max = `$${bid.max_price}`;
|
||||||
const submitted = `$${bid.max_price}`;
|
const submitted = `$${bid.max_price}`;
|
||||||
const nextBid = bid.max_price + bid.plus_price;
|
const nextBid = bid.max_price + bid.plus_price;
|
||||||
|
const from = process.env.MAIL_USER || 'no-reply@example.com';
|
||||||
|
|
||||||
const cardStyle = `
|
const cardStyle = `
|
||||||
max-width: 600px;
|
max-width: 600px;
|
||||||
|
|
@ -146,15 +152,16 @@ export class MailsService {
|
||||||
switch (bid.status) {
|
switch (bid.status) {
|
||||||
case 'biding':
|
case 'biding':
|
||||||
return `
|
return `
|
||||||
<div style="${cardStyle}">
|
<div style="${cardStyle}">
|
||||||
<h2 style="${headerStyle('#2c7a7b')}">✅ Auto Bid Started</h2>
|
<h2 style="${headerStyle('#2c7a7b')}">✅ Auto Bid Started</h2>
|
||||||
${renderRow('Title', title)}
|
${renderRow('Title', title)}
|
||||||
${renderRow('Max', max)}
|
${renderRow('Max', max)}
|
||||||
${renderRow('End time', endTime)}
|
${renderRow('End time', endTime)}
|
||||||
${renderRow('Competitor', competitor)}
|
${renderRow('Competitor', competitor)}
|
||||||
${renderRow('Bid submitted', submitted)}
|
${renderRow('Bid submitted', submitted)}
|
||||||
</div>
|
${renderRow('From', from)}
|
||||||
`;
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
case 'out-bid': {
|
case 'out-bid': {
|
||||||
const overLimit = bid.current_price >= nextBid;
|
const overLimit = bid.current_price >= nextBid;
|
||||||
|
|
@ -163,59 +170,64 @@ export class MailsService {
|
||||||
|
|
||||||
if (isTimeReached(bid.close_time)) {
|
if (isTimeReached(bid.close_time)) {
|
||||||
return `
|
return `
|
||||||
<div style="${cardStyle}">
|
<div style="${cardStyle}">
|
||||||
<h2 style="${headerStyle('#718096')}">⏳ Auction Ended</h2>
|
<h2 style="${headerStyle('#718096')}">⏳ Auction Ended</h2>
|
||||||
${renderRow('Title', title)}
|
${renderRow('Title', title)}
|
||||||
${renderRow('End time', endTime)}
|
${renderRow('End time', endTime)}
|
||||||
${renderRow('Final price', competitor)}
|
${renderRow('Final price', competitor)}
|
||||||
</div>
|
${renderRow('From', from)}
|
||||||
`;
|
</div>
|
||||||
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (overLimit || belowReserve) {
|
if (overLimit || belowReserve) {
|
||||||
return `
|
return `
|
||||||
<div style="${cardStyle}">
|
|
||||||
<h2 style="${headerStyle('#dd6b20')}">⚠️ Outbid (${timeExtended})</h2>
|
|
||||||
${renderRow('Title', title)}
|
|
||||||
${renderRow('Competitor', competitor)}
|
|
||||||
${renderRow('Max', max)}
|
|
||||||
${renderRow('Next bid at', `$${nextBid}`)}
|
|
||||||
${renderRow('End time', endTime)}
|
|
||||||
<p style="color:#c05621; font-weight: 600;">⚠️ Current bid exceeds your max bid.</p>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return `
|
|
||||||
<div style="${cardStyle}">
|
<div style="${cardStyle}">
|
||||||
<h2 style="${headerStyle('#e53e3e')}">🛑 Auction Canceled (${timeExtended})</h2>
|
<h2 style="${headerStyle('#dd6b20')}">⚠️ Outbid (${timeExtended})</h2>
|
||||||
${renderRow('Title', title)}
|
${renderRow('Title', title)}
|
||||||
${renderRow('Competitor', competitor)}
|
${renderRow('Competitor', competitor)}
|
||||||
${renderRow('Max', max)}
|
${renderRow('Max', max)}
|
||||||
${renderRow('Next bid at', `$${nextBid}`)}
|
${renderRow('Next bid at', `$${nextBid}`)}
|
||||||
${renderRow('End time', endTime)}
|
${renderRow('End time', endTime)}
|
||||||
<p style="color:#9b2c2c; font-weight: 600;">🛑 Auction has been canceled.</p>
|
${renderRow('From', from)}
|
||||||
|
<p style="color:#c05621; font-weight: 600;">⚠️ Current bid exceeds your max bid.</p>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `
|
||||||
|
<div style="${cardStyle}">
|
||||||
|
<h2 style="${headerStyle('#e53e3e')}">🛑 Auction Canceled (${timeExtended})</h2>
|
||||||
|
${renderRow('Title', title)}
|
||||||
|
${renderRow('Competitor', competitor)}
|
||||||
|
${renderRow('Max', max)}
|
||||||
|
${renderRow('Next bid at', `$${nextBid}`)}
|
||||||
|
${renderRow('End time', endTime)}
|
||||||
|
${renderRow('From', from)}
|
||||||
|
<p style="color:#9b2c2c; font-weight: 600;">🛑 Auction has been canceled.</p>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'win-bid':
|
case 'win-bid':
|
||||||
return `
|
return `
|
||||||
<div style="${cardStyle}">
|
<div style="${cardStyle}">
|
||||||
<h2 style="${headerStyle('#2b6cb0')}">🎉 You Won!</h2>
|
<h2 style="${headerStyle('#2b6cb0')}">🎉 You Won!</h2>
|
||||||
${renderRow('Title', title)}
|
${renderRow('Title', title)}
|
||||||
${renderRow('Price won', `$${bid.current_price}`)}
|
${renderRow('Price won', `$${bid.current_price}`)}
|
||||||
${renderRow('Max', max)}
|
${renderRow('Max', max)}
|
||||||
</div>
|
${renderRow('From', from)}
|
||||||
`;
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return `
|
return `
|
||||||
<div style="${cardStyle}">
|
<div style="${cardStyle}">
|
||||||
<h2 style="${headerStyle('#718096')}">❓ Unknown Status</h2>
|
<h2 style="${headerStyle('#718096')}">❓ Unknown Status</h2>
|
||||||
${renderRow('Title', title)}
|
${renderRow('Title', title)}
|
||||||
</div>
|
${renderRow('From', from)}
|
||||||
`;
|
</div>
|
||||||
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -227,6 +239,7 @@ export class MailsService {
|
||||||
const max = `$${bid.max_price}`;
|
const max = `$${bid.max_price}`;
|
||||||
const submitted = `$${bid.max_price}`;
|
const submitted = `$${bid.max_price}`;
|
||||||
const maxReached = bid.max_price <= bid.max_price;
|
const maxReached = bid.max_price <= bid.max_price;
|
||||||
|
const from = process.env.MAIL_USER || 'no-reply@example.com';
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
|
|
@ -300,6 +313,10 @@ export class MailsService {
|
||||||
<th>End Time</th>
|
<th>End Time</th>
|
||||||
<td>${endTime}</td>
|
<td>${endTime}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>From</th>
|
||||||
|
<td>${from}</td>
|
||||||
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ import { Injectable, Logger } from '@nestjs/common';
|
||||||
import { Cron } from '@nestjs/schedule';
|
import { Cron } from '@nestjs/schedule';
|
||||||
import * as moment from 'moment';
|
import * as moment from 'moment';
|
||||||
import { Between } from 'typeorm';
|
import { Between } from 'typeorm';
|
||||||
import { ScrapConfigsService } from './scrap-config.service';
|
|
||||||
import { ScrapItemsService } from './scrap-item-config.service';
|
import { ScrapItemsService } from './scrap-item-config.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
|
@ -87,7 +86,7 @@ export class TasksService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Cron('0 2 * * *')
|
@Cron('58 5 * * *')
|
||||||
async handleScraps() {
|
async handleScraps() {
|
||||||
const processName = 'scrape-data-keyword';
|
const processName = 'scrape-data-keyword';
|
||||||
await this.runProcessAndSendReport(processName);
|
await this.runProcessAndSendReport(processName);
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,48 @@ export class AllbidsProductBid extends ProductBid {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getHistoriesData() {
|
||||||
|
if (!this.page_context) return;
|
||||||
|
try {
|
||||||
|
// Chờ cho Angular load (có thể tùy chỉnh thời gian nếu cần)
|
||||||
|
await this.page_context.waitForFunction(
|
||||||
|
() => window.angular !== undefined
|
||||||
|
);
|
||||||
|
|
||||||
|
const historiesData = await this.page_context.evaluate(() => {
|
||||||
|
let data = null;
|
||||||
|
const elements = document.querySelectorAll(".ng-scope");
|
||||||
|
|
||||||
|
for (let i = 0; i < elements.length; i++) {
|
||||||
|
try {
|
||||||
|
const scope = angular.element(elements[i]).scope();
|
||||||
|
if (scope?.bidHistory) {
|
||||||
|
data = scope.bidHistory;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Thử lấy từ $parent nếu không thấy
|
||||||
|
if (scope?.$parent?.bidHistory) {
|
||||||
|
data = scope.$parent.bidHistory;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// Angular element có thể lỗi nếu phần tử không hợp lệ
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
});
|
||||||
|
|
||||||
|
return historiesData;
|
||||||
|
} catch (error) {
|
||||||
|
console.log(
|
||||||
|
`[${this.id}] Error in waitForApiResponse: ${error?.message}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async handleUpdateBid({
|
async handleUpdateBid({
|
||||||
lot_id,
|
lot_id,
|
||||||
close_time,
|
close_time,
|
||||||
|
|
@ -66,6 +108,7 @@ export class AllbidsProductBid extends ProductBid {
|
||||||
current_price,
|
current_price,
|
||||||
reserve_price,
|
reserve_price,
|
||||||
model,
|
model,
|
||||||
|
metadata,
|
||||||
}) {
|
}) {
|
||||||
const response = await updateBid(this.id, {
|
const response = await updateBid(this.id, {
|
||||||
lot_id,
|
lot_id,
|
||||||
|
|
@ -74,6 +117,7 @@ export class AllbidsProductBid extends ProductBid {
|
||||||
current_price,
|
current_price,
|
||||||
reserve_price: Number(reserve_price) || 0,
|
reserve_price: Number(reserve_price) || 0,
|
||||||
model,
|
model,
|
||||||
|
metadata,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response) {
|
if (response) {
|
||||||
|
|
@ -122,6 +166,8 @@ export class AllbidsProductBid extends ProductBid {
|
||||||
// 📌 Chờ phản hồi API từ trang, tối đa 10 giây
|
// 📌 Chờ phản hồi API từ trang, tối đa 10 giây
|
||||||
const result = await this.waitForApiResponse();
|
const result = await this.waitForApiResponse();
|
||||||
|
|
||||||
|
const historiesData = await this.getHistoriesData();
|
||||||
|
|
||||||
// 📌 Nếu không có dữ liệu trả về thì dừng
|
// 📌 Nếu không có dữ liệu trả về thì dừng
|
||||||
if (!result) {
|
if (!result) {
|
||||||
console.log(`⚠️ [${this.id}] No valid data received, skipping update.`);
|
console.log(`⚠️ [${this.id}] No valid data received, skipping update.`);
|
||||||
|
|
@ -140,6 +186,9 @@ export class AllbidsProductBid extends ProductBid {
|
||||||
: null,
|
: null,
|
||||||
// close_time: close_time && !this.close_time ? String(close_time) : null, // test
|
// close_time: close_time && !this.close_time ? String(close_time) : null, // test
|
||||||
name: result?.aucTitle || null,
|
name: result?.aucTitle || null,
|
||||||
|
metadata: {
|
||||||
|
competor_histories: historiesData,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
["close_time"]
|
["close_time"]
|
||||||
);
|
);
|
||||||
|
|
@ -233,12 +282,10 @@ export class AllbidsProductBid extends ProductBid {
|
||||||
|
|
||||||
const data = await this.submitBid();
|
const data = await this.submitBid();
|
||||||
|
|
||||||
console.log({ data });
|
|
||||||
|
|
||||||
await this.page_context.reload({ waitUntil: "networkidle0" });
|
await this.page_context.reload({ waitUntil: "networkidle0" });
|
||||||
|
|
||||||
const { aucUserMaxBid } = await this.waitForApiResponse();
|
const { aucUserMaxBid } = await this.waitForApiResponse();
|
||||||
console.log(`📡 [${this.id}] API Response received:`, lotData);
|
console.log(`📡 [${this.id}] API Response received:`, { aucUserMaxBid });
|
||||||
|
|
||||||
// 📌 Kiểm tra trạng thái đấu giá từ API
|
// 📌 Kiểm tra trạng thái đấu giá từ API
|
||||||
if (aucUserMaxBid == this.max_price) {
|
if (aucUserMaxBid == this.max_price) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue