fast notes added and also new form invite users

main
tteld 6 months ago
parent 5eae5d0d74
commit d12ed32c04

Binary file not shown.

@ -1,23 +1,26 @@
import { message } from "antd";
import { TCall } from "../../types/CallRequests/TCall";
import instance from "../api";
import { TPagination } from "../../types/common/TPagination";
export const callController = {
async read(obj: { status: string }) {
async read(obj: { status: string; page: number; page_size: number }) {
const params = { ...obj };
if (!!obj.status) params.status = obj.status;
if (!!obj.page) params.page = obj.page;
if (!!obj.page_size) params.page_size = obj.page_size;
const { data } = await instance.get<TCall[]>(`callback-requests/`, {
params,
});
const { data } = await instance.get<TPagination<TCall[]>>(
`callback-requests/`,
{
params,
}
);
return data;
},
async callPatch(
obj: { note?: string; status?: string },
id: number
) {
async callPatch(obj: { note?: string; status?: string }, id: number) {
const params = { ...obj };
if (!!obj.note) params.note = obj.note;
if (!!obj.status) params.status = obj.status;
@ -26,7 +29,7 @@ export const callController = {
.put<TCall>(`callback-request/${id}/`, params)
.then((u) => {
setTimeout(() => {
message.success({ content: "Loaded!", duration: 2 });
message.success({ content: "Success!", duration: 2 });
}, 1000);
return u;
});

@ -1,11 +1,13 @@
import { TCompany } from "../../types/Company/TCompany";
import { TPagination } from "../../types/common/TPagination";
import instance from "../api";
import { message } from "antd";
export type TCompanyGetParams = {
name?: string;
page?: string | number;
page?: number;
is_active?: boolean;
page_size?: number;
};
export type TCompanyPutParams = {
name?: string;
@ -29,7 +31,23 @@ export const companyController = {
if (!!filterObject.is_active) params.is_active = filterObject.is_active;
if (!!filterObject.page) params.page = filterObject.page;
const { data } = await instance.get<TCompany[]>(`companies/`, { params });
const { data } = await instance.get<TCompany[]>(`companies/`, {
params,
});
return data;
},
async readPaginated(filterObject: TCompanyGetParams) {
const params = { ...filterObject };
if (!!filterObject.name) params.name = filterObject.name;
if (!!filterObject.is_active) params.is_active = filterObject.is_active;
if (!!filterObject.page) params.page = filterObject.page;
if (!!filterObject.page_size) params.page_size = filterObject.page_size;
const { data } = await instance.get<TPagination<TCompany[]>>(`companies/`, {
params,
});
return data;
},

@ -1,16 +1,19 @@
import { TCustomer } from "../../types/Customer/TCustomer";
import { TPagination } from "../../types/common/TPagination";
import instance from "../api";
import { message } from "antd";
export type TCustomerGetParams = {
name?: string;
pageSize?: string | number;
page_size?: string | number;
page?: string | number;
for_driver_request?: boolean;
is_active?: boolean;
};
export type TCustomerByCompanyGetParams = {
name?: string;
id?: string;
id?: number;
for_driver_request?: boolean;
};
export type TCustomerPutParams = {
name?: string;
@ -29,9 +32,14 @@ export const customerController = {
if (!!filterObject.name) params.name = filterObject.name;
if (!!filterObject.is_active) params.is_active = filterObject.is_active;
if (!!filterObject.page) params.page = filterObject.page;
if (!!filterObject.page) params.pageSize = filterObject.pageSize;
if (!!filterObject.page_size) params.page_size = filterObject.page_size;
if (!!filterObject.for_driver_request)
params.for_driver_request = filterObject.for_driver_request;
const { data } = await instance.get<TCustomer[]>(`customers/`, { params });
const { data } = await instance.get<TPagination<TCustomer[]>>(
`customers/`,
{ params }
);
return data;
},
@ -42,9 +50,14 @@ export const customerController = {
}
},
async customerByCompany(id: string | undefined, name: string | undefined) {
const params = { name };
async customerByCompany(
id: number | undefined,
name: string | undefined,
for_driver_request?: boolean
) {
const params = { name, for_driver_request };
if (!!name) params.name = name;
if (!!for_driver_request) params.for_driver_request = for_driver_request;
if (id) {
const { data } = await instance.get<TCustomer[]>(
`customers-by-company/${id}/`,

@ -67,6 +67,11 @@ export const prof = {
`users/my-profile/`,
{ ...params }
);
message.success({ content: "Changes saved" });
if (params.username) {
localStorage.setItem("username", params.username);
}
window.location.reload()
return data;
} catch (error: any) {
setTimeout(() => {

@ -1,10 +1,14 @@
import { message } from "antd";
import { TRequests } from "../../types/Requests/TRequests";
import instance from "../api";
import { TPagination } from "../../types/common/TPagination";
export type TRequestsGetParams = {
search?: string;
status?: string;
for_driver_request?: boolean;
page?: number;
page_size?: number;
};
export const requestsController = {
@ -13,10 +17,17 @@ export const requestsController = {
if (!!filterObject.search) params.search = filterObject.search;
if (!!filterObject.status) params.status = filterObject.status;
if (!!filterObject.page) params.page = filterObject.page;
if (!!filterObject.page_size) params.page_size = filterObject.page_size;
if (!!filterObject.for_driver_request)
params.for_driver_request = filterObject.for_driver_request;
const { data } = await instance.get<TRequests[]>(`driver-requests/`, {
params,
});
const { data } = await instance.get<TPagination<TRequests[]>>(
`driver-requests/`,
{
params,
}
);
return data;
},
@ -36,16 +47,27 @@ export const requestsController = {
});
return data;
},
async rejectPatch(obj: TRequestsGetParams, id: string | number | undefined) {
const { data } = await instance
.patch<TRequests>(`driver-request/${id}/`, obj)
.then((u) => {
setTimeout(() => {
message.success({ content: "Loaded!", duration: 2 });
}, 1000);
return u;
});
return data;
},
async delete(id: string | number | undefined) {
let res;
let error = "";
try {
const { data } = await instance
.patch(`driver-request/${id}/`, { status: "Rejected" })
.delete(`driver-request/${id}/`)
.then((u) => {
setTimeout(() => {
message.success({ content: "Rejected!", key: id, duration: 2 });
message.success({ content: "Deleted!", key: id, duration: 2 });
}, 1000);
return u;
});

@ -38,7 +38,10 @@ export const serviceController = {
} catch (error: any) {
setTimeout(() => {
message.error({
content: error?.response?.data?.title,
content:
error?.response?.data?.title ||
error?.response?.data?.points ||
"Something went wrong!",
key: 2,
duration: 2,
});

@ -1,12 +1,15 @@
import { message } from "antd";
import { TTask, TTaskHistory } from "../../types/Tasks/TTasks";
import { TPagination } from "../../types/common/TPagination";
import instance from "../api";
import { isMobile } from "../../App";
export type TTasksGetParams = {
search?: string;
status?: string;
team?: string;
page?: string;
page?: number;
page_size: number;
};
export type TTasksPutParams = {
@ -45,8 +48,9 @@ export const taskController = {
async read(filterObject: TTasksGetParams) {
const params = { ...filterObject };
if (!!filterObject.page && filterObject.page !== "0")
if (!!filterObject.page && filterObject.page !== 0)
params.page = filterObject.page;
params.page_size = isMobile ? 10 : 15;
if (!!filterObject.search) params.search = filterObject.search;
if (Array.isArray(filterObject.status)) {
params.status = filterObject.status.join(",");
@ -66,7 +70,7 @@ export const taskController = {
return data;
},
async taskOne(Id: number) {
async taskOne(Id: number | undefined) {
const { data } = await instance.get<TTask>(`task/${Id}/`);
return data;
},
@ -97,7 +101,6 @@ export const taskController = {
for (const file of formData.files) {
form.append("files", file);
}
console.log(form);
// if (Array.isArray(formData.files)) {
// params.files = formData.files;
@ -106,13 +109,17 @@ export const taskController = {
// if (!!formData.shift_update_id)
// params.shift_update_id = formData.shift_update_id;
if (!!formData.description) params.description = formData.description;
const { data } = await instance.post("attachment/", form, {
headers: {
"Content-Type": "multipart/form-data",
},
});
return data;
try {
const { data } = await instance.post("attachment/", form, {
headers: {
"Content-Type": "multipart/form-data",
},
});
message.success({ content: "Success!" });
return data;
} catch (err) {
message.error({ content: `${err}` });
}
},
async deleteTaskController(id: number) {

@ -44,7 +44,7 @@ export const userController = {
},
async userOne(Id: string | number | undefined) {
const { data }: { data: any } = await instance(`users/admin/${Id}/`);
const { data } = await instance.get<TUser>(`users/admin/${Id}/`);
return data;
},

@ -1,11 +1,11 @@
import axios from "axios";
const instance = axios.create({
baseURL: "http://10.10.10.45:8080/api/v1/",
});
// const instance = axios.create({
// baseURL: "https://api.tteld.co/api/v1/",
// baseURL: "http://10.10.10.12:8080/api/v1/",
// });
const instance = axios.create({
baseURL: "https://api.tteld.co/api/v1/",
});
const token: string | null = localStorage.getItem("access");
if (token) {

@ -13,20 +13,21 @@ export const LoginApi = async ({ username, password }: loginInterface) => {
data: { username, password },
headers: { "Content-Type": "application/json" },
});
console.log(data);
const userObject = {
id: data?.data?.id,
first_name: data?.data?.first_name,
last_name: data?.data?.last_name,
username: data?.data?.username,
timezone: data?.data?.timezone,
email: data?.data?.email,
role: data?.data?.role,
team_id: data?.data?.team_id,
team: data?.data?.team,
business: data?.data?.business,
timezone: data?.data?.timezone || data?.data?.business?.timezone,
};
const userJSON = JSON.stringify(userObject);
localStorage.setItem("user", userJSON);
localStorage.setItem("username", data?.data?.username);
localStorage.setItem("access", data?.data.access);
localStorage.setItem("refresh", data?.data.refresh);
localStorage.setItem("admin_id", data?.data.id);

@ -27,17 +27,17 @@ export const registryVerify = async (value: activateInterface) => {
timezone: data?.data.timezone,
role: data?.data.role,
};
console.log(data);
const userJSON = JSON.stringify(userObject);
localStorage.setItem("user", userJSON);
localStorage.setItem("access_token", data?.data.access_token);
localStorage.setItem("refresh_token", data?.data.refresh_token);
localStorage.setItem("access", data?.data.access);
localStorage.setItem("refresh", data?.data.refresh);
document.location.replace("/");
return status;
} catch (error) {
console.log(error);
setTimeout(() => {
message.error({ content: "Something went wrong", duration: 2 });
}, 1000);

@ -33,7 +33,6 @@ export const RegisterApi = async (value: registerInterface) => {
document.location.replace("/");
return status;
} catch (error:any) {
console.log(error);
setTimeout(() => {
message.error({ content: ' ', duration: 2 });
}, 1000);

@ -42,4 +42,4 @@ export const resetPassEmail = async (value: resetPassType) => {
message.error({ content: "Something went wrong", duration: 2 });
}, 1000);
}
};
}

@ -257,6 +257,14 @@
justify-content: end;
}
.mobile-filter {
display: flex;
align-items: end;
justify-content: space-between;
flex-direction: column;
}
.search-input-false {
border: none;
outline: none;
@ -561,7 +569,7 @@
display: flex;
align-items: center;
justify-content: space-between;
margin: 20px 24px 24px;
margin: 12px 24px 12px;
}
.TaskModal-header-dark {
display: flex;
@ -641,9 +649,9 @@
color: rgb(211, 211, 211);
}
.info-div {
margin: 16px 24px;
}
/* .info-div {
margin: 0 !important;
} */
.info-body {
border-radius: 12px;
@ -765,3 +773,21 @@
align-items: center;
justify-content: space-between;
}
.call-requests {
background: rgba(246, 137, 0, 1);
padding: 3px 5px;
border-radius: 20px;
font-size: 10px;
margin-left: 5px;
color: #000000;
}
.call-none {
display: none;
}
.ant-upload-list-text{
overflow: scroll;
height: 50px;
}

@ -1,29 +1,58 @@
import React, { useEffect, useState } from "react";
import React, { useCallback, useEffect, useState } from "react";
import "./App.css";
import { Layout, Menu, ConfigProvider, Dropdown } from "antd";
import { Layout, Menu, ConfigProvider, Dropdown, notification } from "antd";
import { Routes, Route, Navigate, useLocation } from "react-router-dom";
import { allMenu, mainItems, superItems } from "./Utils/sidebar";
import { mainItems, superItems } from "./Utils/sidebar";
import Login from "./Auth/Login";
import Notfound from "./Utils/Notfound";
import { LogoutApi } from "./API/auth/Logout";
import { Link } from "react-router-dom";
import { MenuProps } from "antd";
// @ts-ignore
import themeBtn from "./assets/theme-btn.svg";
// @ts-ignore
import avatar from "./assets/avatar-img.svg";
// @ts-ignore
import taskIcon from "./assets/tasknavicon.png";
// @ts-ignore
import companyIcon from "./assets/companynavicon.png";
// @ts-ignore
import serviceIcon from "./assets/servicenavicon.png";
// @ts-ignore
import teamIcon from "./assets/teamnavicon.png";
// @ts-ignore
import statisticIcon from "./assets/statnavicon.png";
// @ts-ignore
import updateIcon from "./assets/updatenavicon.png";
// @ts-ignore
import userIcon from "./assets/usernavicon.png";
// @ts-ignore
import driverIcon from "./assets/customersIcon.png";
// @ts-ignore
import requestIcon from "./assets/requestIcon.png";
// @ts-ignore
import callIcon from "./assets/callIcon.png";
import Register from "./Auth/Register";
import Activate from "./Auth/Activate";
import Invite from "./Auth/Invite";
import ResetPassword from "./Auth/ResetPassword";
import ResetByEmail from "./Auth/ResetByEmail";
import { NotificationPlacement } from "antd/es/notification/interface";
import { TCall } from "./types/CallRequests/TCall";
import Task from "./Components/Tasks/Tasks";
import Requests from "./Components/Requests/Requests";
import { callController } from "./API/LayoutApi/callrequests";
import Call from "./Components/CallRequests/Call";
import { dark, light } from "./Utils/styles";
const { Header, Sider, Content } = Layout;
const userJSON: any = localStorage.getItem("user");
const userObject = JSON.parse(userJSON);
export const timeZone = userObject?.timezone;
export const role = userObject?.role;
export const admin_id = localStorage.getItem("admin_id");
export const team_id = userObject?.team_id;
export const team_id = userObject?.team?.id;
export const isMobile = window.innerWidth <= 768;
const username = localStorage.getItem("username");
const App: React.FC = () => {
const isAuthenticated = localStorage.getItem("access") as string;
@ -34,6 +63,126 @@ const App: React.FC = () => {
const [theme, setTheme] = useState<any>(
localStorage.getItem("theme") === "true" ? true : false
);
const [calls, setCalls] = useState(0);
const [data, setData] = useState<any>();
useEffect(() => {
if (admin_id) {
callController
.read({ status: "Awaiting", page: 1, page_size: 100 })
.then((data: any) => {
setData(data);
});
}
}, [admin_id]);
useEffect(() => {
if (data) {
setCalls(data.data?.length);
}
}, [data]);
type MenuItem = Required<MenuProps>["items"][number];
function getItem(
label: React.ReactNode,
key: React.Key,
icon?: React.ReactNode,
children?: MenuItem[]
): MenuItem {
return {
key,
icon,
children,
label,
} as MenuItem;
}
const allMenu: MenuItem[] = [
getItem(<Link to="/">Tasks</Link>, "/", <img alt="" src={taskIcon} />),
getItem(
<Link to="companies/">Companies</Link>,
"companies/",
<img alt="" src={companyIcon} />
),
getItem(
<Link to="customers/">Drivers</Link>,
"customers/",
<img alt="" src={driverIcon} />
),
getItem(
<Link to="services/">Services</Link>,
"services/",
<img alt="" src={serviceIcon} />
),
];
if (role === "Tech Support") {
allMenu.push(
getItem(
<Link to="users/">Users</Link>,
"users/",
<img alt="" src={userIcon} />
),
getItem(
<Link to="teams/">Teams</Link>,
"teams/",
<img alt="" src={teamIcon} />
),
getItem(
<Link to="updates/">Updates</Link>,
"updates/",
<img alt="" src={updateIcon} />
),
getItem(
<Link to="requests/">Driver Requests</Link>,
"requests/",
<img alt="" src={requestIcon} />
),
getItem(
<Link to="call/">Call Requests</Link>,
"call/",
<img alt="" src={callIcon} />
)
);
}
if (role === "Owner") {
allMenu.push(
getItem(
<Link to="users/">Users</Link>,
"users/",
<img alt="" src={userIcon} />
),
getItem(
<Link to="teams/">Teams</Link>,
"teams/",
<img alt="" src={teamIcon} />
),
getItem(
<Link to="stats/">Statistics</Link>,
"stats/",
<img alt="" src={statisticIcon} />
),
getItem(
<Link to="updates/">Updates</Link>,
"updates/",
<img alt="" src={updateIcon} />
),
getItem(
<Link to="requests/">Driver Requests</Link>,
"requests/",
<img alt="" src={requestIcon} />
),
getItem(
<Link to="calls/">
Call Requests{" "}
<span className={`${calls !== 0 ? "call-requests" : "call-none"}`}>
{calls}
</span>
</Link>,
"calls/",
<img alt="" src={callIcon} />
)
);
}
useEffect(() => {
localStorage.setItem("theme", theme);
@ -46,172 +195,7 @@ const App: React.FC = () => {
const clickLogout = () => {
LogoutApi();
};
const dark = {
components: {
Table: {
colorBgContainer: "#202020",
colorText: "#BBBBBB",
headerColor: "#BBBBBB",
borderColor: "#3A3A3A",
headerSplitColor: "#3A3A3A",
rowHoverBg: "#333333",
colorBorder: "#3A3A3A",
},
Layout: {
bodyBg: "#181818",
},
Input: {
colorBgContainer: "#2A2A2A",
colorBgContainerDisabled: "#2A2A2A",
colorText: "#BBBBBB",
colorTextPlaceholder: "#BBBBBB",
colorBorder: "#3A3A3A",
colorFillSecondary: "rgba(0, 0, 0, 0.02)",
activeBorderColor: "#3A3A3A",
activeShadow: "#3A3A3A",
hoverBorderColor: "#3A3A3A",
// colorIcon: "#BBBBBB",
// colorIconHover: "#BBBBBB",
},
Select: {
colorBgContainer: "#2A2A2A",
colorText: "#BBBBBB",
colorTextPlaceholder: "#BBBBBB",
colorBorder: "rgba(150, 150, 150, 0.493)",
colorPrimaryHover: "rgba(249, 158, 44, 1)",
colorIconHover: "#BBB",
optionSelectedBg: "#2A2A2A",
colorBgElevated: "#333",
controlOutline: "none",
optionActiveBg: "#333333",
colorTextQuaternary: "#3A3A3A",
},
Button: {
colorBorderSecondary: "rgba(249, 158, 44, 1)",
colorPrimary: "rgba(249, 158, 44, 1)",
colorPrimaryHover: "#BBBBBB",
colorIcon: "rgba(249, 158, 44, 1)",
colorIconHover: "rgba(249, 158, 44, 1)",
primaryShadow: "none",
dangerShadow: "none",
colorTextDisabled: "#AAAAAA",
borderColorDisabled: "#3A3A3A",
},
// Form: {
// labelColor: "#BBBBBB",
// },
Tabs: {
itemColor: "#BBBBBB",
itemHoverColor: "#FFFFFF",
itemSelectedColor: "rgba(249, 158, 44, 1)",
colorPrimaryActive: "rgba(249, 158, 44, 1)",
inkBarColor: "rgba(249, 158, 44, 1)",
},
// Upload: {
// colorText: "#FFFFFF",
// colorInfoBgHover: "#1E1E1E",
// },
// Pagination: {
// colorText: "#BBBBBB",
// colorPrimary: "#FFFFFF",
// colorBgContainer: "#1A1A1A",
// colorBorderSecondary: "#3A3A3A",
// },
Modal: {
contentBg: "#3A3A3A",
headerBg: "#3A3A3A",
titleColor: "#FFFFFF",
colorText: "#BBBBBB",
colorBgTextActive: "#BBBBBB",
colorBgTextHover: "#BBBBBB",
},
Menu: {
darkItemSelectedBg: "#3A3A3A",
colorBgContainer: "#fff",
},
Switch: {
colorPrimary: "#565656",
colorPrimaryHover: "#737373",
},
Radio: {
colorText: "#737373",
colorBorder: "#3A3A3A",
colorPrimaryActive: "#BBBBBB",
buttonCheckedBg: "rgba(249, 158, 44, 1)",
colorPrimaryHover: "#737373",
colorPrimary: "#565656",
},
Dropdown: {
colorBgContainer: "#3A3A3A",
colorText: "#BBBBBB",
colorPrimaryHover: "#565656",
colorPrimary: "#333333",
},
DatePicker: {
colorBgContainer: "#3A3A3A",
colorBgElevated: "#3A3A3A",
colorText: "#BBBBBB",
colorTextPlaceholder: "#BBBBBB",
colorIcon: "#fff",
colorIconHover: "#fff",
colorPrimary: "rgba(249, 158, 44, 1)",
hoverBorderColor: "#BBBBBB",
},
Empty: {
colorText: "rgba(249, 158, 44, 1)",
colorTextDisabled: "rgba(249, 158, 44, 1)",
},
},
token: {
fontFamily: "Inter, sans-serif",
colorText: "#bbb",
borderRadius: 8,
},
};
const light = {
components: {
Table: {
rowHoverBg: "#bae0ff",
headerBg: "none",
colorText: "rgba(24, 26, 41, 1)",
fontWeightStrong: 500,
colorTextHeading: "rgba(161, 162, 171, 1)",
},
Select: {
colorTextPlaceholder: "rgba(155, 157, 170, 1)",
colorPrimary: "rgba(249, 158, 44, 1)",
colorPrimaryHover: "rgba(249, 158, 44, 1)",
},
Tabs: {
inkBarColor: "rgba(249, 158, 44, 1)",
itemSelectedColor: "rgba(24, 26, 41, 1)",
itemHoverColor: "rgba(24, 26, 41, 1)",
},
Input: {
hoverBorderColor: "rgba(249, 158, 44, 1)",
activeBorderColor: "rgba(249, 158, 44, 1)",
colorTextPlaceholder: "rgba(155, 157, 170, 1)",
},
Upload: {
colorPrimaryHover: "rgba(249, 158, 44, 1)",
},
Button: {
colorPrimary: "rgba(249, 158, 44, 1)",
colorPrimaryHover: "rgba(249, 158, 44, 1)",
},
Textarea: {
colorBorder: "0px 1px 3px 0px rgba(20, 22, 41, 0.1)",
},
Menu: {
darkItemSelectedBg: "rgba(255, 255, 255, 0.08)",
},
},
token: {
fontFamily: "Inter, sans-serif",
color: "#262626",
borderRadius: 8,
},
};
const rep = () => {
document.location.replace("/");
};
@ -225,6 +209,63 @@ const App: React.FC = () => {
</Menu.Item>
</Menu>
);
let taskSocket: WebSocket;
const [isLive, setIslive] = useState(true);
const [socketData, setSocketData] = useState<any>();
const connect = async () => {
try {
if (
(!taskSocket || taskSocket.readyState === WebSocket.CLOSED) &&
admin_id
) {
// taskSocket = new WebSocket(
// `ws://10.10.10.12:8080/global/?user_id=${admin_id}`
// );
taskSocket = new WebSocket(
`wss://api.tteld.co/global/?user_id=${admin_id}`
);
taskSocket.addEventListener("open", (event) => {
setIslive(true);
});
taskSocket.addEventListener("message", (event) => {
const newData: any = JSON.parse(event.data);
setSocketData(newData);
});
taskSocket.addEventListener("error", (errorEvent) => {
console.error("WebSocket error:", errorEvent);
});
taskSocket.addEventListener("close", (event) => {
setIslive(false);
});
}
} catch (err) {}
};
useEffect(() => {
connect();
}, []);
const [api, contextHolder] = notification.useNotification();
const openNotification = useCallback(
(placement: NotificationPlacement, data: TCall) => {
api.info({
message: `Driver ${data?.driver?.name} from ${data?.company?.name} company has made a call request`,
placement,
});
},
[api]
);
useEffect(() => {
if (socketData?.type === "callback_request") {
let status = socketData?.callback_request.status;
if (status === "Awaiting") {
openNotification("bottomRight", socketData?.callback_request);
setCalls((prev: any) => prev + 1);
} else if (status === "Resolved" && calls !== 0) {
setCalls((prev: any) => prev - 1);
}
}
}, [socketData, openNotification]);
return (
<ConfigProvider theme={theme === true ? dark : light}>
@ -253,91 +294,134 @@ const App: React.FC = () => {
)}
{authorized ? (
<Layout>
<Sider
theme={"dark"}
collapsible
collapsed={collapsed}
onCollapse={(value) => setCollapsed(value)}
style={{
height: "100vh",
background: theme === true ? "#202020" : "rgba(20, 22, 41, 1)",
}}
>
<p
onClick={rep}
style={{ cursor: "pointer" }}
className={collapsed ? "logo-collapsed" : "logo"}
>
TT ELD
</p>
<Menu
{contextHolder}
{isMobile ? (
<div className=""></div>
) : (
<Sider
theme={"dark"}
mode="inline"
defaultSelectedKeys={[location.pathname]}
items={allMenu}
collapsible
collapsed={collapsed}
onCollapse={(value) => setCollapsed(value)}
style={{
height: "100vh",
background:
theme === true ? "#202020" : "rgba(20, 22, 41, 1)",
color: "rgba(255, 255, 255, 0.6)",
}}
></Menu>
</Sider>
<Layout className="site-layout">
<Header
className="site-layout-background"
style={{
padding: 0,
background:
theme === true ? "#202020" : "rgba(215, 216, 224, 1)",
display: "flex",
justifyContent: "end",
alignItems: "center",
}}
>
<div
<p
onClick={rep}
style={{ cursor: "pointer" }}
className={collapsed ? "logo-collapsed" : "logo"}
>
TT ELD
</p>
<Menu
theme={"dark"}
mode="inline"
defaultSelectedKeys={[location.pathname]}
items={allMenu}
style={{
float: "right",
marginRight: "35px",
background:
theme === true ? "#202020" : "rgba(20, 22, 41, 1)",
color: "rgba(255, 255, 255, 0.6)",
}}
></Menu>
</Sider>
)}
<Layout className="site-layout">
{isMobile ? (
<Header style={{ padding: 0 }}>
<div className="demo-logo" />
<Menu
theme="dark"
mode="horizontal"
defaultSelectedKeys={["2"]}
items={allMenu}
style={{
background:
theme === true ? "#202020" : "rgba(20, 22, 41, 1)",
color: "rgba(255, 255, 255, 0.6)",
}}
/>
</Header>
) : (
<Header
className="site-layout-background"
style={{
padding: 0,
background:
theme === true ? "#202020" : "rgba(215, 216, 224, 1)",
display: "flex",
justifyContent: "end",
alignItems: "center",
alignSelf: "center",
minWidth: 150,
maxWidth: 500,
justifyContent: "space-between",
}}
>
<button
className="theme-btn"
onClick={(e) => setTheme(!theme)}
<div
style={{
float: "right",
marginRight: "35px",
display: "flex",
alignItems: "center",
alignSelf: "center",
minWidth: 150,
maxWidth: 500,
justifyContent: "space-between",
}}
>
<img src={themeBtn} alt="" />
</button>
<Dropdown overlay={menu} trigger={["click"]}>
<div
style={{ cursor: "pointer" }}
onClick={(e) => e.preventDefault()}
<button
className="theme-btn"
onClick={(e) => setTheme(!theme)}
>
<div className="profile-dropdown">
<div className="profile-dropdown-ava">
<img src={avatar} alt="" />
</div>
<div
className="d-flex profile-dropdown-text"
style={{ flexDirection: "column" }}
>
<p
className={
!theme ? "business-name" : "business-name-dark"
}
>
{userObject?.username}
</p>
<img src={themeBtn} alt="" />
</button>
<Dropdown overlay={menu} trigger={["click"]}>
<div
style={{ cursor: "pointer" }}
onClick={(e) => e.preventDefault()}
>
<div className="profile-dropdown">
<div className="profile-dropdown-ava">
<img src={avatar} alt="" />
</div>
<div className="d-flex profile-dropdown-text">
<p
className={
!theme ? "business-name" : "business-name-dark"
}
>
{username}
{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>
)}
</p>
</div>
</div>
</div>
</div>
</Dropdown>
</div>
</Header>
</Dropdown>
</div>
</Header>
)}
<Content
id="element"
style={{
@ -349,6 +433,11 @@ const App: React.FC = () => {
}}
>
<Routes>
<Route
key={"task"}
path="/"
element={<Task socketData={socketData} />}
/>
{mainItems &&
mainItems.map((u) => (
<Route key={u.key} path={u.path} element={u.component} />
@ -357,6 +446,20 @@ const App: React.FC = () => {
superItems.map((u) => (
<Route key={u.key} path={u.path} element={u.component} />
))}
{superItems && (
<Route
key={"requests"}
path="/requests/"
element={<Requests socketData={socketData} />}
/>
)}
{superItems && (
<Route
key={"calls"}
path="/calls/"
element={<Call socketData={socketData} />}
/>
)}
<Route path="*" element={<Notfound />} />
</Routes>
</Content>

@ -1,6 +1,6 @@
import { Navigate, useLocation } from "react-router-dom";
import { useLocation } from "react-router-dom";
import { registryVerify } from "../API/auth/activate";
import { message } from "antd";
const Activate = () => {
const location = useLocation();
@ -15,7 +15,7 @@ const Activate = () => {
})
}
return <div></div>;
return <div><h1>OOOOOOO Bratim qandoysiz?!</h1></div>;
};
export default Activate;

@ -16,8 +16,6 @@ const Invite = () => {
role_id: role_id,
business_id: business_id,
}).then((status) => {
console.log(status);
if (status === 200) {
document.location.replace("/");
}

@ -3,6 +3,7 @@ import { Button, Card, Input, Space } from "antd";
import { Form, Field } from "react-final-form";
import { LockOutlined, UserOutlined } from "@ant-design/icons";
import { LoginApi } from "../API/auth/Login";
import { Link } from "react-router-dom";
const Login: React.FC = () => {
const validate = (val: any) => {
@ -106,12 +107,12 @@ const Login: React.FC = () => {
>
Log In
</Button>
{/* <h5>
<h5>
<Link to='/auth/reset_password'>Forgot password?</Link>
<br />
Don't have an account?
<Link to='/auth/register'> Create one now</Link>
</h5> */}
</h5>
</Space>
</form>
</Card>

@ -71,7 +71,7 @@ const Register: React.FC = () => {
}}
>
<h1>Sign up</h1>
{emailDom && (
{/* { && ( */}
<Card
bodyStyle={{ background: "rgb(250, 250, 250)" }}
title={emailDom}
@ -120,7 +120,7 @@ const Register: React.FC = () => {
</Col>
</Row>
{/* <Form.Item
<Form.Item
name="email"
rules={[
{
@ -147,7 +147,7 @@ const Register: React.FC = () => {
placeholder="E-mail"
/>
)}
</Form.Item> */}
</Form.Item>
<Form.Item
name="username"
@ -262,7 +262,7 @@ const Register: React.FC = () => {
</Form.Item>
</Space>
</Card>
)}
{/* )} */}
</Space>
</Form>
<Success open={open} setOpen={setOpen} email={email} />

@ -1,11 +1,74 @@
import React, { useState } from "react";
import { useEffect, useState } from "react";
import CallTable from "./CallTable";
import { StepForwardOutlined, StepBackwardOutlined } from "@ant-design/icons";
import { useCallData } from "../../Hooks/CallRequests";
import { Radio, RadioChangeEvent } from "antd";
import { Button, Input, Radio, RadioChangeEvent, Space } from "antd";
import { TCall } from "../../types/CallRequests/TCall";
import { TSocket } from "../../types/common/TSocket";
const Call = () => {
const Call = ({ socketData }: { socketData: TSocket | undefined }) => {
const [status, setStatus] = useState("Awaiting");
const { data, isLoading, refetch } = useCallData({ status: status });
const [page, setPage] = useState(1);
const [tableData, setTableData] = useState<TCall[]>();
const { data, isLoading, refetch } = useCallData({
status: status,
page: page,
page_size: 10,
});
useEffect(() => {
setTableData(data?.data);
}, [data]);
useEffect(() => {
let dataStatus = socketData?.callback_request?.status;
if (socketData?.type === "callback_request") {
if (status === "Awaiting") {
setTableData((prev) => {
if (prev && prev.length >= 15) {
prev.pop();
}
if (dataStatus === "Resolved") {
const data = (prev || []).filter(
(b: TCall) => b?.id !== socketData?.callback_request?.id
);
return data;
}
if (dataStatus === "Awaiting") {
return [socketData?.callback_request, ...(prev || [])] as TCall[];
}
return prev;
});
} else if (status === "Resolved") {
setTableData((prev) => {
if (prev && prev.length >= 15) {
prev.pop();
}
if (dataStatus === "Awaiting") {
const data = (prev || []).filter(
(b: TCall) => b?.id !== socketData?.callback_request?.id
);
return data;
}
if (dataStatus === "Resolved") {
return [socketData?.callback_request, ...(prev || [])] as TCall[];
}
return prev;
});
}
}
}, [socketData]);
const Next = () => {
const a = Number(page) + 1;
setPage(a);
};
const Previos = () => {
Number(page);
if (page > 1) {
const a = Number(page) - 1;
setPage(a);
}
};
return (
<div>
@ -25,7 +88,33 @@ const Call = () => {
</Radio.Group>
</div>
</div>
<CallTable data={data} isLoading={isLoading} refetch={refetch} />
<CallTable data={tableData} isLoading={isLoading} refetch={refetch} />
<Space style={{ width: "100%", marginTop: 10 }} direction="vertical">
<Space style={{ width: "100%", justifyContent: "flex-end" }} wrap>
<Button
type="primary"
icon={<StepBackwardOutlined />}
onClick={Previos}
disabled={data?.previous ? false : true}
></Button>
<Input
style={{ width: 50, textAlign: "right" }}
value={page}
onChange={(e) => {
let num = e.target.value;
if (Number(num) && num !== "0") {
setPage(Number(num));
}
}}
/>
<Button
type="primary"
icon={<StepForwardOutlined />}
onClick={Next}
disabled={data?.next ? false : true}
></Button>
</Space>
</Space>
</div>
);
};

@ -11,6 +11,7 @@ import {
RefetchQueryFilters,
} from "react-query";
import { useState } from "react";
import { TPagination } from "../../types/common/TPagination";
const CallTable = ({
data,
isLoading,
@ -20,7 +21,7 @@ const CallTable = ({
isLoading: boolean;
refetch: <TPageData>(
options?: (RefetchOptions & RefetchQueryFilters<TPageData>) | undefined
) => Promise<QueryObserverResult<TCall[], unknown>>;
) => Promise<QueryObserverResult<TPagination<TCall[]>, unknown>>;
}) => {
const statusClick = (record: TCall | any) => {
callController

@ -6,6 +6,7 @@ import {
RefetchQueryFilters,
} from "react-query";
import { TCompany } from "../../types/Company/TCompany";
import { TPagination } from "../../types/common/TPagination";
const AddCompany = ({
open,
@ -14,7 +15,7 @@ const AddCompany = ({
}: {
refetch: <TPageData>(
options?: (RefetchOptions & RefetchQueryFilters<TPageData>) | undefined
) => Promise<QueryObserverResult<TCompany[], unknown>>;
) => Promise<QueryObserverResult<TPagination<TCompany[]>, unknown>>;
open: boolean;
setOpen(open: boolean): void;
}) => {

@ -1,12 +1,15 @@
import { useRef, useState } from "react";
import AddCompany from "./AddCompanies";
import CompanyTable from "./CompaniesTable";
import { StepForwardOutlined, StepBackwardOutlined } from "@ant-design/icons";
import { useCompanyPaginated } from "../../Hooks/Companies";
import { Button, Input, Space } from "antd";
// @ts-ignore
import IconSearch from "../../assets/searchIcon.png";
import { useCompanyData } from "../../Hooks/Companies";
//@ts-ignore
import addicon from "../../assets/addiconpng.png";
import { role } from "../../App";
const theme = localStorage.getItem("theme") === "true" ? true : false;
const Company = () => {
@ -15,10 +18,13 @@ const Company = () => {
setOpen(true);
};
const [search, setSearch] = useState<any>("");
const { data, isLoading, refetch } = useCompanyData({
const [search, setSearch] = useState<string>();
const [page, setPage] = useState<number>(1);
const { data, isLoading, refetch } = useCompanyPaginated({
name: search,
is_active: undefined,
page: page,
page_size: 10,
});
const timerRef = useRef<NodeJS.Timeout | null>(null);
@ -33,19 +39,33 @@ const Company = () => {
}, 1000);
};
const Next = () => {
const a = Number(page) + 1;
setPage(a);
};
const Previos = () => {
Number(page);
if (page > 1) {
const a = Number(page) - 1;
setPage(a);
}
};
return (
<div>
{open && <AddCompany open={open} refetch={refetch} setOpen={setOpen} />}
<div className="header d-flex">
<h1 className="title">Companies</h1>
<button
style={{ marginRight: 0 }}
className="btn-add d-flex"
onClick={showModal}
>
<img src={addicon} style={{ marginRight: 8 }} alt="" />
Add Company
</button>
{role !== "Checker" && (
<button
style={{ marginRight: 0 }}
className="btn-add d-flex"
onClick={showModal}
>
<img src={addicon} style={{ marginRight: 8 }} alt="" />
Add Company
</button>
)}
</div>
<div className="filter d-flex">
<div className="search-div">
@ -59,7 +79,33 @@ const Company = () => {
</div>
</div>
<CompanyTable data={data} isLoading={isLoading} />
<CompanyTable data={data?.data} isLoading={isLoading} />
<Space style={{ width: "100%", marginTop: 10 }} direction="vertical">
<Space style={{ width: "100%", justifyContent: "flex-end" }} wrap>
<Button
type="primary"
icon={<StepBackwardOutlined />}
onClick={Previos}
disabled={data?.previous ? false : true}
></Button>
<Input
style={{ width: 50, textAlign: "right" }}
value={page}
onChange={(e) => {
let num = e.target.value;
if (Number(num) && num !== "0") {
setPage(Number(num));
}
}}
/>
<Button
type="primary"
icon={<StepForwardOutlined />}
onClick={Next}
disabled={data?.next ? false : true}
></Button>
</Space>
</Space>
</div>
);
};

@ -21,8 +21,9 @@ import { DashboardOutlined } from "@ant-design/icons";
import Notfound from "../../Utils/Notfound";
import Table from "antd/es/table";
import AddDriver from "./AddDriver";
import { role } from "../../App";
import { useTeamData } from "../../Hooks/Teams";
import { useCustomerByComanyData } from "../../Hooks/Customers";
// @ts-ignore
import zippy from "../../assets/zippyicon.svg";
// @ts-ignore
@ -39,9 +40,6 @@ import tagIcon from "../../assets/tagIcon.png";
import infoIcon from "../../assets/infoIcon.png";
// @ts-ignore
import infoIconActive from "../../assets/infoIconActive.png";
import { role } from "../../App";
import { useTeamData } from "../../Hooks/Teams";
import { validateLocaleAndSetLanguage } from "typescript";
const TabPane = Tabs.TabPane;
type params = {
readonly id: any;
@ -79,7 +77,6 @@ const CompanyEdit = () => {
};
const [value, setValue] = useState(1);
const onChange = (e: RadioChangeEvent) => {
console.log("radio checked", e.target.value);
setValue(e.target.value);
};
const [activeTab, setActiveTab] = useState("1");

@ -117,32 +117,10 @@ function CompanyTable({
responsive: ["lg"],
},
{
width: "20%",
width: "10%",
title: "Actions",
dataIndex: "action",
render: ({ id, api }: { id: string; api: string }) => {
const enterLoading = (index: number, api: string) => {
setLoadings((prevLoadings) => {
const newLoadings = [...prevLoadings];
newLoadings[index] = true;
return newLoadings;
});
if (api && api !== "") {
companyController.SyncCompany(index);
} else {
message.error({
content: "This company doesn't have an api key",
duration: 2,
});
}
setTimeout(() => {
setLoadings((prevLoadings) => {
const newLoadings = [...prevLoadings];
newLoadings[index] = false;
return newLoadings;
});
}, 6000);
};
render: ({ id }: { id: string }) => {
return (
<Space>
<Link to={`${id}`}>
@ -151,15 +129,6 @@ function CompanyTable({
<Button type="primary" icon={<EyeOutlined />}></Button>
)}
</Link>
{role !== "Checker" && (
<Button
type="primary"
icon={<SyncOutlined />}
loading={loadings[Number(id)]}
onClick={() => enterLoading(Number(id), api)}
/>
)}
</Space>
);
},

@ -1,25 +1,41 @@
import { useRef, useState } from "react";
import AddCustomer from "./AddCustomer";
import CustomerTable from "./CustomersTable";
import { StepForwardOutlined, StepBackwardOutlined } from "@ant-design/icons";
import { useCustomerData } from "../../Hooks/Customers";
//@ts-ignore
import addicon from "../../assets/addiconpng.png";
// @ts-ignore
import IconSearch from "../../assets/searchIcon.png";
import { Button, Input, Space } from "antd";
const Customer = () => {
const [open, setOpen] = useState(false);
const [page, setPage] = useState(1)
const showModal = () => {
setOpen(true);
};
const [search, setSearch] = useState("");
const { isLoading, data, refetch } = useCustomerData({
const { data, isLoading, refetch } = useCustomerData({
name: search,
is_active: undefined,
page_size: 10,
page: page
});
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) {
@ -53,7 +69,33 @@ const Customer = () => {
/>
</div>
</div>
<CustomerTable data={data} isLoading={isLoading} />
<CustomerTable data={data?.data} isLoading={isLoading} />
<Space style={{ width: "100%", marginTop: 10 }} direction="vertical">
<Space style={{ width: "100%", justifyContent: "flex-end" }} wrap>
<Button
type="primary"
icon={<StepBackwardOutlined />}
onClick={Previos}
disabled={!data?.previous}
></Button>
<Input
style={{ width: 50, textAlign: "right" }}
value={page}
onChange={(e) => {
let num = e.target.value;
if (Number(num) && num !== "0") {
setPage(Number(num));
}
}}
/>
<Button
type="primary"
icon={<StepForwardOutlined />}
onClick={Next}
disabled={!data?.next}
></Button>
</Space>
</Space>
</div>
);
};

@ -1,5 +1,6 @@
import { useParams } from "react-router-dom";
import { useParams, useNavigate } from "react-router-dom";
import { useCustomerOne } from "../../Hooks/Customers";
import {
Form,
Spin,
@ -13,7 +14,6 @@ import {
} from "antd";
import { customerController } from "../../API/LayoutApi/customers";
import Notfound from "../../Utils/Notfound";
import { useCompanyOne } from "../../Hooks/Companies";
import { role } from "../../App";
import { useState } from "react";
// @ts-ignore
@ -30,22 +30,19 @@ type params = {
const CustomerEdit = () => {
const { id } = useParams<params>();
const { data, refetch, status } = useCustomerOne(id);
let navigate = useNavigate();
const onSubmit = async (value: any) => {
await customerController.customerPatch(value, id);
refetch();
window.location.replace("/#/customers/");
navigate(-1);
};
const companyData = useCompanyOne(data?.id);
const ClickDelete = () => {
const shouldDelete = window.confirm(
"Are you sure, you want to delete this Driver?"
);
if (shouldDelete && id !== undefined) {
customerController.deleteCustomerController(id).then((data: any) => {
document.location.replace(`/#/customers`);
});
customerController.deleteCustomerController(id);
navigate(-1);
}
};
const [activeTab, setActiveTab] = useState("1");
@ -94,19 +91,18 @@ const CustomerEdit = () => {
autoComplete="off"
>
<Row gutter={[16, 10]}>
{companyData?.data && (
<Col span={6}>
<Form.Item
wrapperCol={{ span: "100%" }}
label="Company"
>
<Input
defaultValue={companyData?.data?.name}
readOnly
/>
</Form.Item>
</Col>
)}
<Col span={6}>
<Form.Item
wrapperCol={{ span: "100%" }}
label="Company"
>
<Input
defaultValue={data?.company?.name}
readOnly
/>
</Form.Item>
</Col>
<Col span={6}>
<Form.Item
wrapperCol={{ span: "100%" }}

@ -1,8 +1,18 @@
import { Table } from "antd";
import { Table, Tooltip } from "antd";
import { useCompanyData } from "../../Hooks/Companies";
import { TCustomer } from "../../types/Customer/TCustomer";
// @ts-ignore
import tagIcon from "../../assets/tagIcon.png";
// @ts-ignore
import zippy from "../../assets/zippyicon.svg";
// @ts-ignore
import evo from "../../assets/evoicon.png";
// @ts-ignore
import zeelog from "../../assets/zeelogicon.svg";
// @ts-ignore
import ontime from "../../assets/ontimeicon.svg";
// @ts-ignore
import tt from "../../assets/tticon.svg";
import { role } from "../../App";
function CustomerTable({
@ -12,12 +22,6 @@ function CustomerTable({
data?: TCustomer[] | undefined;
isLoading?: boolean;
}) {
const CompanyData = useCompanyData({
name: undefined,
page: undefined,
is_active: undefined,
});
type RowProps = {
id: number;
};
@ -30,6 +34,23 @@ function CustomerTable({
},
};
};
const getImageSource = (source: string) => {
switch (source) {
case "Zippy":
return zippy;
case "EVO":
return evo;
case "Ontime":
return ontime;
case "Zeelog":
return zeelog;
case "TT":
return tt;
default:
return tt;
}
};
return (
<div>
<Table
@ -38,10 +59,7 @@ function CustomerTable({
dataSource={data?.map((u, i) => ({
...u,
no: i + 1,
company_id:
CompanyData?.data?.find(
(company: any) => company.id === u?.company_id
)?.name || "",
company: u?.company,
}))}
columns={[
{
@ -55,12 +73,27 @@ function CustomerTable({
},
{
title: "Company",
dataIndex: "company_id",
dataIndex: "company",
render: (text: any, record: any) => (
<Tooltip placement="topLeft" title={text?.name}>
<div style={{ display: "flex", alignItems: "center" }}>
{text?.source && (
<img
src={getImageSource(text?.source)}
alt=""
style={{ width: 20, height: 20, marginRight: 10 }}
/>
)}
{text?.name}
</div>
</Tooltip>
),
},
]}
rowClassName={(record, index) =>
index % 2 === 0 ? "odd-row" : "even-row"
}
pagination={false}
/>
</div>
);

@ -7,7 +7,6 @@ const ChangePassword = () => {
const submit = () => {
form.validateFields().then(async (values) => {
form.resetFields();
console.log(values);
await prof.changePass(values);
});
};

@ -32,7 +32,7 @@ const Profile = () => {
const [range, setRange] = useState<any>(1);
const onSubmit = async (value: TProfilePutParams) => {
await prof.profPatch(value);
await prof.profPatch(value)
refetch();
};

@ -0,0 +1,132 @@
import { Form, Tabs, Row, Col, Input, Modal, Button, Select } from "antd";
import { useState } from "react";
import { useCompanyData } from "../../Hooks/Companies";
// @ts-ignore
import zippy from "../../assets/zippyicon.svg";
// @ts-ignore
import evo from "../../assets/evoicon.png";
// @ts-ignore
import zeelog from "../../assets/zeelogicon.svg";
// @ts-ignore
import ontime from "../../assets/ontimeicon.svg";
// @ts-ignore
import tt from "../../assets/tticon.svg";
import { useCustomerByComanyData } from "../../Hooks/Customers";
const TabPane = Tabs.TabPane;
const AssignedEdit = ({ recordData, editOpen, setEditOpen }: any) => {
const handleCancel = () => {
setEditOpen(!editOpen);
};
const onSubmit = () => {};
const [companyName, setCompanyName] = useState<string>();
const [companyId, setCompanyId] = useState<number>();
const [customerName, setCustomerName] = useState<string>();
const companyData = useCompanyData({ name: companyName });
const customerData = useCustomerByComanyData({
id: companyId,
name: customerName,
});
const getImageSource = (source: string) => {
switch (source) {
case "Zippy":
return zippy;
case "EVO":
return evo;
case "Ontime":
return ontime;
case "Zeelog":
return zeelog;
case "TT":
return tt;
default:
return tt;
}
};
return (
<Modal
onCancel={handleCancel}
footer={null}
open={editOpen}
width={800}
maskClosable={true}
>
<Form
name="basic"
layout="vertical"
wrapperCol={{ span: 16 }}
initialValues={{ ...recordData }}
onFinish={onSubmit}
autoComplete="off"
>
<Row gutter={[16, 10]}>
<Col span={6}>
<Form.Item
wrapperCol={{ span: "100%" }}
label="Company"
name="company"
>
<Select
showSearch
placeholder="Search Company"
onSearch={(value: any) => setCompanyName(value)}
options={companyData?.data?.map((item) => ({
label: (
<div>
{item?.source && (
<img
style={{ width: 15, height: 20, paddingTop: 7 }}
src={getImageSource(item?.source)}
alt=""
/>
)}{" "}
{item?.name}
</div>
),
value: item?.id,
}))}
value={companyName}
filterOption={false}
autoClearSearchValue={false}
allowClear
onChange={(value: any) => setCompanyId(value)}
/>
</Form.Item>
</Col>
<Col span={6}>
<Form.Item
wrapperCol={{ span: "100%" }}
label="Driver"
name="customer"
>
<Select
showSearch
placeholder="Search Driver"
onSearch={(value: any) => setCustomerName(value)}
options={customerData?.data?.map((item) => ({
label: item?.name,
value: item?.id,
}))}
value={customerName}
filterOption={false}
autoClearSearchValue={false}
allowClear
/>
</Form.Item>
</Col>
</Row>
<Form.Item>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
</Modal>
);
};
export default AssignedEdit;

@ -1,22 +1,28 @@
import { useRef, useState } from "react";
import { useEffect, useRef, useState } from "react";
import { useRequestsData } from "../../Hooks/Requests";
import { StepForwardOutlined, StepBackwardOutlined } from "@ant-design/icons";
import { Button, Input, Radio, RadioChangeEvent, Space } from "antd";
import { TRequests } from "../../types/Requests/TRequests";
import { TSocket } from "../../types/common/TSocket";
import RequestsEdit from "./RequestsEdit";
import RequestsTable from "./RequestsTable";
// @ts-ignore
import IconSearch from "../../assets/searchIcon.png";
import { Radio, RadioChangeEvent } from "antd";
import RequestsEdit from "./RequestsEdit";
import { TRequests } from "../../types/Requests/TRequests";
const Requests = () => {
const Requests = ({ socketData }: { socketData: TSocket | undefined }) => {
const [search, setSearch] = useState("");
const [status, setStatus] = useState("Pending");
const [modalOpen, setModalOpen] = useState(false);
const [requestData, setRequestData] = useState<TRequests>();
const [requestData, setRequestData] = useState<TRequests | undefined>();
const [status, setStatus] = useState("Pending");
const [page, setPage] = useState<number>(1);
const { data, refetch, isLoading } = useRequestsData({
search: search,
status: status,
page: page,
page_size: 10,
});
const [mainData, setMainData] = useState<TRequests[]>();
const timerRef = useRef<NodeJS.Timeout | null>(null);
const handleSearchChange = (e: React.ChangeEvent<HTMLInputElement>) => {
@ -31,6 +37,37 @@ const Requests = () => {
};
const theme = localStorage.getItem("theme") === "true" ? true : false;
useEffect(() => {
if (data) {
setMainData(data.data);
}
}, [data]);
// useEffect(() => {
// if (socketData) {
// setMainData((prev: TRequests[] | undefined) => {
// if (prev && prev?.length >= 15) {
// prev?.pop();
// }
// if (socketData.type === "driver_request") {
// }
// return prev;
// });
// }
// }, [socketData]);
const Next = () => {
const a = Number(page) + 1;
setPage(a);
};
const Previos = () => {
Number(page);
if (page > 1) {
const a = Number(page) - 1;
setPage(a);
}
};
return (
<div>
{modalOpen && (
@ -63,16 +100,41 @@ const Requests = () => {
<Radio.Button value={"Pending"}>Pending</Radio.Button>
<Radio.Button value={"Assigned"}>Assigned</Radio.Button>
<Radio.Button value={"Rejected"}>Rejected</Radio.Button>
<Radio.Button value={undefined}>All</Radio.Button>
</Radio.Group>
</div>
<RequestsTable
data={data}
data={mainData}
isLoading={isLoading}
refetch={refetch}
setOpenModal={setModalOpen}
setRequestData={setRequestData}
/>
<Space style={{ width: "100%", marginTop: 10 }} direction="vertical">
<Space style={{ width: "100%", justifyContent: "flex-end" }} wrap>
<Button
type="primary"
icon={<StepBackwardOutlined />}
onClick={Previos}
disabled={data?.previous ? false : true}
></Button>
<Input
style={{ width: 50, textAlign: "right" }}
value={page}
onChange={(e) => {
let num = e.target.value;
if (Number(num) && num !== "0") {
setPage(Number(num));
}
}}
/>
<Button
type="primary"
icon={<StepForwardOutlined />}
onClick={Next}
disabled={data?.next ? false : true}
></Button>
</Space>
</Space>
</div>
);
};

@ -1,7 +1,10 @@
import { Modal, Select, Button } from "antd";
import { TRequests } from "../../types/Requests/TRequests";
import { useState } from "react";
import { useCustomerData } from "../../Hooks/Customers";
import { useEffect, useState } from "react";
import {
useCustomerByComanyData,
useCustomerData,
} from "../../Hooks/Customers";
import { requestsController } from "../../API/LayoutApi/requests";
// @ts-ignore
import plus from "../../assets/add-icon.png";
@ -10,6 +13,8 @@ import {
RefetchOptions,
RefetchQueryFilters,
} from "react-query";
import { useCompanyData } from "../../Hooks/Companies";
import { TPagination } from "../../types/common/TPagination";
const RequestsEdit = ({
modalOpen,
setModalOpen,
@ -18,7 +23,7 @@ const RequestsEdit = ({
}: {
refetch: <TPageData>(
options?: (RefetchOptions & RefetchQueryFilters<TPageData>) | undefined
) => Promise<QueryObserverResult<TRequests[], unknown>>;
) => Promise<QueryObserverResult<TPagination<TRequests[]>, unknown>>;
modalOpen: any;
setModalOpen: any;
requestData: TRequests | undefined;
@ -28,15 +33,49 @@ const RequestsEdit = ({
setModalOpen(!modalOpen);
};
const [companyName, setCompanyName] = useState<string>();
const [customerName, setCustomerName] = useState<string>();
const [driverId, setDriverId] = useState<number>();
const companyData = useCompanyData({ name: companyName });
const [companyId, setCompanyId] = useState<number>();
const [customerOption, setCustomerOption] = useState<any>();
const customerData = useCustomerData({
name: customerName,
page: 1,
page_size: 5,
for_driver_request: true,
});
const customerDataByCompany = useCustomerByComanyData({
id: companyId,
name: customerName,
for_driver_request: true,
});
const optionClick = (value: number) => {
setDriverId(value);
};
useEffect(() => {
if (companyId && customerDataByCompany) {
const newCustomerOption = customerDataByCompany.data?.map((item) => ({
label: item.name,
value: item.id,
}));
if (
JSON.stringify(newCustomerOption) !== JSON.stringify(customerOption)
) {
setCustomerOption(newCustomerOption);
}
} else if (!companyId && customerData) {
const newCustomerOption = customerData.data?.data.map((item) => ({
label: `${item?.name} - ${item.company?.name}`,
value: item.id,
}));
if (
JSON.stringify(newCustomerOption) !== JSON.stringify(customerOption)
) {
setCustomerOption(newCustomerOption);
}
}
}, [companyId, customerData, customerDataByCompany, customerOption]);
const assignClick = () => {
const value = {
@ -48,15 +87,15 @@ const RequestsEdit = ({
refetch();
setModalOpen(false);
});
// console.log(value);
};
return (
<div>
<Modal
onCancel={handleCancel}
footer={null}
open={modalOpen}
width={800}
width={1000}
maskClosable={true}
>
<div className="info-div">
@ -100,6 +139,18 @@ const RequestsEdit = ({
{requestData?.company_usdot}
</p>
</tr>
{requestData?.telegram_user_link && (
<tr>
<p className={!theme ? "sub" : "sub-dark"}>Telegram User</p>
<p className={!theme ? "info" : "info-dark"}>
{requestData?.telegram_user_link ? (
<a href={requestData?.telegram_user_link}>Open Chat</a>
) : (
"This user has no username"
)}
</p>
</tr>
)}
</div>
{requestData?.status === "Assigned" && (
@ -138,15 +189,22 @@ const RequestsEdit = ({
</div>
)}
{requestData?.status === "Pending" && (
<div className="search-driver">
<div className="d-flex">
<div
className="search-driver"
style={{
display: "flex",
alignItems: "center",
flexDirection: "column",
}}
>
<div className="d-flex" style={{ width: "100%" }}>
<Select
showSearch
style={{ width: 325 }}
placeholder="Search Driver"
onSearch={(value) => setCustomerName(value)}
onChange={(value: number) => optionClick(value)}
options={customerData?.data?.map((item) => ({
style={{ marginRight: 15, width: "40%" }}
placeholder="Search Company"
onSearch={(value) => setCompanyName(value)}
onChange={(value: number) => setCompanyId(value)}
options={companyData?.data?.map((item) => ({
label: item?.name,
value: item?.id,
}))}
@ -154,29 +212,50 @@ const RequestsEdit = ({
autoClearSearchValue={false}
allowClear
/>
<Select
style={{ width: "50%" }}
showSearch
placeholder="Search Driver"
onSearch={(value) => setCustomerName(value)}
onChange={(value: number) => setDriverId(value)}
options={customerOption}
filterOption={false}
autoClearSearchValue={false}
allowClear
/>
<Button
type="primary"
onClick={assignClick}
disabled={driverId ? false : true}
style={{ marginLeft: 15 }}
style={{ marginLeft: 15, width: "10%" }}
>
Assign
</Button>
</div>
<Button
onClick={assignClick}
disabled={driverId ? true : false}
<div
style={{
width: "100%",
display: "flex",
alignItems: "center",
padding: "6px 15px",
margin: 0,
justifyContent: "flex-end",
}}
className="btn-add"
>
<img src={plus} alt="" style={{ marginRight: 8 }} />
Add new driver
</Button>
<Button
onClick={assignClick}
disabled={driverId ? true : false}
style={{
padding: "6px 15px",
marginTop: 15,
marginRight: 0,
display: "flex",
alignItems: "center",
}}
className="btn-add"
>
<img src={plus} alt="" style={{ marginRight: 8 }} />
New driver
</Button>
</div>
</div>
)}
</div>

@ -10,6 +10,7 @@ import moment from "moment";
import { TRequests } from "../../types/Requests/TRequests";
import { useEffect, useState } from "react";
import { requestsController } from "../../API/LayoutApi/requests";
import { TPagination } from "../../types/common/TPagination";
const RequestsTable = ({
data,
@ -24,7 +25,7 @@ const RequestsTable = ({
isLoading: boolean;
refetch: <TPageData>(
options?: (RefetchOptions & RefetchQueryFilters<TPageData>) | undefined
) => Promise<QueryObserverResult<TRequests[], unknown>>;
) => Promise<QueryObserverResult<TPagination<TRequests[]>, unknown>>;
}) => {
const [isTextSelected, setIsTextSelected] = useState(false);
@ -55,11 +56,18 @@ const RequestsTable = ({
};
const patchRequest = (record: TRequests) => {
requestsController.delete(record?.id).then(() => {
requestsController
.rejectPatch({ status: "Rejected" }, record?.id)
.then(() => {
refetch();
});
};
const deleteRequest = (record: any) => {
requestsController.delete(record.id).then(() => {
refetch();
});
};
return (
<div>
<Table
@ -115,7 +123,12 @@ const RequestsTable = ({
dataIndex: "action",
render: (text: string, record: TRequests) => {
return (
<div>
<div
style={{
display: "flex",
alignItems: "center",
}}
>
{record?.status === "Pending" && (
<Space>
<Button
@ -127,6 +140,18 @@ const RequestsTable = ({
</Button>
</Space>
)}
{(record?.status === "Assigned" ||
record?.status === "Rejected") && (
<Space>
<Button
type="primary"
danger
onClick={(e) => deleteRequest(record)}
>
Delete
</Button>
</Space>
)}
</div>
);
},

@ -1,11 +1,12 @@
import {
Input,
Modal,
Form as FormAnt,
Select,
Upload,
Switch,
Button,
Radio,
RadioChangeEvent,
Row,
Col,
} from "antd";
@ -26,9 +27,12 @@ import zeelog from "../../assets/zeelogicon.svg";
import ontime from "../../assets/ontimeicon.svg";
// @ts-ignore
import tt from "../../assets/tticon.svg";
//@ts-ignore
import addicon from "../../assets/addiconpng.png";
import AddCustomer from "../Customers/AddCustomer";
import AddDriver from "../Companies/AddDriver";
import TextArea from "antd/es/input/TextArea";
import { isMobile } from "../../App";
const { Option } = Select;
const AddTask = ({
@ -46,7 +50,7 @@ const AddTask = ({
const [fileIds, setFileIds] = useState([]);
const [companyName, setCompanyName] = useState<string>();
const [customerName, setCustomerName] = useState<string>();
const [companyId, setCompanyId] = useState<string>();
const [companyId, setCompanyId] = useState<number>();
const ServiceData = useServiceData();
const TeamData = useTeamData("");
@ -58,20 +62,13 @@ const AddTask = ({
const [driverOpen, setDriverOpen] = useState(false);
const [previewImage, setPreviewImage] = useState<string | null>(null);
function handlePaste(event: any) {
const clipboardData = event.clipboardData || window.Clipboard;
if (clipboardData && clipboardData.items.length > 0) {
const clipboardItem = clipboardData.items[0];
if (clipboardItem.kind === "file") {
const file = clipboardItem.getAsFile();
const reader = new FileReader();
reader.onload = (e) => {
if (e.target && e.target.result) {
setPreviewImage(e.target.result as string);
}
};
reader.readAsDataURL(file);
taskController
.addTaskFile({ files: [file] })
@ -108,6 +105,39 @@ const AddTask = ({
};
const [openDrive, setOpenDrive] = useState(false);
const serviceOptions = ServiceData?.data?.map((item) => ({
label: item?.title,
value: item?.id,
}));
const sortByLabel = (a: any, b: any) => {
if (a.label === "Shift") return -1;
if (b.label === "Shift") return 1;
return 0;
};
const noteOptions = [
{ label: "No", value: "" },
{ label: "Empty", value: "Empty" },
{ label: "Bobtail", value: "Bobtail" },
];
const [note, setNote] = useState("");
const onChange = ({ target: { value } }: RadioChangeEvent) => {
setNote(value);
};
const noteOptions2 = [
{ label: "No", value: "" },
{ label: "+1 soat", value: "+1 soat" },
{ label: "+3 soat", value: "+3 soat" },
];
const [note2, setNote2] = useState("");
const onChange2 = ({ target: { value } }: RadioChangeEvent) => {
setNote2(value);
};
const [text, setText] = useState("");
const changeText = (e: any) => {
setText(e.target.value);
};
return (
<div onPaste={(event) => handlePaste(event)}>
{openDrive && (
@ -127,8 +157,11 @@ const AddTask = ({
onCancel={handleCancel}
onOk={() => {
form.validateFields().then(async (values) => {
// const updatedValues = { ...values };
values.attachment_ids = fileIds;
values.note =
(text ? text + ", " : "") +
(note ? note + ", " : "") +
(note2 ? note2 + ", " : "");
form.resetFields();
await taskController.addTaskController(values);
setOpen(!open);
@ -137,184 +170,241 @@ const AddTask = ({
>
<FormAnt
form={form}
layout="horizontal"
layout={isMobile ? "vertical" : "horizontal"}
name="form_in_modal"
initialValues={{ modifier: "public" }}
>
<FormAnt.Item
label="Company"
name="company_id"
rules={[{ required: true, message: "Please input company!" }]}
>
<Select
showSearch
placeholder="Search Company"
onSearch={(value: any) => setCompanyName(value)}
options={companyData?.data?.map((item) => ({
label: (
<div>
{item?.source && (
<img
style={{ width: 15, height: 20, paddingTop: 7 }}
src={getImageSource(item?.source)}
alt=""
/>
)}{" "}
{item?.name}
</div>
),
value: item?.id,
}))}
value={companyName}
filterOption={false}
autoClearSearchValue={false}
allowClear
onChange={(value: any) => setCompanyId(value)}
/>
</FormAnt.Item>
<div
style={{
display: "flex",
justifyContent: "space-between",
}}
>
<FormAnt.Item
label="Driver"
name="customer_id"
style={{ width: "85%" }}
rules={[
{ required: true, message: "Please input service points!" },
]}
>
<Select
showSearch
placeholder="Search Driver"
onSearch={(value: any) => setCustomerName(value)}
options={customerData?.data?.map((item) => ({
label: item?.name,
value: item?.id,
}))}
value={customerName}
filterOption={false}
autoClearSearchValue={false}
allowClear
/>
</FormAnt.Item>
<Button onClick={(e) => setDriverOpen(true)} type="primary">
Add
</Button>
</div>
<Row gutter={[16, 16]}>
<Col span={24}>
<FormAnt.Item
label="Company"
name="company_id"
rules={[{ required: true, message: "Please input company!" }]}
>
<Select
showSearch
placeholder="Search Company"
onSearch={(value: any) => setCompanyName(value)}
options={companyData?.data?.map((item) => ({
label: (
<div>
{item?.source && (
<img
style={{ width: 15, height: 20, paddingTop: 7 }}
src={getImageSource(item?.source)}
alt=""
/>
)}{" "}
{item?.name}
</div>
),
value: item?.id,
}))}
value={companyName}
filterOption={false}
autoClearSearchValue={false}
allowClear
onChange={(value: any) => setCompanyId(value)}
/>
</FormAnt.Item>
</Col>
<Col span={24}>
<div
style={{
display: "flex",
justifyContent: "space-around",
alignItems: isMobile ? "center" : "none",
}}
>
<FormAnt.Item
label="Driver"
name="customer_id"
style={{ width: "85%" }}
rules={[
{ required: true, message: "Please input service points!" },
]}
>
<Select
showSearch
placeholder="Search Driver"
onSearch={(value: any) => setCustomerName(value)}
options={customerData?.data?.map((item) => ({
label: item?.name,
value: item?.id,
}))}
value={customerName}
filterOption={false}
autoClearSearchValue={false}
allowClear
/>
</FormAnt.Item>
<Button
onClick={(e) => setDriverOpen(true)}
type="primary"
style={{
display: "flex",
alignItems: "center",
justifyContent: "space-between",
marginTop: isMobile ? 5 : 0,
}}
>
{isMobile && <img src={addicon} alt="" />}
{!isMobile && "Add"}
</Button>
</div>
</Col>
<FormAnt.Item
label="Service"
name="service_id"
rules={[{ required: true, message: "Please select service!" }]}
>
<Select
options={ServiceData?.data?.map((item) => ({
label: item?.title,
value: item?.id,
}))}
/>
</FormAnt.Item>
<Col span={isMobile ? 12 : 24}>
<FormAnt.Item
label="Service"
name="service_id"
rules={[{ required: true, message: "Please select service!" }]}
>
<Select options={serviceOptions?.sort(sortByLabel)} />
</FormAnt.Item>
</Col>
<FormAnt.Item
label="Assigned to"
name="assigned_to_id"
rules={[
{
required: true,
message: "Please select one of the teams!",
},
]}
>
<Select
options={TeamData?.data?.map((item) => ({
label: item?.name,
value: item?.id,
}))}
/>
</FormAnt.Item>
<Col span={isMobile ? 12 : 24}>
<FormAnt.Item
label="Assigned to"
name="assigned_to_id"
rules={[
{
required: true,
message: "Please select one of the teams!",
},
]}
>
<Select
options={TeamData?.data?.map((item) => ({
label: item?.name,
value: item?.id,
}))}
/>
</FormAnt.Item>
</Col>
<FormAnt.Item
label="Status"
name="status"
rules={[
{ required: false, message: "Please input service points!" },
]}
>
<Select defaultValue="New">
<Option value="New">New</Option>
<Option value="Checking">Checking</Option>
<Option value="Done">Done</Option>
</Select>
</FormAnt.Item>
<Col span={isMobile ? 12 : 12}>
<FormAnt.Item
label="Status"
name="status"
rules={[
{ required: false, message: "Please input service points!" },
]}
>
<Select defaultValue="New">
<Option value="New">New</Option>
<Option value="Checking">Checking</Option>
<Option value="Done">Done</Option>
</Select>
</FormAnt.Item>
</Col>
<FormAnt.Item
label="PTI"
name="pti"
rules={[
{ required: false, message: "Please input service points!" },
]}
>
<Switch />
</FormAnt.Item>
<Col span={isMobile ? 12 : 12}>
<FormAnt.Item
label="PTI"
name="pti"
rules={[
{ required: false, message: "Please input service points!" },
]}
>
<Switch />
</FormAnt.Item>
</Col>
<FormAnt.Item
label="Note"
name="note"
rules={[
{ required: false, message: "Please input service points!" },
]}
>
<TextArea
style={{ padding: "7px 11px" }}
placeholder="note"
autoSize={{ minRows: 3, maxRows: 3 }}
/>
</FormAnt.Item>
<Col span={24}>
<FormAnt.Item
label="Note"
name="note"
rules={[
{ required: false, message: "Please input service points!" },
]}
>
<TextArea
style={{ padding: "7px 11px" }}
placeholder="note"
autoSize={{ minRows: isMobile ? 1 : 2, maxRows: 1 }}
onChange={changeText}
value={text}
/>
{!isMobile && (
<div
style={{
marginTop: 10,
display: "flex",
alignItems: "center",
}}
>
<Radio.Group
options={noteOptions}
onChange={onChange}
// value={value}
optionType="button"
defaultValue={""}
style={{ marginRight: 15 }}
/>
<Radio.Group
options={noteOptions2}
defaultValue={""}
onChange={onChange2}
// value={value}
optionType="button"
/>
</div>
)}
<br />
</FormAnt.Item>
</Col>
</Row>
</FormAnt>
<FormAnt>
<FormAnt.Item name="attachment">
<div>
<Upload.Dragger
name="file"
multiple={true}
customRequest={({ file, onSuccess }: any) => {
const formData = new FormData();
formData.append("file", file);
taskController
.addTaskFile({ task_id: undefined, files: [file] })
.then((response) => {
console.log(response);
const fileId = response.data.file_ids[0];
setFileIds((prevFileIds): any => [
...prevFileIds,
fileId,
]);
onSuccess();
// const updatedValues = form.getFieldsValue();
// updatedValues.attachment_ids = [
// ...updatedValues.attachment_ids,
// fileId,
// ];
// form.setFieldsValue(updatedValues);
});
}}
>
<p className="ant-upload-drag-icon">
<UploadOutlined style={{ color: "rgba(249, 158, 44, 1)" }} />
</p>
<p
className="ant-upload-text"
style={{ color: "rgba(249, 158, 44, 1)" }}
>
Click or drag a file here to upload
</p>
</Upload.Dragger>
</div>
</FormAnt.Item>
<Row gutter={[16, 16]}>
<Col span={isMobile ? 6 : 24}>
<FormAnt.Item name="attachment">
<div>
<Upload.Dragger
name="file"
height={isMobile ? 100 : 150}
multiple={true}
customRequest={({ file, onSuccess }: any) => {
const formData = new FormData();
formData.append("file", file);
taskController
.addTaskFile({ task_id: undefined, files: [file] })
.then((response) => {
const fileId = response.data.file_ids[0];
setFileIds((prevFileIds): any => [
...prevFileIds,
fileId,
]);
onSuccess();
});
}}
>
{!isMobile ? (
<p className={`ant-upload-drag-icon`}>
<UploadOutlined
style={{ color: "rgba(249, 158, 44, 1)" }}
/>
</p>
) : (
<UploadOutlined />
)}
{!isMobile && (
<p
className="ant-upload-text"
style={{ color: "rgba(249, 158, 44, 1)" }}
>
Click or drag a file here to upload (only .jpeg .jpg
.png .pdf)
</p>
)}
</Upload.Dragger>
</div>
</FormAnt.Item>
</Col>
</Row>
</FormAnt>
</Modal>
</div>

@ -11,6 +11,13 @@ import {
import TabPane from "antd/es/tabs/TabPane";
import { timeZone } from "../../App";
import { useTaskHistory } from "../../Hooks/Tasks";
import TextArea from "antd/es/input/TextArea";
import { useEffect, useState } from "react";
import { taskController } from "../../API/LayoutApi/tasks";
import { useTeamData } from "../../Hooks/Teams";
import { TTeam } from "../../types/Team/TTeam";
import { EditOutlined, CaretRightOutlined } from "@ant-design/icons";
import { TSocket } from "../../types/common/TSocket";
// @ts-ignore
import closeIcon from "../../assets/closeIcon.png";
// @ts-ignore
@ -45,40 +52,28 @@ import forwardIcon from "../../assets/forward.png";
import driverIcon from "../../assets/drivericon.png";
// @ts-ignore
import userIcon from "../../assets/userIcon.png";
import TextArea from "antd/es/input/TextArea";
import { useState } from "react";
import { taskController } from "../../API/LayoutApi/tasks";
import {
QueryObserverResult,
RefetchOptions,
RefetchQueryFilters,
} from "react-query";
import { TPagination } from "../../types/common/TPagination";
import { useTeamData } from "../../Hooks/Teams";
import { TTeam } from "../../types/Team/TTeam";
import { EditOutlined } from "@ant-design/icons";
const TaskModal = ({
modalOpen,
setModalOpen,
recordTask,
setRecordTask,
uploadOpen,
setUploadOpen,
refetch,
socketData,
}: {
recordTask: TTask | undefined;
modalOpen: any;
setRecordTask: React.Dispatch<React.SetStateAction<TTask | undefined>>;
modalOpen: boolean;
setModalOpen: any;
uploadOpen: any;
uploadOpen: boolean;
setUploadOpen: any;
refetch: <TPageData>(
options?: (RefetchOptions & RefetchQueryFilters<TPageData>) | undefined
) => Promise<QueryObserverResult<TPagination<TTask[]>, unknown>>;
socketData: TSocket | undefined;
}) => {
const moment = require("moment-timezone");
const theme = localStorage.getItem("theme") === "true" ? true : false;
const [text, setText] = useState<string | undefined>(recordTask?.note);
const [pti, setPti] = useState<boolean | undefined>(recordTask?.pti);
const theme = localStorage.getItem("theme") === "true" ? true : false;
const [status, setStatus] = useState(recordTask?.status);
const [teamName, setTeamName] = useState(recordTask?.assigned_to?.name);
const { data, isLoading } = useTaskHistory(recordTask?.id);
@ -112,9 +107,41 @@ const TaskModal = ({
}
const teamData = useTeamData("");
const teams: MenuProps["items"] = teamData?.data?.map((item) => ({
// const teamData = useCustomerData({name: "", page: 1, page_size: 100});
const teams: MenuProps["items"] = teamData?.data?.map((item, index) => ({
key: item?.id,
label: <p>{item?.name}</p>,
label: (
<div
style={{
display: "flex",
alignItems: "center",
}}
>
<div
style={{
width: 10,
height: 10,
background: `rgb(${index * (255 / teamData?.data.length)}, ${
255 - index * (255 / teamData?.data.length)
}, 0)`,
padding: 5,
borderRadius: 5,
marginRight: 10,
}}
></div>
<div
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
width: "100%",
}}
>
<p>{item?.name}</p>
<p style={{ marginLeft: 30 }}>{item?.task_count_percentage}%</p>
</div>
</div>
),
onClick: () => teampatch(item),
}));
const items: MenuProps["items"] = [
@ -157,7 +184,11 @@ const TaskModal = ({
taskController
.taskPatch({ assigned_to_id: item?.id }, recordTask?.id)
.then(() => {
message.success({ content: "Success", duration: 1 });
message.success({
content: "Task forwarded successfully",
duration: 1,
});
setModalOpen(false);
});
};
@ -167,8 +198,24 @@ const TaskModal = ({
.then(() => {
message.success({ content: "Saved!" });
});
setModalOpen(!modalOpen);
};
const nextStatus = (status : string) => {
console.log();
}
useEffect(() => {
if (socketData && socketData.task) {
if (
socketData.type === "task_update" &&
socketData.task.id === recordTask?.id
) {
setRecordTask(socketData.task);
}
}
}, [socketData]);
return (
<Modal
onCancel={handleCancel}
@ -195,6 +242,9 @@ const TaskModal = ({
{status}
<EditOutlined style={{ marginLeft: 4 }} />
</button>
{/* <button onClick={e > nextStatus(status)}>
<CaretRightOutlined />
</button> */}
</Dropdown>
</div>
<div className="mdoal-actions">
@ -231,14 +281,13 @@ const TaskModal = ({
</div>
</div>
<div className="TaskModal-content">
<Tabs>
<Tabs style={{ marginLeft: 24 }}>
<TabPane
tab={
<span
style={{
display: "flex",
alignItems: "center",
marginLeft: 24,
}}
>
<img style={{ marginRight: 10 }} src={infoIcon} alt="" />
@ -365,12 +414,61 @@ const TaskModal = ({
dataSource={recordTask?.attachment_set?.map((u, i) => ({
no: i + 1,
...u,
created: moment(u?.updated_at)
.tz(timeZone)
.format("DD.MM.YYYY HH:mm"),
created: moment(u?.created_at)
?.tz(timeZone)
?.format("DD.MM.YYYY HH:mm"),
action: { ...u },
by: u,
}))}
columns={[
{
title: "User/Driver",
dataIndex: "by",
width: "25%",
key: 1,
ellipsis: {
showTitle: false,
},
render: (text: any) => (
<Tooltip
placement="topLeft"
title={
text?.uploaded_by_which_driver?.username ||
text?.uploaded_by_which_user?.username
}
>
<div style={{ display: "flex", alignItems: "center" }}>
{text?.uploaded_by_which_user ? (
<>
<img
src={getImageSource("user")}
alt=""
style={{ width: 15, height: 15, marginRight: 10 }}
/>
<p>
{text?.uploaded_by_which_user?.username
? text?.uploaded_by_which_user?.username
: ""}
</p>
</>
) : (
<>
<img
src={getImageSource("driver")}
alt=""
style={{ width: 20, height: 15, marginRight: 10 }}
/>
<p>
{text?.uploaded_by_which_driver?.name
? text?.uploaded_by_which_driver?.name
: ""}
</p>
</>
)}
</div>
</Tooltip>
),
},
{
title: "Name/Description",
dataIndex: "file_name",
@ -466,9 +564,8 @@ const TaskModal = ({
? { user: u?.user?.username }
: { driver: u?.driver?.name },
created: moment(u?.timestamp)
.tz(timeZone)
.format("DD.MM.YYYY HH:mm"),
// action: { ...u. },
?.tz(timeZone)
?.format("DD.MM.YYYY HH:mm"),
}))}
columns={[
{
@ -480,7 +577,10 @@ const TaskModal = ({
showTitle: false,
},
render: (text: any, record: any) => (
<Tooltip placement="topLeft" title={text}>
<Tooltip
placement="topLeft"
title={text?.user || text?.driver}
>
<div style={{ display: "flex", alignItems: "center" }}>
{text?.user ? (
<>
@ -490,7 +590,7 @@ const TaskModal = ({
style={{ width: 15, height: 15, marginRight: 10 }}
/>
<p>
{typeof text.user === "string" ? text.user : ""}
{typeof text?.user === "string" ? text?.user : ""}
</p>
</>
) : (

@ -18,7 +18,7 @@ import tt from "../../assets/tticon.svg";
import tagIcon from "../../assets/tagIcon.png";
// @ts-ignore
import tgIcon from "../../assets/telegram.png";
import { role } from "../../App";
import { isMobile, role } from "../../App";
const admin_id = localStorage.getItem("admin_id");
const TaskTable = ({
@ -59,6 +59,15 @@ const TaskTable = ({
});
}
};
const ptiPatch = (record: TTask) => {
Modal.confirm({
title: "Confirmation",
content: `Are you sure you want to change PTI?`,
onOk: () => {
taskController.taskPatch({ pti: !record.pti }, record.id);
},
});
};
const [isTextSelected, setIsTextSelected] = useState(false);
@ -118,7 +127,9 @@ const TaskTable = ({
{
title: "",
dataIndex: "no",
width: "4%",
width: isMobile ? "1%" : "5%",
fixed: isMobile ? "left" : false,
key: "1",
render: (text: any, record: TTask) => (
<div
style={{
@ -148,7 +159,9 @@ const TaskTable = ({
</div>
),
dataIndex: "no",
width: "5%",
width: isMobile ? "1%" : "5%",
fixed: isMobile ? "left" : false,
key: "2",
render: (text: any, record: TTask) => (
<div
style={{
@ -165,6 +178,7 @@ const TaskTable = ({
title: "Company",
dataIndex: "company",
width: "13%",
key: "3",
responsive: ["xl"],
ellipsis: {
showTitle: true,
@ -187,7 +201,8 @@ const TaskTable = ({
{
title: "Driver",
dataIndex: "customer",
width: "13%",
width: isMobile ? "5%" : "13%",
key: "4",
ellipsis: {
showTitle: false,
},
@ -200,7 +215,8 @@ const TaskTable = ({
{
title: "Service",
dataIndex: "service",
width: "7%",
width: isMobile ? "5%" : "7%",
key: "5",
ellipsis: {
showTitle: false,
},
@ -213,7 +229,8 @@ const TaskTable = ({
{
title: "Status",
dataIndex: "status",
width: "8%",
width: isMobile ? "5%" : "8%",
key: "6",
ellipsis: {
showTitle: false,
},
@ -230,7 +247,8 @@ const TaskTable = ({
{
title: "Team",
dataIndex: "assigned_to",
width: "8%",
width: isMobile ? "5%" : "8%",
key: "7",
ellipsis: {
showTitle: false,
},
@ -243,7 +261,8 @@ const TaskTable = ({
{
title: "Assignee",
dataIndex: "in_charge",
width: "12%",
width: isMobile ? "5%" : "12%",
key: "8",
ellipsis: {
showTitle: false,
},
@ -257,13 +276,24 @@ const TaskTable = ({
title: "PTI",
dataIndex: "pti",
width: "6%",
key: "8",
responsive: ["lg"],
render: (pti: boolean) => (pti ? "No need" : "Do"),
render: (pti: boolean, record: TTask) =>
pti ? (
<p onClick={(e) => ptiPatch(record)} className="status-Assigned">
No need
</p>
) : (
<p onClick={(e) => ptiPatch(record)} className="status-Rejected">
Do
</p>
),
},
{
title: "Note",
dataIndex: "note",
width: "12%",
key: "9",
responsive: ["lg"],
ellipsis: {
showTitle: false,
@ -278,6 +308,7 @@ const TaskTable = ({
title: "Created at",
dataIndex: "created",
width: "12%",
key: "10",
responsive: ["xxl"],
ellipsis: {
showTitle: false,
@ -291,10 +322,12 @@ const TaskTable = ({
{
title: "Actions",
dataIndex: "action",
width: "8%",
width: isMobile ? "3%" : "8%",
key: "11",
fixed: isMobile ? "right" : false,
render: (text: string, record: TTask) => {
return (
<div>
<div style={{ zIndex: 1000 }}>
{role === "Checker" ? (
<Space>
{record.status === "New" && (
@ -370,6 +403,9 @@ const TaskTable = ({
pagination={false}
loading={isLoading}
rowClassName={rowClassName}
scroll={
isMobile ? { x: "calc(800px + 40%)" } : { x: "calc(0px + 100%)" }
}
bordered
/>
</div>

@ -1,29 +1,19 @@
import { Modal, Upload, Button, Space } from "antd";
import { taskController } from "../../API/LayoutApi/tasks";
// @ts-ignore
import uploadfile from "../../assets/uploadfile.png";
// @ts-ignore
import createIcon from "../../assets/galkaIcon.png";
import { CloseOutlined } from "@ant-design/icons";
import { TTask } from "../../types/Tasks/TTasks";
import { useState } from "react";
import TextArea from "antd/es/input/TextArea";
import {
QueryObserverResult,
RefetchOptions,
RefetchQueryFilters,
} from "react-query";
import { TPagination } from "../../types/common/TPagination";
// @ts-ignore
import uploadfile from "../../assets/uploadfile.png";
// @ts-ignore
import createIcon from "../../assets/galkaIcon.png";
const TaskUploadModal = ({
uploadOpen,
recordTask,
setUploadOpen,
refetch,
}: {
refetch: <TPageData>(
options?: (RefetchOptions & RefetchQueryFilters<TPageData>) | undefined
) => Promise<QueryObserverResult<TPagination<TTask[]>, unknown>>;
recordTask: TTask | undefined;
uploadOpen: boolean;
setUploadOpen(open: boolean): void;
@ -60,7 +50,6 @@ const TaskUploadModal = ({
description: text,
})
.then(() => {
refetch();
setUploadOpen(!uploadOpen);
});
}}
@ -109,7 +98,7 @@ const TaskUploadModal = ({
letterSpacing: "-2%",
}}
>
Maximum file size is 10 MB
Maximum file size is 10 MB (.jpeg, .jpg, .pdf, .png files only)
</span>
</p>
</Upload.Dragger>

@ -1,12 +1,12 @@
import { useCallback, useEffect, useRef, useState } from "react";
import { useEffect, useRef, useState } from "react";
import AddTask from "./AddTask";
import { Button, Input, Select, Space, notification } from "antd";
import { Button, Input, Select, Space } 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";
import { isMobile, role, team_id } from "../../App";
//@ts-ignore
import addicon from "../../assets/addiconpng.png";
//@ts-ignore
@ -15,94 +15,38 @@ import refreshicon from "../../assets/refreshIcon.png";
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";
import { TSocket } from "../../types/common/TSocket";
const { Option } = Select;
const Task = () => {
const Task = ({ socketData }: { socketData: TSocket | undefined }) => {
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 [page, setPage] = useState(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 &&
socketData.task &&
((role !== "Checker" &&
(!team || team.includes(socketData?.task?.assigned_to?.id))) ||
role === "Checker") &&
(!status || status.includes(socketData.task.status))
(!status || status.includes(socketData?.task?.status))
) {
setCharacters((prev: TTask[] | undefined) => {
setCharacters((prev: any) => {
if (prev && prev?.length >= 15) {
prev?.pop();
}
if (socketData.type === "task_create") {
if (socketData.type === "task_create" && socketData.task) {
return [socketData.task, ...(prev || [])];
} else if (socketData.type === "task_update") {
if (role !== "Checker") {
const updatedData =
prev?.filter((b: TTask) => b.id !== socketData.task.id) || [];
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") {
@ -119,28 +63,28 @@ const Task = () => {
return data;
} else {
const data = (prev || []).map((b: TTask) =>
b.id === socketData.task.id ? socketData.task : b
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
(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) {
if (socketData.task?.assigned_to.id === team_id) {
return [socketData.task, ...(prev || [])];
} else {
const data = (prev || []).filter(
(b: TTask) => b.id !== socketData.task.id
(b: TTask) => b.id !== socketData.task?.id
);
return data;
}
} else {
const updatedData =
prev?.filter((b: TTask) => b.id !== socketData.task.id) || [];
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") {
@ -156,17 +100,11 @@ const Task = () => {
});
return data;
}
} else if (socketData.type === "callback_request") {
if (socketData?.callback_request) {
console.log("run");
openNotification("bottomRight", socketData?.callback_request);
}
}
return prev;
});
}
}, [socketData, openNotification]);
}, [socketData]);
const teamData = useTeamData("");
@ -175,12 +113,13 @@ const Task = () => {
label: item?.name,
value: item?.id,
}));
const page_size = isMobile ? 10 : 15;
const { data, isLoading, refetch } = useTasks({
search,
status,
team,
page,
page_size,
});
useEffect(() => {
if (data) {
@ -227,11 +166,9 @@ const Task = () => {
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}
@ -242,46 +179,44 @@ const Task = () => {
uploadOpen={uploadOpen}
setUploadOpen={setUploadOpen}
recordTask={recordTask}
setRecordTask={setRecordTask}
modalOpen={modalOpen}
socketData={socketData}
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
<img
style={{ marginRight: isMobile ? "0px" : "8px" }}
src={addicon}
alt=""
/>
{!isMobile && "Add Task"}
</button>
)}
<button
className={`btn-refresh-${theme && "dark"} d-flex`}
onClick={() => {
refetch();
connect();
// connect();
}}
>
<img style={{ marginRight: 8 }} src={refreshicon} alt="" />
Refresh
<img
style={{ marginRight: isMobile ? "-8px" : "8px" }}
src={refreshicon}
alt=""
/>
{!isMobile && "Refetch"}
</button>
</div>
</div>
<div className="filter d-flex">
<div className={`filter ${isMobile ? "mobile-filter" : "d-flex"}`}>
<div className="search-div">
<img src={IconSearch} alt="" />
<input
@ -292,7 +227,12 @@ const Task = () => {
/>
</div>
<Select
style={{ width: 260, marginLeft: 12 }}
style={{
width: 260,
marginLeft: 12,
marginTop: isMobile ? 10 : 0,
marginBottom: isMobile ? 10 : 0,
}}
placeholder="status"
onChange={(value: any) => setStatus(value)}
mode="multiple"
@ -322,6 +262,7 @@ const Task = () => {
type="primary"
icon={<StepBackwardOutlined />}
onClick={Previos}
disabled={data?.previous ? false : true}
></Button>
<Input
style={{ width: 50, textAlign: "right" }}
@ -329,7 +270,7 @@ const Task = () => {
onChange={(e) => {
let num = e.target.value;
if (Number(num) && num !== "0") {
setPage(num);
setPage(Number(num));
}
}}
/>
@ -337,6 +278,7 @@ const Task = () => {
type="primary"
icon={<StepForwardOutlined />}
onClick={Next}
disabled={data?.next ? false : true}
></Button>
</Space>
</Space>

@ -55,30 +55,7 @@ const TeamTable = ({
title: "Created at",
dataIndex: "created",
},
{
title: "Is Active",
dataIndex: "is_active",
render: (tag: boolean) => (
<Tag color={tag ? "geekblue" : "red"}>{tag ? "True" : "False"}</Tag>
),
filters: [
{
text: "True",
value: true,
},
{
text: "False",
value: false,
},
],
onFilter: (value: string | number | boolean, record: TTeam) => {
return record.is_active === value;
},
},
]}
rowClassName={(record, index) =>
index % 2 === 0 ? "odd-row" : "even-row"
}
/>
);
};

@ -2,7 +2,6 @@ import { Input, Modal, Form as FormAnt, Select, Upload } from "antd";
import { updateController } from "../../API/LayoutApi/update";
import { useState } from "react";
import { UploadOutlined } from "@ant-design/icons";
import { taskController } from "../../API/LayoutApi/tasks";
import { useCompanyData } from "../../Hooks/Companies";
import { useCustomerByComanyData } from "../../Hooks/Customers";
import {
@ -29,10 +28,9 @@ const AddUpdate = ({
refetch();
setOpen(!open);
};
const [fileIds, setFileIds] = useState([]);
const [companyName, setCompanyName] = useState<string>("");
const [customerName, setCustomerName] = useState<string>("");
const [companyId, setCompanyId] = useState<string>();
const [companyId, setCompanyId] = useState<number>();
const companyData = useCompanyData({ name: companyName });
const customerData = useCustomerByComanyData({
@ -40,7 +38,6 @@ const AddUpdate = ({
name: customerName,
});
const [imgname, setImgname] = useState<any>([]);
function handlePaste(event: any) {
const clipboardData = event.clipboardData || window.Clipboard;
if (clipboardData && clipboardData.items.length > 0) {
@ -76,7 +73,6 @@ const AddUpdate = ({
onOk={() => {
form.validateFields().then(async (values) => {
const updatedValues = { ...values };
updatedValues.attachment_ids = fileIds;
form.resetFields();
await updateController.addUpdateController(updatedValues);
setOpen(!open);
@ -187,7 +183,6 @@ const AddUpdate = ({
Click or drag file to this area to upload
</p>
</Upload.Dragger>
<p>{imgname.join(",\n")}</p>
</FormAnt.Item>
</FormAnt>
</Modal>

@ -1,6 +1,6 @@
import { useState } from "react";
import AddUpdate from "./AddUpdate";
import { Button, Select } from "antd";
import { Select } from "antd";
import UpdateTable from "./UpdateTable";
import { useUpdateData } from "../../Hooks/Update";
//@ts-ignore

@ -94,7 +94,7 @@ const UpdateTable = ({
company_name: CompanyData?.data?.find(
(company: any) => company.id === u.company_id
)?.name,
customer_name: CustomerData?.data?.find(
customer_name: CustomerData?.data?.data?.find(
(customer: any) => customer.id === u.customer_id
)?.name,
in_charge_name: AdminData?.data?.find(
@ -191,8 +191,8 @@ const UpdateTable = ({
<p className="status-in-progress">Checking</p>
)}
{status === "New" && <p className="status-new">New</p>}
{status === "Setup" && <p className="status-setup">Setup</p>}
{status === "Paper" && <p className="status-paper">Paper</p>}
{status === "Setup" && <p className="status-new">Setup</p>}
{status === "Paper" && <p className="status-new">Paper</p>}
</span>
),
},

@ -26,13 +26,11 @@ const TabPane = Tabs.TabPane;
type params = {
readonly id: string;
};
type MyObjectType = {
[key: string | number]: any;
};
const UserEdit = () => {
const { id } = useParams<params>();
const { data, refetch, status }: MyObjectType = useUserOne(id);
const { data, refetch, status } = useUserOne(id);
const onSubmit = async (value: any) => {
id && (await userController.userPatch(value, id));
@ -69,80 +67,83 @@ const UserEdit = () => {
{status === "loading" ? (
<Spin size="large" spinning={!data} />
) : data ? (
<Spin size="large" spinning={!data}>
<Space
direction="vertical"
size="middle"
style={{ display: "flex" }}
<Space
direction="vertical"
size="middle"
style={{ display: "flex" }}
>
<Tabs
defaultActiveKey="1"
activeKey={activeTab}
onChange={(key) => setActiveTab(key)}
>
<Tabs
defaultActiveKey="1"
activeKey={activeTab}
onChange={(key) => setActiveTab(key)}
<TabPane
tab={
<span style={{ display: "flex", alignItems: "center" }}>
<img
style={{ marginRight: 10 }}
src={activeTab === "1" ? infoIconActive : infoIcon}
alt=""
/>
Information
</span>
}
key="1"
>
<TabPane
tab={
<span style={{ display: "flex", alignItems: "center" }}>
<img
style={{ marginRight: 10 }}
src={activeTab === "1" ? infoIconActive : infoIcon}
alt=""
/>
Information
</span>
}
key="1"
<Space
direction="vertical"
size="middle"
style={{ display: "flex" }}
>
<Space
direction="vertical"
size="middle"
style={{ display: "flex" }}
<Form
name="basic"
layout="vertical"
wrapperCol={{ span: 16 }}
initialValues={{ ...data }}
onFinish={onSubmit}
autoComplete="off"
>
<Form
name="basic"
layout="vertical"
wrapperCol={{ span: 16 }}
initialValues={{ ...data }}
onFinish={onSubmit}
autoComplete="off"
>
<Row gutter={[16, 10]}>
<Col span={6}>
<Form.Item
wrapperCol={{ span: "100%" }}
label="First name"
name="first_name"
>
<Input readOnly />
</Form.Item>
</Col>
<Col span={6}>
<Form.Item
wrapperCol={{ span: "100%" }}
label="Last name"
name="last_name"
>
<Input readOnly />
</Form.Item>
</Col>
<Col span={6}>
<Form.Item
wrapperCol={{ span: "100%" }}
label="Username"
name="username"
>
<Input readOnly />
</Form.Item>
</Col>
<Col span={4}>
<Form.Item
wrapperCol={{ span: "100%" }}
label="Team"
name="team_id"
>
<Select options={TeamOption} />
</Form.Item>
</Col>
<Row gutter={[16, 10]}>
<Col span={6}>
<Form.Item
wrapperCol={{ span: "100%" }}
label="First name"
name="first_name"
>
<Input readOnly />
</Form.Item>
</Col>
<Col span={6}>
<Form.Item
wrapperCol={{ span: "100%" }}
label="Last name"
name="last_name"
>
<Input readOnly />
</Form.Item>
</Col>
<Col span={6}>
<Form.Item
wrapperCol={{ span: "100%" }}
label="Username"
name="username"
>
<Input readOnly />
</Form.Item>
</Col>
<Col span={4}>
<Form.Item
wrapperCol={{ span: "100%" }}
label="Team"
name="team_id"
>
<Select
options={TeamOption}
defaultValue={data?.team?.name}
/>
</Form.Item>
</Col>
{role === "Owner" && (
<Col span={4}>
<Form.Item
wrapperCol={{ span: "100%" }}
@ -154,31 +155,32 @@ const UserEdit = () => {
label: item?.name,
value: item?.id,
}))}
defaultValue={data?.role?.name}
/>
</Form.Item>
</Col>
</Row>
<Form.Item>
{role !== "Checker" && (
<Button
onClick={() => ClickDelete()}
type="primary"
style={{ marginRight: 10 }}
danger
>
Delete
</Button>
)}
<Button type="primary" htmlType="submit">
Submit
)}
</Row>
<Form.Item>
{role !== "Checker" && (
<Button
onClick={() => ClickDelete()}
type="primary"
style={{ marginRight: 10 }}
danger
>
Delete
</Button>
</Form.Item>
</Form>
</Space>
</TabPane>
</Tabs>
</Space>
</Spin>
)}
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
</Space>
</TabPane>
</Tabs>
</Space>
) : (
<Notfound />
)}

@ -21,8 +21,6 @@ const UserTable = ({
) => Promise<QueryObserverResult<TUser[], unknown>>;
}) => {
const navigate = useNavigate();
const TeamData = useTeamData("");
const Row = (record: TUser) => {
let isTextSelected = false;
document.addEventListener("selectionchange", () => {
@ -49,13 +47,8 @@ const UserTable = ({
onRow={(record) => Row(record)}
dataSource={data?.map((u, i) => ({
no: i + 1,
team: TeamData?.data?.map((team: any) => {
if (team.id === u?.team_id) {
return team?.name;
} else {
return null;
}
}),
team_name: u?.team?.name,
role_name: u?.role?.name,
action: { id: u.id },
...u,
}))}
@ -72,7 +65,11 @@ const UserTable = ({
},
{
title: "Team",
dataIndex: "team",
dataIndex: "team_name",
},
{
title: "Role",
dataIndex: "role_name",
},
{
title: "Is Active",

@ -1,10 +1,18 @@
import { useQuery } from "react-query";
import { callController } from "../../API/LayoutApi/callrequests";
export const useCallData = ({ status }: { status: string }) => {
export const useCallData = ({
status,
page,
page_size,
}: {
status: string;
page: number;
page_size: number;
}) => {
return useQuery(
[`callback-requests/`, { status }],
() => callController.read({ status }),
[`callback-requests/`, { status, page, page_size }],
() => callController.read({ status, page, page_size }),
{
refetchOnWindowFocus: false,
}

@ -11,8 +11,19 @@ export const useCompanyData = ({
}: TCompanyGetParams) => {
return useQuery(
[`companies/`, name, page, is_active],
() =>
companyController.read({ name: name, page: page, is_active: is_active }),
() => companyController.read({ name, page, is_active }),
{ refetchOnWindowFocus: false }
);
};
export const useCompanyPaginated = ({
name,
page,
is_active,
page_size,
}: TCompanyGetParams) => {
return useQuery(
[`companies/`, name, page, is_active, page_size],
() => companyController.readPaginated({ name, page, is_active, page_size }),
{ refetchOnWindowFocus: false }
);
};

@ -9,16 +9,18 @@ export const useCustomerData = ({
name,
page,
is_active,
pageSize,
page_size,
for_driver_request,
}: TCustomerGetParams) => {
return useQuery(
[`customers/`, name, page, is_active, pageSize],
[`customers/`, name, page, is_active, page_size, for_driver_request],
() =>
customerController.read({
name: name,
page: page,
is_active: is_active,
pageSize: pageSize,
name,
page,
is_active,
page_size,
for_driver_request,
}),
{ refetchOnWindowFocus: false }
);
@ -27,10 +29,11 @@ export const useCustomerData = ({
export const useCustomerByComanyData = ({
name,
id,
for_driver_request,
}: TCustomerByCompanyGetParams) => {
return useQuery(
[`customers-by-company/${id}`, name],
() => customerController.customerByCompany(id, name),
[`customers-by-company/${id}`, name, for_driver_request],
() => customerController.customerByCompany(id, name, for_driver_request),
{ refetchOnWindowFocus: false }
);
};

@ -4,10 +4,26 @@ import {
TRequestsGetParams,
} from "../../API/LayoutApi/requests";
export const useRequestsData = ({ search, status }: TRequestsGetParams) => {
export const useRequestsData = ({
search,
status,
page,
page_size,
for_driver_request,
}: TRequestsGetParams) => {
return useQuery(
[`driver-requests/`, { search, status }],
() => requestsController.read({ search, status }),
[
`driver-requests/`,
{ search, status, page, page_size, for_driver_request },
],
() =>
requestsController.read({
search,
status,
page,
page_size,
for_driver_request,
}),
{ refetchOnWindowFocus: false }
);
};

@ -1,15 +1,21 @@
import { useQuery } from "react-query";
import { TTasksGetParams, taskController } from "../../API/LayoutApi/tasks";
export const useTasks = ({ search, status, team, page }: TTasksGetParams) => {
export const useTasks = ({
search,
status,
team,
page,
page_size,
}: TTasksGetParams) => {
return useQuery(
[`tasks/`, search, status, team, page],
() => taskController.read({ search, status, team, page }),
[`tasks/`, search, status, team, page, page_size],
() => taskController.read({ search, status, team, page, page_size }),
{ refetchOnWindowFocus: false }
);
};
export const useTaskOne = (taskId: number) => {
export const useTaskOne = (taskId: number | undefined) => {
return useQuery(
[`task/${taskId}/`, taskId],
() => taskController.taskOne(taskId),

@ -2,19 +2,15 @@ import { useQuery } from "react-query";
import { teamController } from "../../API/LayoutApi/teams";
export const useTeamData = (name: string) => {
return useQuery(
[`teams/?name=${name}/`, name],
() => teamController.read(name),
{ refetchOnWindowFocus: false }
);
return useQuery([`teams/?name=${name}/`], () => teamController.read(name), {
refetchOnWindowFocus: false,
});
};
export const useTeamOne = (
teamId: number | string | undefined
): any => {
export const useTeamOne = (teamId: number | string | undefined): any => {
return useQuery(
[`team/${teamId || "all"}`, teamId],
() => teamController.teamOne(teamId),
{ refetchOnWindowFocus: false }
);
};
};

@ -1,17 +1,15 @@
import { useQuery } from "react-query";
import { TUsersGetParams, userController } from "../../API/LayoutApi/users";
export const useUserData = ({name, team, role}: TUsersGetParams) => {
export const useUserData = ({ name, team, role }: TUsersGetParams) => {
return useQuery(
[`users/admins/`, {name, team, role}],
() => userController.read({name, team, role}),
[`users/admins/`, { name, team, role }],
() => userController.read({ name, team, role }),
{ refetchOnWindowFocus: false }
);
};
export const useUserOne = (
userId: number | string | undefined
): any => {
export const useUserOne = (userId?: number | string) => {
return useQuery(
[`user/${userId || "all"}`, userId],
() => userController.userOne(userId),
@ -19,12 +17,10 @@ export const useUserOne = (
);
};
export const useCheckUser = (
username: string
): any => {
export const useCheckUser = (username: string): any => {
return useQuery(
[`user/${username}/`],
() => userController.CheckUsername(username),
{ refetchOnWindowFocus: false }
);
};
};

@ -1,140 +1,17 @@
import { MenuProps } from "antd";
import { Link } from "react-router-dom";
import Company from "../Components/Companies/Companies";
import CompanyEdit from "../Components/Companies/CompaniesEdit";
import Customer from "../Components/Customers/Customers";
import CustomerEdit from "../Components/Customers/CustomersEdit";
import Service from "../Components/Services/Services";
import ServiceEdit from "../Components/Services/ServiceEdit";
import Task from "../Components/Tasks/Tasks";
import TeamEdit from "../Components/Teams/TeamEdit";
import Team from "../Components/Teams/Teams";
import User from "../Components/Users/Users";
import UserEdit from "../Components/Users/UserEdit";
import MenuItem from "antd/es/menu/MenuItem";
import Stat from "../Components/Statistics/Statistic";
import Profile from "../Components/Profile/Profile";
import Update from "../Components/Updates/Update";
import UpdateEdit from "../Components/Updates/UpdateEdit";
// @ts-ignore
import taskIcon from "../assets/tasknavicon.png";
// @ts-ignore
import companyIcon from "../assets/companynavicon.png";
// @ts-ignore
import serviceIcon from "../assets/servicenavicon.png";
// @ts-ignore
import teamIcon from "../assets/teamnavicon.png";
// @ts-ignore
import statisticIcon from "../assets/statnavicon.png";
// @ts-ignore
import updateIcon from "../assets/updatenavicon.png";
// @ts-ignore
import userIcon from "../assets/usernavicon.png";
// @ts-ignore
import driverIcon from "../assets/customersIcon.png";
// @ts-ignore
import requestIcon from "../assets/requestIcon.png";
// @ts-ignore
import callIcon from "../assets/callIcon.png";
import Requests from "../Components/Requests/Requests";
import Call from "../Components/CallRequests/Call";
const loc: any = localStorage.getItem("user");
const role = JSON.parse(loc)?.role;
type MenuItem = Required<MenuProps>["items"][number];
function getItem(
label: React.ReactNode,
key: React.Key,
icon?: React.ReactNode,
children?: MenuItem[]
): MenuItem {
return {
key,
icon,
children,
label,
} as MenuItem;
}
export const allMenu: MenuItem[] = [
getItem(<Link to="/">Tasks</Link>, "/", <img alt="" src={taskIcon} />),
getItem(
<Link to="companies/">Companies</Link>,
"companies/",
<img alt="" src={companyIcon} />
),
getItem(
<Link to="customers/">Drivers</Link>,
"customers/",
<img alt="" src={driverIcon} />
),
getItem(
<Link to="services/">Services</Link>,
"services/",
<img alt="" src={serviceIcon} />
),
];
if (role === "Tech Support") {
allMenu.push(
getItem(
<Link to="teams/">Teams</Link>,
"teams/",
<img alt="" src={teamIcon} />
),
getItem(
<Link to="updates/">Updates</Link>,
"updates/",
<img alt="" src={updateIcon} />
),
getItem(
<Link to="requests/">Driver Requests</Link>,
"requests/",
<img alt="" src={requestIcon} />
),
getItem(
<Link to="call/">Call Requests</Link>,
"call/",
<img alt="" src={callIcon} />
)
);
}
if (role === "Owner") {
allMenu.push(
getItem(
<Link to="users/">Users</Link>,
"users/",
<img alt="" src={userIcon} />
),
getItem(
<Link to="teams/">Teams</Link>,
"teams/",
<img alt="" src={teamIcon} />
),
getItem(
<Link to="stats/">Statistics</Link>,
"stats/",
<img alt="" src={statisticIcon} />
),
getItem(
<Link to="updates/">Updates</Link>,
"updates/",
<img alt="" src={updateIcon} />
),
getItem(
<Link to="requests/">Driver Requests</Link>,
"requests/",
<img alt="" src={requestIcon} />
),
getItem(
<Link to="call/">Call Requests</Link>,
"call/",
<img alt="" src={callIcon} />
)
);
}
type TItems = {
path: string;
@ -173,11 +50,6 @@ export const mainItems: TItems[] = [
component: <ServiceEdit />,
key: "/service/:id/",
},
{
path: "/",
component: <Task />,
key: "tasks",
},
];
export const superItems: TItems[] = [
@ -221,14 +93,4 @@ export const superItems: TItems[] = [
component: <UpdateEdit />,
key: "/update/:id/",
},
{
path: "/requests/",
component: <Requests />,
key: "/requests/",
},
{
path: "/call/",
component: <Call />,
key: "/call/",
},
];

@ -0,0 +1,147 @@
export const dark = {
components: {
Table: {
colorBgContainer: "#202020",
colorText: "#BBBBBB",
headerColor: "#BBBBBB",
borderColor: "#3A3A3A",
headerSplitColor: "#3A3A3A",
rowHoverBg: "#333333",
colorBorder: "#3A3A3A",
},
Layout: {
bodyBg: "#181818",
},
Input: {
colorBgContainer: "#2A2A2A",
colorBgContainerDisabled: "#2A2A2A",
colorText: "#BBBBBB",
colorTextPlaceholder: "#BBBBBB",
colorBorder: "#3A3A3A",
colorFillSecondary: "rgba(0, 0, 0, 0.02)",
activeBorderColor: "#3A3A3A",
activeShadow: "#3A3A3A",
hoverBorderColor: "#3A3A3A",
},
Select: {
colorBgContainer: "#2A2A2A",
colorText: "#BBBBBB",
colorTextPlaceholder: "#BBBBBB",
colorBorder: "rgba(150, 150, 150, 0.493)",
colorPrimaryHover: "rgba(249, 158, 44, 1)",
colorIconHover: "#BBB",
optionSelectedBg: "#2A2A2A",
colorBgElevated: "#333",
controlOutline: "none",
optionActiveBg: "#333333",
colorTextQuaternary: "#3A3A3A",
},
Button: {
colorBorderSecondary: "rgba(249, 158, 44, 1)",
colorPrimary: "rgba(249, 158, 44, 1)",
colorPrimaryHover: "#BBBBBB",
colorIcon: "rgba(249, 158, 44, 1)",
colorIconHover: "rgba(249, 158, 44, 1)",
primaryShadow: "none",
dangerShadow: "none",
colorTextDisabled: "#AAAAAA",
borderColorDisabled: "#3A3A3A",
},
// Form: {
// labelColor: "#BBBBBB",
// },
Tabs: {
itemColor: "#BBBBBB",
itemHoverColor: "#FFFFFF",
itemSelectedColor: "rgba(249, 158, 44, 1)",
colorPrimaryActive: "rgba(249, 158, 44, 1)",
inkBarColor: "rgba(249, 158, 44, 1)",
},
Modal: {
contentBg: "#3A3A3A",
headerBg: "#3A3A3A",
titleColor: "#FFFFFF",
colorText: "#BBBBBB",
colorBgTextActive: "#BBBBBB",
colorBgTextHover: "#BBBBBB",
},
Menu: {
darkItemSelectedBg: "#3A3A3A",
colorBgContainer: "#fff",
},
Switch: {
colorPrimary: "#565656",
colorPrimaryHover: "#737373",
},
Radio: {
colorText: "#737373",
colorBorder: "#3A3A3A",
colorPrimaryActive: "#BBBBBB",
buttonCheckedBg: "rgba(249, 158, 44, 1)",
colorPrimaryHover: "#737373",
colorPrimary: "#565656",
},
Dropdown: {
colorBgContainer: "#3A3A3A",
colorText: "#BBBBBB",
colorPrimaryHover: "#565656",
colorPrimary: "#333333",
},
DatePicker: {
colorBgContainer: "#3A3A3A",
colorBgElevated: "#3A3A3A",
colorText: "#BBBBBB",
colorTextPlaceholder: "#BBBBBB",
colorIcon: "#fff",
colorIconHover: "#fff",
colorPrimary: "rgba(249, 158, 44, 1)",
hoverBorderColor: "#BBBBBB",
},
Empty: {
colorText: "rgba(249, 158, 44, 1)",
colorTextDisabled: "rgba(249, 158, 44, 1)",
},
},
token: {
fontFamily: "Inter, sans-serif",
colorText: "#bbb",
borderRadius: 8,
},
};
export const light = {
components: {
Select: {
colorTextPlaceholder: "rgba(155, 157, 170, 1)",
colorPrimary: "rgba(249, 158, 44, 1)",
colorPrimaryHover: "rgba(249, 158, 44, 1)",
},
Tabs: {
inkBarColor: "rgba(249, 158, 44, 1)",
itemSelectedColor: "rgba(24, 26, 41, 1)",
itemHoverColor: "rgba(24, 26, 41, 1)",
},
Input: {
hoverBorderColor: "rgba(249, 158, 44, 1)",
activeBorderColor: "rgba(249, 158, 44, 1)",
colorTextPlaceholder: "rgba(155, 157, 170, 1)",
},
Upload: {
colorPrimaryHover: "rgba(249, 158, 44, 1)",
},
Button: {
colorPrimary: "rgba(249, 158, 44, 1)",
colorPrimaryHover: "rgba(249, 158, 44, 1)",
},
Textarea: {
colorBorder: "0px 1px 3px 0px rgba(20, 22, 41, 0.1)",
},
Menu: {
darkItemSelectedBg: "rgba(255, 255, 255, 0.08)",
},
},
token: {
fontFamily: "Inter, sans-serif",
color: "#262626",
borderRadius: 8,
},
};

@ -11,4 +11,5 @@ export type TRequests = {
is_active: boolean;
created_at: string;
updated_at: string;
telegram_user_link: string;
};

@ -1,3 +1,5 @@
import { TUser } from "../User/TUser";
type data = {
id: number;
name?: string;
@ -21,7 +23,19 @@ export type TTask = {
assigned_to: AssignedTo;
in_charge: InCharge;
forwarded_from: { id: number; name: string };
attachment_set: any[];
attachment_set?: TAttachment[];
};
export type TAttachment = {
created_at: string;
id: number;
description: string;
file_name: string;
path: string;
updated_at: string;
uploaded_by: number;
uploaded_by_which_driver?: { id: number; name: string } | null;
uploaded_by_which_user?: TUser | null;
};
export interface AssignedTo {

@ -1,6 +1,8 @@
export type TTeam = {
id: number;
name: string;
is_active: boolean;
created_at: Date;
}
id: number;
name: string;
is_active: boolean;
created_at: Date;
task_count_percentage: number;
task_count?: number;
};

@ -1,8 +1,8 @@
export type TUser = {
id: number;
username: string;
team_id: number | null;
role_id: number[];
team: {id: number, name: string}
role: {id: number, name: string}
first_name: string | '';
last_name: string | '';
is_active: boolean;

@ -1,6 +1,6 @@
export type TPagination<T> = {
data: T;
page_size: number;
};
data: T;
page_size: number;
next: string | null;
previous: string | null;
};

@ -0,0 +1,11 @@
import { TCall } from "../CallRequests/TCall";
import { TRequests } from "../Requests/TRequests";
import { TTask } from "../Tasks/TTasks";
export type TSocket = {
type: string;
task?: TTask;
callback_request?: TCall;
driver_request?: TRequests;
};

@ -5350,6 +5350,11 @@ fs.realpath@^1.0.0:
resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz"
integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
fsevents@^2.3.2, fsevents@~2.3.2:
version "2.3.3"
resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz"
integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
function-bind@^1.1.1:
version "1.1.1"
resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz"

Loading…
Cancel
Save