501 lines
13 KiB
TypeScript
501 lines
13 KiB
TypeScript
import {
|
|
ActionIcon,
|
|
Anchor,
|
|
Badge,
|
|
Box,
|
|
Button,
|
|
Menu,
|
|
Text,
|
|
Tooltip,
|
|
} from "@mantine/core";
|
|
import { useDisclosure } from "@mantine/hooks";
|
|
import {
|
|
IconAd,
|
|
IconAdOff,
|
|
IconCode,
|
|
IconEdit,
|
|
IconHammer,
|
|
IconHistory,
|
|
IconMenu,
|
|
IconPlayerRecord,
|
|
IconPlus,
|
|
IconTrash,
|
|
} from "@tabler/icons-react";
|
|
import _ from "lodash";
|
|
import { useMemo, useRef, useState } from "react";
|
|
import { deleteBid, getBids, toggleBid } from "../apis/bid";
|
|
import {
|
|
BidModal,
|
|
RecordModal,
|
|
ResponseDemoModal,
|
|
ShowHistoriesModal,
|
|
} from "../components/bid";
|
|
import DeleteRowAction from "../components/bid/delete-row-action";
|
|
import ShowHistoriesApiModal from "../components/bid/show-histories-api/show-histories-api-modal";
|
|
import { haveHistories } from "../constant";
|
|
import Table from "../lib/table/table";
|
|
import { IColumn, TRefTableFn } from "../lib/table/type";
|
|
import { useChoosesStore } from "../lib/zustand/use-chooses-store";
|
|
import { useConfirmStore } from "../lib/zustand/use-confirm";
|
|
import { mappingStatusColors } from "../system/constants";
|
|
import { IBid } from "../system/type";
|
|
import {
|
|
extractDomainSmart,
|
|
formatTime,
|
|
getMode,
|
|
getResponseDemo,
|
|
} from "../utils";
|
|
|
|
export default function Bids() {
|
|
const refTableFn: TRefTableFn<IBid> = useRef({});
|
|
|
|
const [clickData, setClickData] = useState<IBid | null>(null);
|
|
|
|
const { setConfirm } = useConfirmStore();
|
|
|
|
const { setChooses } = useChoosesStore();
|
|
|
|
const [openedHistories, historiesModal] = useDisclosure(false);
|
|
|
|
const [openedHistoriesView, openedHistoriesViewModal] = useDisclosure(false);
|
|
const [openedBid, bidModal] = useDisclosure(false);
|
|
const [openedRecord, recordModal] = useDisclosure(false);
|
|
const [openedResponseDemo, responseDemoModal] = useDisclosure(false);
|
|
|
|
const columns: IColumn<IBid>[] = [
|
|
{
|
|
key: "id",
|
|
title: "ID",
|
|
typeFilter: "number",
|
|
},
|
|
{
|
|
key: "name",
|
|
title: "Name",
|
|
typeFilter: "text",
|
|
renderRow(row) {
|
|
return (
|
|
<Anchor
|
|
target="_blank"
|
|
className="text-[14px]"
|
|
href={row.url}
|
|
size="sm"
|
|
style={{ color: "inherit" }}
|
|
>
|
|
{row.name}
|
|
</Anchor>
|
|
);
|
|
},
|
|
},
|
|
{
|
|
key: "web_bid",
|
|
title: "Web",
|
|
typeFilter: "none",
|
|
renderRow(row) {
|
|
return (
|
|
<span>{extractDomainSmart(row.web_bid?.origin_url) || "None"}</span>
|
|
);
|
|
},
|
|
},
|
|
{
|
|
key: "lot_id",
|
|
title: "Lot ID",
|
|
typeFilter: "none",
|
|
},
|
|
{
|
|
key: "model",
|
|
title: "Model",
|
|
typeFilter: "text",
|
|
},
|
|
|
|
{
|
|
key: "plus_price",
|
|
title: "Plus price",
|
|
typeFilter: "none",
|
|
},
|
|
{
|
|
key: "max_price",
|
|
title: "Max price",
|
|
typeFilter: "none",
|
|
},
|
|
{
|
|
key: "current_price",
|
|
title: "Current price",
|
|
typeFilter: "none",
|
|
},
|
|
{
|
|
key: "reserve_price",
|
|
title: "Reserve price",
|
|
typeFilter: "none",
|
|
},
|
|
{
|
|
key: "histories",
|
|
title: "Current bid",
|
|
typeFilter: "none",
|
|
renderRow(row) {
|
|
const bidPrice = _.maxBy(row.histories, "price");
|
|
|
|
return <Text>{bidPrice ? bidPrice.price : "None"}</Text>;
|
|
},
|
|
},
|
|
{
|
|
key: "start_bid_time",
|
|
title: "Start bid",
|
|
typeFilter: "none",
|
|
renderRow(row) {
|
|
return (
|
|
<Tooltip hidden={!row.start_bid_time} label={row.start_bid_time}>
|
|
<Text size="sm">
|
|
{row.start_bid_time
|
|
? formatTime(row.start_bid_time, "HH:mm:ss DD/MM/YYYY")
|
|
: "None"}
|
|
</Text>
|
|
</Tooltip>
|
|
);
|
|
},
|
|
},
|
|
{
|
|
key: "close_time_ts",
|
|
title: "Close time",
|
|
typeFilter: "date",
|
|
renderRow(row) {
|
|
return (
|
|
<Tooltip hidden={!row.close_time} label={row.close_time}>
|
|
<Text size="sm">
|
|
{row.close_time
|
|
? formatTime(row.close_time, "HH:mm:ss DD/MM/YYYY")
|
|
: "None"}
|
|
</Text>
|
|
</Tooltip>
|
|
);
|
|
},
|
|
},
|
|
{
|
|
key: "status",
|
|
title: "Status",
|
|
typeFilter: {
|
|
type: "select",
|
|
data: [
|
|
{
|
|
label: "Biding",
|
|
value: "biding",
|
|
},
|
|
{
|
|
label: "Win bid",
|
|
value: "win-bid",
|
|
},
|
|
{
|
|
label: "Out bid",
|
|
value: "out-bid",
|
|
},
|
|
],
|
|
},
|
|
renderRow(row) {
|
|
return (
|
|
<Box className="flex items-center justify-center">
|
|
<Badge color={mappingStatusColors[row.status]} size="sm">
|
|
{row.status}
|
|
</Badge>
|
|
</Box>
|
|
);
|
|
},
|
|
},
|
|
{
|
|
key: "metadata",
|
|
title: "Mode",
|
|
typeFilter: "none",
|
|
style: {
|
|
style: {
|
|
minWidth: "100px",
|
|
},
|
|
},
|
|
renderRow(row) {
|
|
return (
|
|
<Box className="flex items-center justify-center">
|
|
<Badge
|
|
color={getMode(row) === "live" ? "teal" : "orange"}
|
|
size="sm"
|
|
>
|
|
{row.metadata.find((item) => item.key_name === "mode_key")
|
|
?.value || "Live"}
|
|
</Badge>
|
|
</Box>
|
|
);
|
|
},
|
|
},
|
|
];
|
|
|
|
const handleDelete = (bid: IBid) => {
|
|
setConfirm({
|
|
title: "Delete ?",
|
|
message: `This bid will be delete: ${bid.name || bid.model}`,
|
|
handleOk: async () => {
|
|
await deleteBid(bid);
|
|
|
|
if (refTableFn.current?.fetchData) {
|
|
refTableFn.current.fetchData();
|
|
}
|
|
},
|
|
});
|
|
};
|
|
|
|
const handleToggleBid = async (bid: IBid) => {
|
|
const isEnable =
|
|
bid.status === "biding" ? true : bid.status === "out-bid" ? false : true;
|
|
|
|
setConfirm({
|
|
title: (isEnable ? "Disable " : "Enable ") + "ID: " + bid.id,
|
|
message: "This bid will be " + (isEnable ? "disable " : "enable "),
|
|
handleOk: async () => {
|
|
await toggleBid(bid);
|
|
|
|
if (refTableFn.current?.fetchData) {
|
|
refTableFn.current.fetchData();
|
|
}
|
|
},
|
|
okButton: {
|
|
value: isEnable ? "Disable " : "Enable ",
|
|
color: isEnable ? "red" : "blue",
|
|
},
|
|
});
|
|
};
|
|
|
|
const table = useMemo(() => {
|
|
return (
|
|
<Table
|
|
onChooses={setChooses}
|
|
tableChildProps={{
|
|
trbody: {
|
|
className: "cursor-pointer",
|
|
},
|
|
}}
|
|
actionsOptions={{
|
|
showMainAction: false,
|
|
leftActionSession: (
|
|
<Box className="flex items-end gap-2">
|
|
<Button
|
|
onClick={bidModal.open}
|
|
size="xs"
|
|
rightSection={<IconPlus size={14} />}
|
|
>
|
|
Add
|
|
</Button>
|
|
|
|
<DeleteRowAction
|
|
onDeleted={() => {
|
|
refTableFn.current?.fetchData?.();
|
|
}}
|
|
/>
|
|
</Box>
|
|
),
|
|
}}
|
|
refTableFn={refTableFn}
|
|
striped
|
|
showLoading={true}
|
|
highlightOnHover
|
|
styleDefaultHead={{
|
|
justifyContent: "flex-start",
|
|
width: "fit-content",
|
|
}}
|
|
options={{
|
|
query: getBids,
|
|
pathToData: "data.data",
|
|
keyOptions: {
|
|
last_page: "lastPage",
|
|
per_page: "perPage",
|
|
from: "from",
|
|
to: "to",
|
|
total: "total",
|
|
},
|
|
}}
|
|
rows={[]}
|
|
withColumnBorders
|
|
showChooses={true}
|
|
withTableBorder
|
|
columns={columns}
|
|
actions={{
|
|
title: <Box className="w-full text-center">Action</Box>,
|
|
body: (row) => {
|
|
return (
|
|
<Box className="flex items-center gap-2">
|
|
<Menu shadow="md" width={200}>
|
|
<Menu.Target>
|
|
<Box
|
|
onClick={(e) => e.stopPropagation()}
|
|
className="flex w-full items-center justify-center"
|
|
>
|
|
<ActionIcon size="sm" variant="light">
|
|
<IconMenu size={14} />
|
|
</ActionIcon>
|
|
</Box>
|
|
</Menu.Target>
|
|
|
|
<Menu.Dropdown onClick={(e) => e.stopPropagation()}>
|
|
<Menu.Item
|
|
onClick={() => {
|
|
setClickData(row);
|
|
bidModal.open();
|
|
}}
|
|
leftSection={<IconEdit size={14} />}
|
|
>
|
|
Edit
|
|
</Menu.Item>
|
|
|
|
<Menu.Item
|
|
onClick={() => {
|
|
setClickData(row);
|
|
historiesModal.open();
|
|
}}
|
|
leftSection={<IconHistory size={14} />}
|
|
>
|
|
Histories
|
|
</Menu.Item>
|
|
{haveHistories.includes(row?.web_bid?.origin_url) && (
|
|
<Menu.Item
|
|
onClick={() => {
|
|
setClickData(row);
|
|
openedHistoriesViewModal.open();
|
|
}}
|
|
leftSection={<IconHammer size={14} />}
|
|
>
|
|
Bids
|
|
</Menu.Item>
|
|
)}
|
|
|
|
<Menu.Item
|
|
disabled={row.status === "win-bid"}
|
|
onClick={() => handleToggleBid(row)}
|
|
leftSection={
|
|
row.status === "biding" ? (
|
|
<IconAdOff size={14} />
|
|
) : (
|
|
<IconAd size={14} />
|
|
)
|
|
}
|
|
>
|
|
{row.status === "biding" ? "Disable" : "Enable"}
|
|
</Menu.Item>
|
|
|
|
<Menu.Item
|
|
onClick={() => {
|
|
setClickData(row);
|
|
|
|
if (getResponseDemo(row)) {
|
|
responseDemoModal.open();
|
|
} else {
|
|
recordModal.open();
|
|
}
|
|
}}
|
|
leftSection={
|
|
getResponseDemo(row) ? (
|
|
<IconCode size={14} />
|
|
) : (
|
|
<IconPlayerRecord size={14} />
|
|
)
|
|
}
|
|
>
|
|
{getResponseDemo(row) ? "Response demo" : "Record"}
|
|
</Menu.Item>
|
|
</Menu.Dropdown>
|
|
</Menu>
|
|
|
|
<ActionIcon
|
|
onClick={() => handleDelete(row)}
|
|
size={"sm"}
|
|
color="red"
|
|
>
|
|
<IconTrash size={14} />
|
|
</ActionIcon>
|
|
</Box>
|
|
);
|
|
},
|
|
}}
|
|
rowKey="id"
|
|
/>
|
|
);
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, []);
|
|
|
|
return (
|
|
<Box>
|
|
{table}
|
|
<ShowHistoriesModal
|
|
opened={openedHistories}
|
|
onClose={() => {
|
|
historiesModal.close();
|
|
setClickData(null);
|
|
}}
|
|
data={clickData}
|
|
/>
|
|
|
|
<BidModal
|
|
onUpdated={() => {
|
|
if (refTableFn.current?.fetchData) {
|
|
refTableFn.current.fetchData();
|
|
}
|
|
|
|
setClickData(null);
|
|
}}
|
|
opened={openedBid}
|
|
onClose={() => {
|
|
bidModal.close();
|
|
|
|
setClickData(null);
|
|
}}
|
|
data={clickData}
|
|
/>
|
|
|
|
<RecordModal
|
|
onUpdated={() => {
|
|
if (refTableFn.current?.fetchData) {
|
|
refTableFn.current.fetchData();
|
|
}
|
|
|
|
setClickData(null);
|
|
}}
|
|
opened={openedRecord}
|
|
onClose={() => {
|
|
recordModal.close();
|
|
|
|
setClickData(null);
|
|
}}
|
|
data={clickData}
|
|
/>
|
|
|
|
<ResponseDemoModal
|
|
onUpdated={() => {
|
|
if (refTableFn.current?.fetchData) {
|
|
refTableFn.current.fetchData();
|
|
}
|
|
|
|
setClickData(null);
|
|
}}
|
|
opened={openedResponseDemo}
|
|
onClose={() => {
|
|
responseDemoModal.close();
|
|
|
|
setClickData(null);
|
|
}}
|
|
data={clickData}
|
|
/>
|
|
|
|
{openedHistoriesView && (
|
|
<ShowHistoriesApiModal
|
|
onUpdated={() => {
|
|
if (refTableFn.current?.fetchData) {
|
|
refTableFn.current.fetchData();
|
|
}
|
|
|
|
setClickData(null);
|
|
}}
|
|
opened={true}
|
|
onClose={() => {
|
|
openedHistoriesViewModal.close();
|
|
setClickData(null);
|
|
}}
|
|
data={clickData}
|
|
/>
|
|
)}
|
|
</Box>
|
|
);
|
|
}
|