You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
348 lines
10 KiB
348 lines
10 KiB
import { useCallback, useEffect, useRef, useState } from "react";
|
|
import AddTask from "./AddTask";
|
|
import { Button, Input, Select, Space, notification } from "antd";
|
|
import TaskTable from "./TaskTable";
|
|
import { useTeamData } from "../../Hooks/Teams";
|
|
import { StepForwardOutlined, StepBackwardOutlined } from "@ant-design/icons";
|
|
import { useTasks } from "../../Hooks/Tasks";
|
|
import { TTask } from "../../types/Tasks/TTasks";
|
|
import { admin_id, role, team_id } from "../../App";
|
|
//@ts-ignore
|
|
import addicon from "../../assets/addiconpng.png";
|
|
//@ts-ignore
|
|
import refreshicon from "../../assets/refreshIcon.png";
|
|
// @ts-ignore
|
|
import IconSearch from "../../assets/searchIcon.png";
|
|
import TaskModal from "./TaskModal";
|
|
import TaskUploadModal from "./TaskUploadModal";
|
|
import { NotificationPlacement } from "antd/es/notification/interface";
|
|
import { TCall } from "../../types/CallRequests/TCall";
|
|
|
|
const { Option } = Select;
|
|
const Task = () => {
|
|
const [open, setOpen] = useState(false);
|
|
const [modalOpen, setModalOpen] = useState(false);
|
|
const [characters, setCharacters] = useState<TTask[] | undefined>();
|
|
const [team, setTeam] = useState<any>();
|
|
const [search, setSearch] = useState<string>("");
|
|
const [status, setStatus] = useState<any>();
|
|
const [page, setPage] = useState<any>(1);
|
|
const [uploadOpen, setUploadOpen] = useState(false);
|
|
|
|
let taskSocket: WebSocket;
|
|
interface newData {
|
|
callback_request: TCall;
|
|
type: string;
|
|
task: TTask;
|
|
}
|
|
|
|
const [isLive, setIslive] = useState(true);
|
|
const [socketData, setSocketData] = useState<newData>();
|
|
const connect = async () => {
|
|
try {
|
|
if (!taskSocket || taskSocket.readyState === WebSocket.CLOSED) {
|
|
taskSocket = new WebSocket(
|
|
`ws://10.10.10.45:8080/tasks/?user_id=${admin_id}`
|
|
);
|
|
// taskSocket = new WebSocket(
|
|
// `wss://api.tteld.co/tasks/?user_id=${admin_id}`
|
|
// );
|
|
|
|
taskSocket.addEventListener("open", (event) => {
|
|
console.log("open");
|
|
setIslive(true);
|
|
});
|
|
taskSocket.addEventListener("message", (event) => {
|
|
const newData: newData = JSON.parse(event.data);
|
|
setSocketData(newData);
|
|
});
|
|
taskSocket.addEventListener("error", (errorEvent) => {
|
|
console.error("WebSocket error:", errorEvent);
|
|
});
|
|
|
|
taskSocket.addEventListener("close", (event) => {
|
|
setIslive(false);
|
|
console.log("close");
|
|
});
|
|
}
|
|
} catch (err) {}
|
|
};
|
|
|
|
useEffect(() => {
|
|
connect();
|
|
});
|
|
|
|
const [api, contextHolder] = notification.useNotification();
|
|
const openNotification = useCallback(
|
|
(placement: NotificationPlacement, data: TCall) => {
|
|
console.log(data);
|
|
|
|
api.info({
|
|
message: `Driver ${data?.driver?.name} from ${data?.company?.name} company has made a call request`,
|
|
placement,
|
|
});
|
|
},
|
|
[api]
|
|
);
|
|
|
|
useEffect(() => {
|
|
if (
|
|
socketData &&
|
|
((role !== "Checker" &&
|
|
(!team || team.includes(socketData?.task?.assigned_to?.id))) ||
|
|
role === "Checker") &&
|
|
(!status || status.includes(socketData.task.status))
|
|
) {
|
|
setCharacters((prev: TTask[] | undefined) => {
|
|
if (prev && prev?.length >= 15) {
|
|
prev?.pop();
|
|
}
|
|
if (socketData.type === "task_create") {
|
|
return [socketData.task, ...(prev || [])];
|
|
} else if (socketData.type === "task_update") {
|
|
if (role !== "Checker") {
|
|
const updatedData =
|
|
prev?.filter((b: TTask) => b.id !== socketData.task.id) || [];
|
|
const data: TTask[] = [socketData.task, ...updatedData];
|
|
data.sort((a: TTask, b: TTask) => {
|
|
if (a.status === "New" && b.status === "New") {
|
|
return 0;
|
|
}
|
|
if (a.status === "New") {
|
|
return -1;
|
|
}
|
|
if (b.status === "New") {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
});
|
|
return data;
|
|
} else {
|
|
const data = (prev || []).map((b: TTask) =>
|
|
b.id === socketData.task.id ? socketData.task : b
|
|
);
|
|
return data;
|
|
}
|
|
} else if (socketData.type === "task_delete") {
|
|
const data = (prev || []).filter(
|
|
(b: TTask) => b.id !== socketData.task.id
|
|
);
|
|
return data;
|
|
} else if (socketData.type === "task_forward") {
|
|
if (role === "Checker") {
|
|
if (socketData.task.assigned_to.id === team_id) {
|
|
return [socketData.task, ...(prev || [])];
|
|
} else {
|
|
const data = (prev || []).filter(
|
|
(b: TTask) => b.id !== socketData.task.id
|
|
);
|
|
return data;
|
|
}
|
|
} else {
|
|
const updatedData =
|
|
prev?.filter((b: TTask) => b.id !== socketData.task.id) || [];
|
|
const data: TTask[] = [socketData.task, ...updatedData];
|
|
data.sort((a: TTask, b: TTask) => {
|
|
if (a.status === "New" && b.status === "New") {
|
|
return 0;
|
|
}
|
|
if (a.status === "New") {
|
|
return -1;
|
|
}
|
|
if (b.status === "New") {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
});
|
|
return data;
|
|
}
|
|
} else if (socketData.type === "callback_request") {
|
|
if (socketData?.callback_request) {
|
|
console.log("run");
|
|
|
|
openNotification("bottomRight", socketData?.callback_request);
|
|
}
|
|
}
|
|
return prev;
|
|
});
|
|
}
|
|
}, [socketData, openNotification]);
|
|
|
|
const teamData = useTeamData("");
|
|
|
|
const teamOptions: { label: string; value: any }[] | undefined =
|
|
teamData?.data?.map((item) => ({
|
|
label: item?.name,
|
|
value: item?.id,
|
|
}));
|
|
|
|
const { data, isLoading, refetch } = useTasks({
|
|
search,
|
|
status,
|
|
team,
|
|
page,
|
|
});
|
|
useEffect(() => {
|
|
if (data) {
|
|
setCharacters(data?.data);
|
|
}
|
|
}, [data]);
|
|
|
|
useEffect(() => {
|
|
setPage(1);
|
|
}, [search, status, team]);
|
|
|
|
const showModal = () => {
|
|
setOpen(true);
|
|
};
|
|
const [recordTask, setRecordTask] = useState<TTask>();
|
|
const showTaskModal = (e: TTask) => {
|
|
setRecordTask(e);
|
|
setModalOpen(true);
|
|
};
|
|
|
|
const Next = () => {
|
|
const a = Number(page) + 1;
|
|
setPage(a);
|
|
};
|
|
const Previos = () => {
|
|
Number(page);
|
|
if (page > 1) {
|
|
const a = Number(page) - 1;
|
|
setPage(a);
|
|
}
|
|
};
|
|
|
|
const timerRef = useRef<NodeJS.Timeout | null>(null);
|
|
const handleSearchChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
if (timerRef.current) {
|
|
clearTimeout(timerRef.current);
|
|
}
|
|
|
|
const searchText = e.target.value;
|
|
timerRef.current = setTimeout(() => {
|
|
setSearch(searchText);
|
|
}, 1000);
|
|
};
|
|
const theme = localStorage.getItem("theme") === "true" ? true : false;
|
|
return (
|
|
<div>
|
|
{contextHolder}
|
|
{open && <AddTask open={open} setOpen={setOpen} />}
|
|
{uploadOpen && (
|
|
<TaskUploadModal
|
|
refetch={refetch}
|
|
recordTask={recordTask}
|
|
uploadOpen={uploadOpen}
|
|
setUploadOpen={setUploadOpen}
|
|
/>
|
|
)}
|
|
{modalOpen && (
|
|
<TaskModal
|
|
uploadOpen={uploadOpen}
|
|
setUploadOpen={setUploadOpen}
|
|
recordTask={recordTask}
|
|
modalOpen={modalOpen}
|
|
setModalOpen={setModalOpen}
|
|
refetch={refetch}
|
|
/>
|
|
)}
|
|
<div className="header d-flex">
|
|
<div className="header-title d-flex">
|
|
<p className="title">Tasks</p>
|
|
{isLive ? (
|
|
<div className="d-flex" style={{ marginRight: 15 }}>
|
|
<div className="circle"></div>
|
|
<p className={!theme ? "live-p" : "live-p-dark"}>online</p>
|
|
</div>
|
|
) : (
|
|
<div className="d-flex" style={{ marginRight: 15 }}>
|
|
<div className="circle2"></div>
|
|
<p className="live-p">offline</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
<div className="d-flex">
|
|
{role !== "Checker" && (
|
|
<button className="btn-add d-flex" onClick={showModal}>
|
|
<img style={{ marginRight: 8 }} src={addicon} alt="" />
|
|
Add Task
|
|
</button>
|
|
)}
|
|
<button
|
|
className={`btn-refresh-${theme && "dark"} d-flex`}
|
|
onClick={() => {
|
|
refetch();
|
|
connect();
|
|
}}
|
|
>
|
|
<img style={{ marginRight: 8 }} src={refreshicon} alt="" />
|
|
Refresh
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div className="filter d-flex">
|
|
<div className="search-div">
|
|
<img src={IconSearch} alt="" />
|
|
<input
|
|
className={`search-input-${theme}`}
|
|
type="text"
|
|
placeholder="Search"
|
|
onChange={handleSearchChange}
|
|
/>
|
|
</div>
|
|
<Select
|
|
style={{ width: 260, marginLeft: 12 }}
|
|
placeholder="status"
|
|
onChange={(value: any) => setStatus(value)}
|
|
mode="multiple"
|
|
>
|
|
<Option value="New">New</Option>
|
|
<Option value="Checking">Checking</Option>
|
|
<Option value="Done">Done</Option>
|
|
</Select>
|
|
{role !== "Checker" && (
|
|
<Select
|
|
mode="multiple"
|
|
style={{ width: 260, marginLeft: 12 }}
|
|
placeholder="team"
|
|
onChange={(value: any) => setTeam(value)}
|
|
options={teamOptions}
|
|
/>
|
|
)}
|
|
</div>
|
|
<TaskTable
|
|
data={{ characters }}
|
|
isLoading={isLoading}
|
|
showTaskModal={showTaskModal}
|
|
/>
|
|
<Space style={{ width: "100%", marginTop: 10 }} direction="vertical">
|
|
<Space style={{ width: "100%", justifyContent: "flex-end" }} wrap>
|
|
<Button
|
|
type="primary"
|
|
icon={<StepBackwardOutlined />}
|
|
onClick={Previos}
|
|
></Button>
|
|
<Input
|
|
style={{ width: 50, textAlign: "right" }}
|
|
value={page}
|
|
onChange={(e) => {
|
|
let num = e.target.value;
|
|
if (Number(num) && num !== "0") {
|
|
setPage(num);
|
|
}
|
|
}}
|
|
/>
|
|
<Button
|
|
type="primary"
|
|
icon={<StepForwardOutlined />}
|
|
onClick={Next}
|
|
></Button>
|
|
</Space>
|
|
</Space>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default Task;
|