update view admin

This commit is contained in:
Admin 2025-05-12 15:16:00 +07:00
parent 4020aef7f6
commit 7cc30dc142
6 changed files with 427 additions and 293 deletions

View File

@ -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>
);
}

View File

@ -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>

View File

@ -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>
);
} }

View File

@ -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],
})),
}));

View File

@ -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>
); );
}, },
}} }}

View File

@ -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);