update view admin
This commit is contained in:
parent
4020aef7f6
commit
7cc30dc142
|
|
@ -0,0 +1,22 @@
|
||||||
|
import { ActionIcon } from "@mantine/core";
|
||||||
|
import { IconTrash } from "@tabler/icons-react";
|
||||||
|
import { useChoosesStore } from "../../lib/zustand/use-chooses-store";
|
||||||
|
import { IBid } from "../../system/type";
|
||||||
|
|
||||||
|
export interface IDeleteRowActionProps {
|
||||||
|
onClick?: () => void;
|
||||||
|
data: IBid
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function DeleteRowAction({ data,onClick }: IDeleteRowActionProps) {
|
||||||
|
|
||||||
|
|
||||||
|
const {chooses} = useChoosesStore()
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ActionIcon disabled={!chooses.some(item => item.id === data.id)} onClick={onClick} size={"sm"} color="red">
|
||||||
|
<IconTrash size={14} />
|
||||||
|
</ActionIcon>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -6,8 +6,16 @@ import { Socket } from "socket.io-client";
|
||||||
import { getImagesWorking } from "../../apis/bid";
|
import { getImagesWorking } from "../../apis/bid";
|
||||||
import { useStatusToolStore } from "../../lib/zustand/use-status-tool-store";
|
import { useStatusToolStore } from "../../lib/zustand/use-status-tool-store";
|
||||||
import { IBid, IWebBid } from "../../system/type";
|
import { IBid, IWebBid } from "../../system/type";
|
||||||
import { cn, extractDomainSmart, findNearestClosingChild, isTimeReached, stringToColor, subtractSeconds } from "../../utils";
|
import {
|
||||||
|
cn,
|
||||||
|
extractDomainSmart,
|
||||||
|
findNearestClosingChild,
|
||||||
|
isTimeReached,
|
||||||
|
stringToColor,
|
||||||
|
subtractSeconds,
|
||||||
|
} from "../../utils";
|
||||||
import ShowImageModal from "./show-image-modal";
|
import ShowImageModal from "./show-image-modal";
|
||||||
|
import { IconExternalLink, IconImageInPicture } from "@tabler/icons-react";
|
||||||
export interface IWorkingPageProps {
|
export interface IWorkingPageProps {
|
||||||
data: (IBid | IWebBid) & { type: string };
|
data: (IBid | IWebBid) & { type: string };
|
||||||
socket: Socket;
|
socket: Socket;
|
||||||
|
|
@ -110,9 +118,8 @@ export default function WorkingPage({ data, socket }: IWorkingPageProps) {
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
if (!isIBid(data)) {
|
||||||
if(!isIBid(data)){
|
console.log(data);
|
||||||
console.log(data)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -144,20 +151,24 @@ export default function WorkingPage({ data, socket }: IWorkingPageProps) {
|
||||||
<Text className="text-xs tracking-wide">{`Current price: $${data.current_price}`}</Text>
|
<Text className="text-xs tracking-wide">{`Current price: $${data.current_price}`}</Text>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<Text className="text-sm italic opacity-80">
|
<Text className="text-sm italic opacity-80">
|
||||||
{moment(lastUpdate).format("HH:mm:ss DD/MM/YYYY")}
|
{moment(lastUpdate).format("HH:mm:ss DD/MM/YYYY")}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
|
{!isIBid(data) && (
|
||||||
{!isIBid(data) && <Tooltip label={'Time to tracking'}><Text>{`TT: ${moment(subtractSeconds(findNearestClosingChild(data)?.close_time || '', data.early_tracking_seconds)).format(
|
<Tooltip label={"Time to tracking"}>
|
||||||
"HH:mm:ss DD/MM/YYYY"
|
<Text>{`TT: ${moment(
|
||||||
)}`}</Text></Tooltip>}
|
subtractSeconds(
|
||||||
|
findNearestClosingChild(data)?.close_time || "",
|
||||||
|
data.early_tracking_seconds
|
||||||
|
)
|
||||||
|
).format("HH:mm:ss DD/MM/YYYY")}`}</Text>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
|
||||||
<Box className="flex items-center gap-3">
|
<Box className="flex items-center gap-3">
|
||||||
{isIBid(data) && (
|
{isIBid(data) && (
|
||||||
<Tooltip label={'Close time'}>
|
<Tooltip label={"Close time"}>
|
||||||
<Text
|
<Text
|
||||||
style={{ fontSize: "12px" }}
|
style={{ fontSize: "12px" }}
|
||||||
className="tracking-wide"
|
className="tracking-wide"
|
||||||
|
|
@ -174,7 +185,7 @@ export default function WorkingPage({ data, socket }: IWorkingPageProps) {
|
||||||
data.web_bid?.early_tracking_seconds || 0
|
data.web_bid?.early_tracking_seconds || 0
|
||||||
)
|
)
|
||||||
) && (
|
) && (
|
||||||
<Tooltip label={'Time to tracking'}>
|
<Tooltip label={"Time to tracking"}>
|
||||||
<Text
|
<Text
|
||||||
style={{ fontSize: "12px" }}
|
style={{ fontSize: "12px" }}
|
||||||
className="tracking-wide"
|
className="tracking-wide"
|
||||||
|
|
@ -189,6 +200,7 @@ export default function WorkingPage({ data, socket }: IWorkingPageProps) {
|
||||||
</Box>
|
</Box>
|
||||||
<Box className="flex items-center gap-4">
|
<Box className="flex items-center gap-4">
|
||||||
<Button
|
<Button
|
||||||
|
rightSection={<IconImageInPicture size={14}/>}
|
||||||
size="xs"
|
size="xs"
|
||||||
color="green"
|
color="green"
|
||||||
onClick={open}
|
onClick={open}
|
||||||
|
|
@ -197,6 +209,7 @@ export default function WorkingPage({ data, socket }: IWorkingPageProps) {
|
||||||
Show
|
Show
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
|
rightSection={<IconExternalLink size={14} />}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
component="a"
|
component="a"
|
||||||
size="xs"
|
size="xs"
|
||||||
|
|
@ -218,11 +231,15 @@ export default function WorkingPage({ data, socket }: IWorkingPageProps) {
|
||||||
|
|
||||||
<Badge
|
<Badge
|
||||||
color={stringToColor(
|
color={stringToColor(
|
||||||
isIBid(data) ? extractDomainSmart(data.web_bid.origin_url) : extractDomainSmart(data.origin_url)
|
isIBid(data)
|
||||||
|
? extractDomainSmart(data.web_bid.origin_url)
|
||||||
|
: extractDomainSmart(data.origin_url)
|
||||||
)}
|
)}
|
||||||
size="xs"
|
size="xs"
|
||||||
>
|
>
|
||||||
{isIBid(data) ? extractDomainSmart(data.web_bid.origin_url) : extractDomainSmart(data.origin_url)}
|
{isIBid(data)
|
||||||
|
? extractDomainSmart(data.web_bid.origin_url)
|
||||||
|
: extractDomainSmart(data.origin_url)}
|
||||||
</Badge>
|
</Badge>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
|
||||||
|
|
@ -1,222 +1,278 @@
|
||||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||||
import { Box, Button, ComboboxItem, Dialog, Select, SelectProps, Text, TextInput, TextInputProps } from '@mantine/core';
|
import {
|
||||||
import { useForm } from '@mantine/form';
|
Box,
|
||||||
import { useDisclosure } from '@mantine/hooks';
|
Button,
|
||||||
import { IconSearch, IconX } from '@tabler/icons-react';
|
ComboboxItem,
|
||||||
import { ReactNode, useCallback, useEffect, useImperativeHandle, useMemo, useState } from 'react';
|
Dialog,
|
||||||
import { IActionData, ITableFilter, TRefTableActionFn } from './type';
|
Select,
|
||||||
import { searchKey } from './ultils';
|
SelectProps,
|
||||||
|
Text,
|
||||||
|
TextInput,
|
||||||
|
TextInputProps,
|
||||||
|
} from "@mantine/core";
|
||||||
|
import { useForm } from "@mantine/form";
|
||||||
|
import { useDisclosure } from "@mantine/hooks";
|
||||||
|
import { IconSearch, IconX } from "@tabler/icons-react";
|
||||||
|
import {
|
||||||
|
ReactNode,
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useImperativeHandle,
|
||||||
|
useMemo,
|
||||||
|
useState,
|
||||||
|
} from "react";
|
||||||
|
import { IActionData, ITableFilter, TRefTableActionFn } from "./type";
|
||||||
|
import { searchKey } from "./ultils";
|
||||||
|
|
||||||
export interface ITableActionsProps<R extends Record<string, string | number>> {
|
export interface ITableActionsProps<R extends Record<string, string | number>> {
|
||||||
actions?: IActionData[];
|
actions?: IActionData[];
|
||||||
chooses?: R[];
|
chooses?: R[];
|
||||||
showSearch?: boolean;
|
showSearch?: boolean;
|
||||||
loading?: boolean;
|
loading?: boolean;
|
||||||
showAction?: boolean;
|
showAction?: boolean;
|
||||||
initFilter?: ITableFilter<R>[];
|
initFilter?: ITableFilter<R>[];
|
||||||
searchOptions?: {
|
searchOptions?: {
|
||||||
props?: TextInputProps;
|
props?: TextInputProps;
|
||||||
render?: () => ReactNode;
|
render?: () => ReactNode;
|
||||||
};
|
};
|
||||||
refAction?: TRefTableActionFn;
|
refAction?: TRefTableActionFn;
|
||||||
selectProps?: SelectProps;
|
selectProps?: SelectProps;
|
||||||
onSearch?: (data: ITableFilter<R>[]) => void;
|
leftActionSession?: ReactNode;
|
||||||
renderComfirm?: (data: IActionData) => ReactNode;
|
onSearch?: (data: ITableFilter<R>[]) => void;
|
||||||
onCloseComfirm?: () => void;
|
renderComfirm?: (data: IActionData) => ReactNode;
|
||||||
|
onCloseComfirm?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function TableActions<R extends Record<string, string | number>>({
|
export default function TableActions<
|
||||||
showSearch = true,
|
R extends Record<string, string | number>
|
||||||
showAction = true,
|
>({
|
||||||
loading,
|
showSearch = true,
|
||||||
searchOptions,
|
showAction = true,
|
||||||
initFilter,
|
loading,
|
||||||
selectProps,
|
searchOptions,
|
||||||
actions,
|
initFilter,
|
||||||
chooses,
|
selectProps,
|
||||||
refAction,
|
actions,
|
||||||
onSearch,
|
chooses,
|
||||||
renderComfirm,
|
refAction,
|
||||||
onCloseComfirm,
|
onSearch,
|
||||||
|
renderComfirm,
|
||||||
|
leftActionSession,
|
||||||
|
onCloseComfirm,
|
||||||
}: ITableActionsProps<R>) {
|
}: ITableActionsProps<R>) {
|
||||||
const [opened, { toggle, close }] = useDisclosure(false);
|
const [opened, { toggle, close }] = useDisclosure(false);
|
||||||
|
|
||||||
const [action, setAction] = useState<IActionData | null>(null);
|
const [action, setAction] = useState<IActionData | null>(null);
|
||||||
|
|
||||||
const [isLoading, setIsLoading] = useState(loading || false);
|
const [isLoading, setIsLoading] = useState(loading || false);
|
||||||
|
|
||||||
const [selectValue, setSelectValue] = useState<string | null>(null);
|
const [selectValue, setSelectValue] = useState<string | null>(null);
|
||||||
|
|
||||||
const form = useForm<{ [searchKey]: string }>({
|
const form = useForm<{ [searchKey]: string }>({
|
||||||
initialValues: {
|
initialValues: {
|
||||||
[searchKey]: '',
|
[searchKey]: "",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleSubmit = (data: { [searchKey]: string }) => {
|
const handleSubmit = (data: { [searchKey]: string }) => {
|
||||||
const filter = { type: data[searchKey].trim(), key: searchKey } as ITableFilter<R>;
|
const filter = {
|
||||||
|
type: data[searchKey].trim(),
|
||||||
|
key: searchKey,
|
||||||
|
} as ITableFilter<R>;
|
||||||
|
|
||||||
if (onSearch) {
|
if (onSearch) {
|
||||||
onSearch(data[searchKey]?.length ? [filter] : []);
|
onSearch(data[searchKey]?.length ? [filter] : []);
|
||||||
|
}
|
||||||
|
|
||||||
|
form.reset();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClear = () => {
|
||||||
|
form.reset();
|
||||||
|
|
||||||
|
if (onSearch) {
|
||||||
|
onSearch([]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleChangeAction = (value: string | null, _option: ComboboxItem) => {
|
||||||
|
setSelectValue(value);
|
||||||
|
|
||||||
|
if (!actions) return;
|
||||||
|
|
||||||
|
const action = actions.find((action) => String(action.key) === value);
|
||||||
|
|
||||||
|
if (!action) return;
|
||||||
|
|
||||||
|
setAction(action);
|
||||||
|
|
||||||
|
if (action.comfirmAction) {
|
||||||
|
toggle();
|
||||||
|
} else {
|
||||||
|
handleCallBack(action.callback);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCallBack = useCallback(
|
||||||
|
async (callback: (chooses: R[]) => void) => {
|
||||||
|
if (callback.constructor.name === "AsyncFunction") {
|
||||||
|
try {
|
||||||
|
setIsLoading(true);
|
||||||
|
await callback((chooses || []) as R[]);
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
callback((chooses || []) as R[]);
|
||||||
|
}
|
||||||
|
|
||||||
form.reset();
|
handleClose();
|
||||||
};
|
},
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
[chooses]
|
||||||
|
);
|
||||||
|
|
||||||
const handleClear = () => {
|
const actionDataMemo = useMemo(() => {
|
||||||
form.reset();
|
const newActions = actions?.reduce((prev, cur) => {
|
||||||
|
prev.push({
|
||||||
|
value: String(cur.key),
|
||||||
|
label: cur.title,
|
||||||
|
disabled: cur?.disabled ? cur.disabled(chooses || []) : false,
|
||||||
|
});
|
||||||
|
return prev;
|
||||||
|
}, [] as { value: string; label: string; disabled: boolean }[]);
|
||||||
|
|
||||||
if (onSearch) {
|
return newActions;
|
||||||
onSearch([]);
|
}, [actions, chooses]);
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleChangeAction = (value: string | null, _option: ComboboxItem) => {
|
const handleClose = () => {
|
||||||
setSelectValue(value);
|
close();
|
||||||
|
setAction(null);
|
||||||
|
setSelectValue(null);
|
||||||
|
|
||||||
if (!actions) return;
|
if (onCloseComfirm) {
|
||||||
|
onCloseComfirm();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const action = actions.find((action) => String(action.key) === value);
|
const handleClearAction = () => {
|
||||||
|
setAction(null);
|
||||||
|
setSelectValue(null);
|
||||||
|
};
|
||||||
|
|
||||||
if (!action) return;
|
const comfirmViewMemo = useMemo(() => {
|
||||||
|
if (!action || !action.comfirmAction) return;
|
||||||
|
|
||||||
setAction(action);
|
return renderComfirm ? (
|
||||||
|
renderComfirm(action)
|
||||||
if (action.comfirmAction) {
|
) : (
|
||||||
toggle();
|
<Dialog
|
||||||
} else {
|
opened={opened}
|
||||||
handleCallBack(action.callback);
|
withCloseButton
|
||||||
}
|
onClose={handleClose}
|
||||||
};
|
size="lg"
|
||||||
|
radius="md"
|
||||||
const handleCallBack = useCallback(
|
>
|
||||||
async (callback: (chooses: R[]) => void) => {
|
<Text size="sm" mb="xs" fw={500}>
|
||||||
if (callback.constructor.name === 'AsyncFunction') {
|
{action?.comfirmOption && action?.comfirmOption(action)?.title
|
||||||
try {
|
? action.comfirmOption(action).title
|
||||||
setIsLoading(true);
|
: "Are you sure to execute this action"}
|
||||||
await callback((chooses || []) as R[]);
|
</Text>
|
||||||
} finally {
|
<div className="flex items-center justify-end w-full gap-3">
|
||||||
setIsLoading(false);
|
<Button
|
||||||
}
|
size="xs"
|
||||||
} else {
|
disabled={isLoading}
|
||||||
callback((chooses || []) as R[]);
|
onClick={() => handleCallBack(action.callback)}
|
||||||
}
|
>
|
||||||
|
Ok
|
||||||
handleClose();
|
</Button>
|
||||||
},
|
<Button
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
size="xs"
|
||||||
[chooses],
|
disabled={isLoading}
|
||||||
|
color="red"
|
||||||
|
onClick={handleClose}
|
||||||
|
>
|
||||||
|
Close
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Dialog>
|
||||||
);
|
);
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [action, opened, close, renderComfirm]);
|
||||||
|
|
||||||
const actionDataMemo = useMemo(() => {
|
useEffect(() => {
|
||||||
const newActions = actions?.reduce((prev, cur) => {
|
if (!initFilter) return;
|
||||||
prev.push({ value: String(cur.key), label: cur.title, disabled: cur?.disabled ? cur.disabled(chooses || []) : false });
|
|
||||||
return prev;
|
|
||||||
}, [] as { value: string; label: string; disabled: boolean }[]);
|
|
||||||
|
|
||||||
return newActions;
|
const params = initFilter.reduce((prev, cur) => {
|
||||||
}, [actions, chooses]);
|
if (cur.key === searchKey) {
|
||||||
|
prev[cur.key] = cur.type;
|
||||||
|
}
|
||||||
|
return prev;
|
||||||
|
}, {} as Record<string, string | number>);
|
||||||
|
|
||||||
const handleClose = () => {
|
form.setValues(params);
|
||||||
close();
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
setAction(null);
|
}, [initFilter]);
|
||||||
setSelectValue(null);
|
|
||||||
|
|
||||||
if (onCloseComfirm) {
|
useEffect(() => {
|
||||||
onCloseComfirm();
|
setIsLoading(!!loading);
|
||||||
}
|
}, [loading]);
|
||||||
};
|
|
||||||
|
|
||||||
const handleClearAction = () => {
|
useImperativeHandle(
|
||||||
setAction(null);
|
refAction,
|
||||||
setSelectValue(null);
|
() => {
|
||||||
};
|
return {
|
||||||
|
setAction,
|
||||||
|
clearAction: handleClearAction,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
const comfirmViewMemo = useMemo(() => {
|
return (
|
||||||
if (!action || !action.comfirmAction) return;
|
<Box className="flex justify-between items-center">
|
||||||
|
{showSearch && searchOptions?.render ? (
|
||||||
return renderComfirm ? (
|
searchOptions.render()
|
||||||
renderComfirm(action)
|
) : (
|
||||||
) : (
|
<form onSubmit={form.onSubmit(handleSubmit)}>
|
||||||
<Dialog opened={opened} withCloseButton onClose={handleClose} size="lg" radius="md">
|
<TextInput
|
||||||
<Text size="sm" mb="xs" fw={500}>
|
{...form.getInputProps(searchKey)}
|
||||||
{action?.comfirmOption && action?.comfirmOption(action)?.title ? action.comfirmOption(action).title : 'Are you sure to execute this action'}
|
className="min-w-[260px]"
|
||||||
</Text>
|
leftSection={<IconSearch size={"14px"} />}
|
||||||
<div className="flex items-center justify-end w-full gap-3">
|
rightSection={
|
||||||
<Button size="xs" disabled={isLoading} onClick={() => handleCallBack(action.callback)}>
|
form.getValues()[searchKey].length ? (
|
||||||
Ok
|
<IconX
|
||||||
</Button>
|
onClick={handleClear}
|
||||||
<Button size="xs" disabled={isLoading} color="red" onClick={handleClose}>
|
className="cursor-pointer hover:text-red-400 select-none"
|
||||||
Close
|
size={"14px"}
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</Dialog>
|
|
||||||
);
|
|
||||||
}, [action, opened, close, renderComfirm]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!initFilter) return;
|
|
||||||
|
|
||||||
const params = initFilter.reduce((prev, cur) => {
|
|
||||||
if (cur.key === searchKey) {
|
|
||||||
prev[cur.key] = cur.type;
|
|
||||||
}
|
|
||||||
return prev;
|
|
||||||
}, {} as Record<string, string | number>);
|
|
||||||
|
|
||||||
form.setValues(params);
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [initFilter]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setIsLoading(!!loading);
|
|
||||||
}, [loading]);
|
|
||||||
|
|
||||||
useImperativeHandle(
|
|
||||||
refAction,
|
|
||||||
() => {
|
|
||||||
return {
|
|
||||||
setAction,
|
|
||||||
clearAction: handleClearAction,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box className="flex justify-between items-center">
|
|
||||||
{showSearch && searchOptions?.render ? (
|
|
||||||
searchOptions.render()
|
|
||||||
) : (
|
|
||||||
<form onSubmit={form.onSubmit(handleSubmit)}>
|
|
||||||
<TextInput
|
|
||||||
{...form.getInputProps(searchKey)}
|
|
||||||
className="min-w-[260px]"
|
|
||||||
leftSection={<IconSearch size={'14px'} />}
|
|
||||||
rightSection={
|
|
||||||
form.getValues()[searchKey].length ? <IconX onClick={handleClear} className="cursor-pointer hover:text-red-400 select-none" size={'14px'} /> : undefined
|
|
||||||
}
|
|
||||||
placeholder="Search by keyword"
|
|
||||||
size="xs"
|
|
||||||
label={'Search'}
|
|
||||||
{...searchOptions?.props}
|
|
||||||
/>
|
|
||||||
</form>
|
|
||||||
)}
|
|
||||||
{showAction && (
|
|
||||||
<Select
|
|
||||||
size="xs"
|
|
||||||
value={selectValue}
|
|
||||||
onChange={handleChangeAction}
|
|
||||||
label="Actions"
|
|
||||||
placeholder="Pick value"
|
|
||||||
defaultChecked={false}
|
|
||||||
data={actionDataMemo}
|
|
||||||
{...selectProps}
|
|
||||||
/>
|
/>
|
||||||
)}
|
) : undefined
|
||||||
|
}
|
||||||
|
placeholder="Search by keyword"
|
||||||
|
size="xs"
|
||||||
|
label={"Search"}
|
||||||
|
{...searchOptions?.props}
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
)}
|
||||||
|
{showAction && (
|
||||||
|
<Box className="flex items-end gap-4">
|
||||||
|
{leftActionSession}
|
||||||
|
|
||||||
{comfirmViewMemo}
|
<Select
|
||||||
|
size="xs"
|
||||||
|
value={selectValue}
|
||||||
|
onChange={handleChangeAction}
|
||||||
|
label="Actions"
|
||||||
|
placeholder="Pick value"
|
||||||
|
defaultChecked={false}
|
||||||
|
data={actionDataMemo}
|
||||||
|
{...selectProps}
|
||||||
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
)}
|
||||||
|
|
||||||
|
{comfirmViewMemo}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
// stores/useChooseStore.ts
|
||||||
|
import { create } from "zustand";
|
||||||
|
import { IBid } from "../../system/type";
|
||||||
|
|
||||||
|
interface ChoosesStore {
|
||||||
|
chooses: IBid[];
|
||||||
|
setChooses: (items: IBid[]) => void;
|
||||||
|
addChoose: (item: IBid) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useChoosesStore = create<ChoosesStore>((set) => ({
|
||||||
|
chooses: [],
|
||||||
|
setChooses: (items) => set({ chooses: items }),
|
||||||
|
addChoose: (item) =>
|
||||||
|
set((state) => ({
|
||||||
|
chooses: [...state.chooses, item],
|
||||||
|
})),
|
||||||
|
}));
|
||||||
|
|
@ -1,4 +1,13 @@
|
||||||
import { ActionIcon, Anchor, Badge, Box, Menu, Text, Tooltip } from "@mantine/core";
|
import {
|
||||||
|
ActionIcon,
|
||||||
|
Anchor,
|
||||||
|
Badge,
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Menu,
|
||||||
|
Text,
|
||||||
|
Tooltip,
|
||||||
|
} from "@mantine/core";
|
||||||
import { useDisclosure } from "@mantine/hooks";
|
import { useDisclosure } from "@mantine/hooks";
|
||||||
import {
|
import {
|
||||||
IconAd,
|
IconAd,
|
||||||
|
|
@ -7,7 +16,7 @@ import {
|
||||||
IconHammer,
|
IconHammer,
|
||||||
IconHistory,
|
IconHistory,
|
||||||
IconMenu,
|
IconMenu,
|
||||||
IconTrash
|
IconPlus
|
||||||
} 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";
|
||||||
|
|
@ -18,9 +27,11 @@ import {
|
||||||
ShowHistoriesBidPicklesApiModal,
|
ShowHistoriesBidPicklesApiModal,
|
||||||
ShowHistoriesModal,
|
ShowHistoriesModal,
|
||||||
} from "../components/bid";
|
} from "../components/bid";
|
||||||
|
import DeleteRowAction from "../components/bid/delete-row-action";
|
||||||
import constants, { haveHistories } from "../constant";
|
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";
|
||||||
|
|
@ -31,6 +42,8 @@ export default function Bids() {
|
||||||
|
|
||||||
const [clickData, setClickData] = useState<IBid | null>(null);
|
const [clickData, setClickData] = useState<IBid | null>(null);
|
||||||
|
|
||||||
|
const {setChooses} = useChoosesStore()
|
||||||
|
|
||||||
const { setConfirm } = useConfirmStore();
|
const { setConfirm } = useConfirmStore();
|
||||||
|
|
||||||
const [openedHistories, historiesModel] = useDisclosure(false);
|
const [openedHistories, historiesModel] = useDisclosure(false);
|
||||||
|
|
@ -52,9 +65,16 @@ export default function Bids() {
|
||||||
title: "Name",
|
title: "Name",
|
||||||
typeFilter: "text",
|
typeFilter: "text",
|
||||||
renderRow(row) {
|
renderRow(row) {
|
||||||
|
return (
|
||||||
|
<Anchor
|
||||||
return <Anchor className="text-[14px]" href={row.url} size="sm" style={{color: 'inherit'}}>{row.name}</Anchor>
|
className="text-[14px]"
|
||||||
|
href={row.url}
|
||||||
|
size="sm"
|
||||||
|
style={{ color: "inherit" }}
|
||||||
|
>
|
||||||
|
{row.name}
|
||||||
|
</Anchor>
|
||||||
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -157,7 +177,7 @@ export default function Bids() {
|
||||||
const handleDelete = (bid: IBid) => {
|
const handleDelete = (bid: IBid) => {
|
||||||
setConfirm({
|
setConfirm({
|
||||||
title: "Delete ?",
|
title: "Delete ?",
|
||||||
message: "This bid will be delete",
|
message: `This bid will be delete: ${bid.name || bid.model}`,
|
||||||
handleOk: async () => {
|
handleOk: async () => {
|
||||||
await deleteBid(bid);
|
await deleteBid(bid);
|
||||||
|
|
||||||
|
|
@ -192,7 +212,7 @@ export default function Bids() {
|
||||||
const table = useMemo(() => {
|
const table = useMemo(() => {
|
||||||
return (
|
return (
|
||||||
<Table
|
<Table
|
||||||
|
onChooses={setChooses}
|
||||||
tableChildProps={{
|
tableChildProps={{
|
||||||
trbody: {
|
trbody: {
|
||||||
className: "cursor-pointer",
|
className: "cursor-pointer",
|
||||||
|
|
@ -200,13 +220,13 @@ export default function Bids() {
|
||||||
}}
|
}}
|
||||||
actionsOptions={{
|
actionsOptions={{
|
||||||
actions: [
|
actions: [
|
||||||
{
|
// {
|
||||||
key: "add",
|
// key: "add",
|
||||||
title: "Add",
|
// title: "Add",
|
||||||
callback: () => {
|
// callback: () => {
|
||||||
bidModal.open();
|
// bidModal.open();
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
{
|
{
|
||||||
key: "delete",
|
key: "delete",
|
||||||
title: "Delete",
|
title: "Delete",
|
||||||
|
|
@ -228,6 +248,9 @@ export default function Bids() {
|
||||||
disabled: (data) => data.length <= 0,
|
disabled: (data) => data.length <= 0,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
leftActionSession: (
|
||||||
|
<Button onClick={bidModal.open} size="xs" rightSection={<IconPlus size={14}/>}>Add</Button>
|
||||||
|
)
|
||||||
}}
|
}}
|
||||||
refTableFn={refTableFn}
|
refTableFn={refTableFn}
|
||||||
striped
|
striped
|
||||||
|
|
@ -257,84 +280,88 @@ export default function Bids() {
|
||||||
title: <Box className="w-full text-center">Action</Box>,
|
title: <Box className="w-full text-center">Action</Box>,
|
||||||
body: (row) => {
|
body: (row) => {
|
||||||
return (
|
return (
|
||||||
<Menu shadow="md" width={200}>
|
<Box className="flex items-center gap-2">
|
||||||
<Menu.Target>
|
<Menu shadow="md" width={200}>
|
||||||
<Box
|
<Menu.Target>
|
||||||
onClick={(e) => e.stopPropagation()}
|
<Box
|
||||||
className="flex w-full items-center justify-center"
|
onClick={(e) => e.stopPropagation()}
|
||||||
>
|
className="flex w-full items-center justify-center"
|
||||||
<ActionIcon size="sm" variant="light">
|
>
|
||||||
<IconMenu size={14} />
|
<ActionIcon size="sm" variant="light">
|
||||||
</ActionIcon>
|
<IconMenu size={14} />
|
||||||
</Box>
|
</ActionIcon>
|
||||||
</Menu.Target>
|
</Box>
|
||||||
|
</Menu.Target>
|
||||||
|
|
||||||
<Menu.Dropdown onClick={(e) => e.stopPropagation()}>
|
<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);
|
|
||||||
historiesModel.open();
|
|
||||||
}}
|
|
||||||
leftSection={<IconHistory size={14} />}
|
|
||||||
>
|
|
||||||
Histories
|
|
||||||
</Menu.Item>
|
|
||||||
{haveHistories.includes(row?.web_bid.origin_url) && (
|
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setClickData(row);
|
setClickData(row);
|
||||||
switch (row.web_bid.origin_url) {
|
bidModal.open();
|
||||||
case constants.grays: {
|
|
||||||
historiesGraysApiModel.open();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case constants.pickles: {
|
|
||||||
historiesPicklesApiModel.open();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
historiesGraysApiModel.open();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
leftSection={<IconHammer size={14} />}
|
leftSection={<IconEdit size={14} />}
|
||||||
>
|
>
|
||||||
Bids
|
Edit
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
)}
|
|
||||||
|
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
disabled={row.status === "win-bid"}
|
onClick={() => {
|
||||||
onClick={() => handleToggleBid(row)}
|
setClickData(row);
|
||||||
leftSection={
|
historiesModel.open();
|
||||||
row.status === "biding" ? (
|
}}
|
||||||
<IconAdOff size={14} />
|
leftSection={<IconHistory size={14} />}
|
||||||
) : (
|
>
|
||||||
<IconAd size={14} />
|
Histories
|
||||||
)
|
</Menu.Item>
|
||||||
}
|
{haveHistories.includes(row?.web_bid.origin_url) && (
|
||||||
>
|
<Menu.Item
|
||||||
{row.status === "biding" ? "Disable" : "Enable"}
|
onClick={() => {
|
||||||
</Menu.Item>
|
setClickData(row);
|
||||||
|
switch (row.web_bid.origin_url) {
|
||||||
|
case constants.grays: {
|
||||||
|
historiesGraysApiModel.open();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case constants.pickles: {
|
||||||
|
historiesPicklesApiModel.open();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
historiesGraysApiModel.open();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
leftSection={<IconHammer size={14} />}
|
||||||
|
>
|
||||||
|
Bids
|
||||||
|
</Menu.Item>
|
||||||
|
)}
|
||||||
|
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
onClick={() => handleDelete(row)}
|
disabled={row.status === "win-bid"}
|
||||||
leftSection={<IconTrash color="red" size={14} />}
|
onClick={() => handleToggleBid(row)}
|
||||||
>
|
leftSection={
|
||||||
Delete
|
row.status === "biding" ? (
|
||||||
</Menu.Item>
|
<IconAdOff size={14} />
|
||||||
</Menu.Dropdown>
|
) : (
|
||||||
</Menu>
|
<IconAd size={14} />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{row.status === "biding" ? "Disable" : "Enable"}
|
||||||
|
</Menu.Item>
|
||||||
|
|
||||||
|
{/* <Menu.Item
|
||||||
|
onClick={() => handleDelete(row)}
|
||||||
|
leftSection={<IconTrash color="red" size={14} />}
|
||||||
|
>
|
||||||
|
Delete
|
||||||
|
</Menu.Item> */}
|
||||||
|
</Menu.Dropdown>
|
||||||
|
</Menu>
|
||||||
|
|
||||||
|
<DeleteRowAction data={row} onClick={() => handleDelete(row)} />
|
||||||
|
</Box>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
|
|
|
||||||
|
|
@ -419,12 +419,6 @@ const trackingLoginStatus = async () => {
|
||||||
login_status,
|
login_status,
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(
|
|
||||||
"%cindex.js:422 ehehehehehe",
|
|
||||||
"color: #007acc;",
|
|
||||||
"ehehehehehe"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Set time to update login sau 1 phút
|
// Set time to update login sau 1 phút
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const oneMinuteLater = new Date(now.getTime() + 60 * 1000);
|
const oneMinuteLater = new Date(now.getTime() + 60 * 1000);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue