19-06 deploy

main
tteld 5 months ago
parent d12ed32c04
commit 82b59d8c54

Binary file not shown.

@ -43,6 +43,9 @@ export type TTaskPostFileParams = {
shift_update_id?: number;
description?: string;
};
interface TMessageResponse {
message: string;
}
export const taskController = {
async read(filterObject: TTasksGetParams) {
@ -76,12 +79,22 @@ export const taskController = {
},
async taskPatch(obj: TTasksPutParams, task_id: number | undefined) {
const { data } = await instance
.put<TTask>(`task/${task_id}/`, obj)
.then((u) => {
return u;
});
return data;
try {
const response = await instance.put<TTask | TMessageResponse>(
`task/${task_id}/`,
obj
);
if (response.status === 202) {
const data = response.data as TMessageResponse;
message.success({ content: data.message });
}
return { data: response?.data as TTask, status: response?.status };
} catch (error: any) {
return {
data: error?.response?.data.data,
status: error?.response.status,
};
}
},
async addTaskController(obj: TTasksPostParams) {

@ -1,7 +1,7 @@
import axios from "axios";
// const instance = axios.create({
// baseURL: "http://10.10.10.12:8080/api/v1/",
// baseURL: "http://10.10.10.19:8080/api/v1/",
// });
const instance = axios.create({
baseURL: "https://api.tteld.co/api/v1/",

@ -25,21 +25,20 @@ export const RegisterApi = async (value: registerInterface) => {
timezone: data?.data.timezone,
role: data?.data.role,
};
message.success({
content: "Email sent successfully!",
duration: 3,
});
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);
document.location.replace("/");
return status;
} catch (error:any) {
setTimeout(() => {
message.error({ content: ' ', duration: 2 });
}, 1000);
}
} catch (e) {}
};
export const validateUsername = async (value: any) => {
const {status} = await instance.get(`users/check/${value}/`);
return status
const { status } = await instance.get(`users/check/${value}/`);
return status;
};

@ -73,7 +73,7 @@ const App: React.FC = () => {
setData(data);
});
}
}, [admin_id]);
}, []);
useEffect(() => {
if (data) {
@ -137,8 +137,8 @@ const App: React.FC = () => {
<img alt="" src={requestIcon} />
),
getItem(
<Link to="call/">Call Requests</Link>,
"call/",
<Link to="calls/">Call Requests</Link>,
"calls/",
<img alt="" src={callIcon} />
)
);
@ -209,8 +209,52 @@ const App: React.FC = () => {
</Menu.Item>
</Menu>
);
// const [isOnline, setIsOnline] = useState<boolean>(navigator.onLine);
// const reconnectingMessageKey = "reconnectingMessage";
// const reconnectingMessageContent = "Reconnecting...";
// useEffect(() => {
// let reconnectingTimeout: NodeJS.Timeout | null = null;
// const handleOnlineStatus = () => {
// setIsOnline(true);
// message.success({ content: "Reconnected!" });
// if (isOnline === false) {
// message.destroy(reconnectingMessageKey);
// }
// if (reconnectingTimeout) {
// clearTimeout(reconnectingTimeout);
// }
// };
// const handleOfflineStatus = () => {
// setIsOnline(false);
// if (isOnline !== false) {
// message.loading({
// content: reconnectingMessageContent,
// key: reconnectingMessageKey,
// duration: 0,
// });
// reconnectingTimeout = setTimeout(() => {
// message.destroy(reconnectingMessageKey);
// }, 30 * 60 * 1000); // 30 minutes
// }
// };
// window.addEventListener("online", handleOnlineStatus);
// window.addEventListener("offline", handleOfflineStatus);
// return () => {
// window.removeEventListener("online", handleOnlineStatus);
// window.removeEventListener("offline", handleOfflineStatus);
// if (reconnectingTimeout) {
// clearTimeout(reconnectingTimeout);
// }
// };
// }, [isOnline]);
let taskSocket: WebSocket;
const [isLive, setIslive] = useState(true);
const [isLive, setIslive] = useState(false);
const [socketData, setSocketData] = useState<any>();
const connect = async () => {
try {
@ -219,7 +263,7 @@ const App: React.FC = () => {
admin_id
) {
// taskSocket = new WebSocket(
// `ws://10.10.10.12:8080/global/?user_id=${admin_id}`
// `ws://10.10.10.19:8080/global/?user_id=${admin_id}`
// );
taskSocket = new WebSocket(
`wss://api.tteld.co/global/?user_id=${admin_id}`
@ -236,6 +280,7 @@ const App: React.FC = () => {
console.error("WebSocket error:", errorEvent);
});
taskSocket.addEventListener("close", (event) => {
console.log("WebSocket: clocse");
setIslive(false);
});
}
@ -245,6 +290,15 @@ const App: React.FC = () => {
useEffect(() => {
connect();
}, []);
// function checkConnection() {
// if (!isLive) {
// connect();
// }
// }
// setInterval(checkConnection, 5000);
const [api, contextHolder] = notification.useNotification();
const openNotification = useCallback(
(placement: NotificationPlacement, data: TCall) => {
@ -436,7 +490,13 @@ const App: React.FC = () => {
<Route
key={"task"}
path="/"
element={<Task socketData={socketData} />}
element={
<Task
socketData={socketData}
connect={connect}
isLive={isLive}
/>
}
/>
{mainItems &&
mainItems.map((u) => (

@ -38,12 +38,10 @@ const Register: React.FC = () => {
}
});
} else if (status === 200) {
setTimeout(() => {
message.error({
content: "This username already exists!",
duration: 2,
});
}, 1000);
message.error({
content: "This username already exists!",
duration: 2,
});
}
});
};
@ -72,55 +70,55 @@ const Register: React.FC = () => {
>
<h1>Sign up</h1>
{/* { && ( */}
<Card
bodyStyle={{ background: "rgb(250, 250, 250)" }}
title={emailDom}
className="login-form-card "
style={{ width: 600, textAlign: "left" }}
<Card
bodyStyle={{ background: "rgb(250, 250, 250)" }}
title={emailDom}
className="login-form-card "
style={{ width: 600, textAlign: "left" }}
>
<Space
direction="vertical"
size="middle"
style={{ display: "flex", gap: "20px", margin: "auto" }}
>
<Space
direction="vertical"
size="middle"
style={{ display: "flex", gap: "20px", margin: "auto" }}
>
<Row gutter={24}>
<Col span={12}>
<Form.Item
name="first_name"
rules={[
{
required: true,
message: "Please input your first name!",
whitespace: true,
},
]}
>
<Input
// prefix={<UserOutlined className="site-form-item-icon" />}
placeholder="First name"
/>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name="last_name"
rules={[
{
required: true,
message: "Please input your last name!",
whitespace: true,
},
]}
>
<Input
// prefix={<UserOutlined className="site-form-item-icon" />}
placeholder="Last name"
/>
</Form.Item>
</Col>
</Row>
<Row gutter={24}>
<Col span={12}>
<Form.Item
name="first_name"
rules={[
{
required: true,
message: "Please input your first name!",
whitespace: true,
},
]}
>
<Input
// prefix={<UserOutlined className="site-form-item-icon" />}
placeholder="First name"
/>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name="last_name"
rules={[
{
required: true,
message: "Please input your last name!",
whitespace: true,
},
]}
>
<Input
// prefix={<UserOutlined className="site-form-item-icon" />}
placeholder="Last name"
/>
</Form.Item>
</Col>
</Row>
<Form.Item
<Form.Item
name="email"
rules={[
{
@ -149,119 +147,119 @@ const Register: React.FC = () => {
)}
</Form.Item>
<Form.Item
name="username"
tooltip="What do you want others to call you?"
rules={[
{
required: true,
message: "Please input your username!",
whitespace: true,
},
]}
>
<Input
prefix={<UserOutlined className="site-form-item-icon" />}
placeholder="Username"
/>
</Form.Item>
<Form.Item
name="username"
tooltip="What do you want others to call you?"
rules={[
{
required: true,
message: "Please input your username!",
whitespace: true,
},
]}
>
<Input
prefix={<UserOutlined className="site-form-item-icon" />}
placeholder="Username"
/>
</Form.Item>
<Form.Item
name="password"
rules={[
{
required: true,
message: "Please input your password!",
<Form.Item
name="password"
rules={[
{
required: true,
message: "Please input your password!",
},
{
min: 8,
message:
"Your password must contain at least 8 characters.",
},
() => ({
validator(_, value) {
if (!value || /[^0-9]+/.test(value)) {
return Promise.resolve();
}
return Promise.reject(
new Error("Your password cant be entirely numeric.")
);
},
{
min: 8,
message:
"Your password must contain at least 8 characters.",
}),
() => ({
validator(_, value) {
// Список общеупотребимых паролей (пример)
const commonPasswords = common;
if (!value || !commonPasswords.includes(value)) {
return Promise.resolve();
}
return Promise.reject(
new Error(
"Your password cant be a commonly used password."
)
);
},
() => ({
validator(_, value) {
if (!value || /[^0-9]+/.test(value)) {
return Promise.resolve();
}
return Promise.reject(
new Error("Your password cant be entirely numeric.")
);
},
}),
() => ({
validator(_, value) {
// Список общеупотребимых паролей (пример)
const commonPasswords = common;
if (!value || !commonPasswords.includes(value)) {
return Promise.resolve();
}
return Promise.reject(
new Error(
"Your password cant be a commonly used password."
)
);
},
}),
({ getFieldValue }) => ({
validator(_, value) {
const personalInfo = getFieldValue("username");
if (!value || !value.includes(personalInfo)) {
return Promise.resolve();
}
return Promise.reject(
new Error(
"Your password cant be too similar to your other personal information."
)
);
},
}),
]}
hasFeedback
>
<Input.Password
prefix={<LockOutlined className="site-form-item-icon" />}
type="password"
placeholder="Password"
/>
</Form.Item>
}),
({ getFieldValue }) => ({
validator(_, value) {
const personalInfo = getFieldValue("username");
if (!value || !value.includes(personalInfo)) {
return Promise.resolve();
}
return Promise.reject(
new Error(
"Your password cant be too similar to your other personal information."
)
);
},
}),
]}
hasFeedback
>
<Input.Password
prefix={<LockOutlined className="site-form-item-icon" />}
type="password"
placeholder="Password"
/>
</Form.Item>
<Form.Item
name="password_confirm"
dependencies={["password"]}
hasFeedback
rules={[
{
required: true,
message: "Please confirm your password!",
<Form.Item
name="password_confirm"
dependencies={["password"]}
hasFeedback
rules={[
{
required: true,
message: "Please confirm your password!",
},
({ getFieldValue }) => ({
validator(_, value) {
if (!value || getFieldValue("password") === value) {
return Promise.resolve();
}
return Promise.reject(
new Error(
"The new password that you entered do not match!"
)
);
},
({ getFieldValue }) => ({
validator(_, value) {
if (!value || getFieldValue("password") === value) {
return Promise.resolve();
}
return Promise.reject(
new Error(
"The new password that you entered do not match!"
)
);
},
}),
]}
>
<Input.Password
prefix={<LockOutlined className="site-form-item-icon" />}
type="password"
placeholder="Re-enter Password"
/>
</Form.Item>
}),
]}
>
<Input.Password
prefix={<LockOutlined className="site-form-item-icon" />}
type="password"
placeholder="Re-enter Password"
/>
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit" loading={loading}>
Confirm
</Button>
</Form.Item>
</Space>
</Card>
<Form.Item>
<Button type="primary" htmlType="submit" loading={loading}>
Confirm
</Button>
</Form.Item>
</Space>
</Card>
{/* )} */}
</Space>
</Form>

@ -11,6 +11,7 @@ import {
Col,
Input,
Button,
Select,
} from "antd";
import { customerController } from "../../API/LayoutApi/customers";
import Notfound from "../../Utils/Notfound";
@ -20,6 +21,17 @@ import { useState } from "react";
import infoIcon from "../../assets/infoIcon.png";
// @ts-ignore
import infoIconActive from "../../assets/infoIconActive.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 { useCompanyData } from "../../Hooks/Companies";
const TabPane = Tabs.TabPane;
@ -29,11 +41,12 @@ type params = {
const CustomerEdit = () => {
const { id } = useParams<params>();
const { data, refetch, status } = useCustomerOne(id);
const { data, status } = useCustomerOne(id);
let navigate = useNavigate();
const onSubmit = async (value: any) => {
value.company_id = companyVal;
await customerController.customerPatch(value, id);
navigate(-1);
window.location.replace("/#/customers/");
};
const ClickDelete = () => {
@ -46,6 +59,28 @@ const CustomerEdit = () => {
}
};
const [activeTab, setActiveTab] = useState("1");
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;
}
};
const [companyName, setCompanyName] = useState<string>("");
const [companyVal, setCompanyVal] = useState<any>();
const { data: companyData } = useCompanyData({ name: companyName });
return (
<div>
<Spin size="large" spinning={!data}>
@ -96,9 +131,35 @@ const CustomerEdit = () => {
wrapperCol={{ span: "100%" }}
label="Company"
>
<Input
<Select
defaultValue={data?.company?.name}
readOnly
onSearch={(value: any) => setCompanyName(value)}
onChange={(e: any) => {
setCompanyVal(e);
}}
options={companyData?.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,
}))}
filterOption={false}
autoClearSearchValue={false}
allowClear
// value={companyName}
/>
</Form.Item>
</Col>

@ -0,0 +1,45 @@
import { TTask } from "../../types/Tasks/TTasks";
import { Modal } from "antd";
import TaskTable from "./TaskTable";
const ErrorUncompletedTasksModal = ({
errorModal,
setErrorModal,
uncomletedData,
}: {
uncomletedData: TTask[] | undefined;
errorModal: boolean;
setErrorModal: any;
}) => {
const handleCancel = () => {
setErrorModal(!errorModal);
};
return (
<Modal
onCancel={handleCancel}
footer={null}
open={errorModal}
width={1400}
maskClosable={true}
>
<div className="uncomleted-tasks-modal">
<p
className="p-driver"
style={{ textAlign: "center", marginBottom: 10, color: "red" }}
>
You have unfinished tasks. You cannot assign another one until they
are completed.
</p>
<TaskTable
data={{ characters: uncomletedData }}
showTaskModal={false}
showErrorModal={false}
isLoading={false}
setErrorModal={setErrorModal}
/>
</div>
</Modal>
);
};
export default ErrorUncompletedTasksModal;

@ -16,7 +16,7 @@ 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 { EditOutlined } from "@ant-design/icons";
import { TSocket } from "../../types/common/TSocket";
// @ts-ignore
import closeIcon from "../../assets/closeIcon.png";
@ -201,9 +201,9 @@ const TaskModal = ({
setModalOpen(!modalOpen);
};
const nextStatus = (status : string) => {
console.log();
}
// const nextStatus = (status : string) => {
// console.log();
// }
useEffect(() => {
if (socketData && socketData.task) {

@ -25,12 +25,16 @@ const TaskTable = ({
data,
isLoading,
showTaskModal,
showErrorModal,
setErrorModal,
}: {
data: {
characters: TTask[] | undefined;
};
showTaskModal: any;
showErrorModal: any;
isLoading: boolean;
setErrorModal: React.Dispatch<React.SetStateAction<boolean>>;
}) => {
const moment = require("moment");
const statusClick = (record: any) => {
@ -42,7 +46,13 @@ const TaskTable = ({
const value = {
status: "Checking",
};
taskController.taskPatch(value, record.id);
taskController
.taskPatch(value, record?.id)
.then((response: { data: TTask; status: number }) => {
if (response?.status == 403) {
showErrorModal(response);
}
});
},
});
}
@ -54,7 +64,9 @@ const TaskTable = ({
const value = {
status: "Done",
};
taskController.taskPatch(value, record.id);
taskController.taskPatch(value, record.id).then(() => {
setErrorModal(false);
});
},
});
}
@ -138,7 +150,7 @@ const TaskTable = ({
justifyContent: "space-around",
}}
>
{record.via_telegram && (
{record?.via_telegram && (
<Tooltip placement="topLeft" title={"Created via Telegram"}>
<img src={tgIcon} alt="" style={{ width: 20, height: 20 }} />
</Tooltip>
@ -162,7 +174,7 @@ const TaskTable = ({
width: isMobile ? "1%" : "5%",
fixed: isMobile ? "left" : false,
key: "2",
render: (text: any, record: TTask) => (
render: (text?: any, record?: TTask) => (
<div
style={{
display: "flex",
@ -220,9 +232,9 @@ const TaskTable = ({
ellipsis: {
showTitle: false,
},
render: (item: { title: string; id: number }, record: TTask) => (
render: (item?: { title?: string; id: number }, record?: TTask) => (
<Tooltip placement="topLeft" title={item?.title}>
{item.title}
{item?.title}
</Tooltip>
),
},
@ -234,7 +246,7 @@ const TaskTable = ({
ellipsis: {
showTitle: false,
},
render: (status: string) => (
render: (status?: string) => (
<span>
{status === "Done" && <p className="status-done">Done</p>}
{status === "Checking" && (

@ -16,9 +16,18 @@ import IconSearch from "../../assets/searchIcon.png";
import TaskModal from "./TaskModal";
import TaskUploadModal from "./TaskUploadModal";
import { TSocket } from "../../types/common/TSocket";
import ErrorUncompletedTasksModal from "./ErrorUncompletedTasksModal";
const { Option } = Select;
const Task = ({ socketData }: { socketData: TSocket | undefined }) => {
const Task = ({
socketData,
connect,
isLive,
}: {
socketData: TSocket | undefined;
connect: () => Promise<void>;
isLive: boolean;
}) => {
const [open, setOpen] = useState(false);
const [modalOpen, setModalOpen] = useState(false);
const [characters, setCharacters] = useState<TTask[] | undefined>();
@ -27,6 +36,8 @@ const Task = ({ socketData }: { socketData: TSocket | undefined }) => {
const [status, setStatus] = useState<any>();
const [page, setPage] = useState(1);
const [uploadOpen, setUploadOpen] = useState(false);
const [errorModal, setErrorModal] = useState(false);
const [uncomletedData, setUncomletedData] = useState<TTask[]>();
useEffect(() => {
if (
@ -34,8 +45,8 @@ const Task = ({ socketData }: { socketData: TSocket | undefined }) => {
socketData.task &&
((role !== "Checker" &&
(!team || team.includes(socketData?.task?.assigned_to?.id))) ||
role === "Checker") &&
(!status || status.includes(socketData?.task?.status))
role === "Checker")
// &&(!status || status.includes(socketData?.task?.status))
) {
setCharacters((prev: any) => {
if (prev && prev?.length >= 15) {
@ -139,6 +150,11 @@ const Task = ({ socketData }: { socketData: TSocket | undefined }) => {
setRecordTask(e);
setModalOpen(true);
};
const showErrorModal = (e: { data: TTask[]; status: number }) => {
setUncomletedData(e?.data);
setErrorModal(true);
};
const Next = () => {
const a = Number(page) + 1;
@ -164,6 +180,7 @@ const Task = ({ socketData }: { socketData: TSocket | undefined }) => {
}, 1000);
};
const theme = localStorage.getItem("theme") === "true" ? true : false;
return (
<div>
{open && <AddTask open={open} setOpen={setOpen} />}
@ -185,6 +202,13 @@ const Task = ({ socketData }: { socketData: TSocket | undefined }) => {
setModalOpen={setModalOpen}
/>
)}
{errorModal && (
<ErrorUncompletedTasksModal
errorModal={errorModal}
setErrorModal={setErrorModal}
uncomletedData={uncomletedData}
/>
)}
<div className="header d-flex">
<div className="header-title d-flex">
<p className="title">Tasks</p>
@ -204,7 +228,9 @@ const Task = ({ socketData }: { socketData: TSocket | undefined }) => {
className={`btn-refresh-${theme && "dark"} d-flex`}
onClick={() => {
refetch();
// connect();
if (!isLive) {
connect();
}
}}
>
<img
@ -255,6 +281,8 @@ const Task = ({ socketData }: { socketData: TSocket | undefined }) => {
data={{ characters }}
isLoading={isLoading}
showTaskModal={showTaskModal}
showErrorModal={showErrorModal}
setErrorModal={setErrorModal}
/>
<Space style={{ width: "100%", marginTop: 10 }} direction="vertical">
<Space style={{ width: "100%", justifyContent: "flex-end" }} wrap>

@ -1,12 +1,5 @@
import { TUser } from "../User/TUser";
type data = {
id: number;
name?: string;
title?: string;
username?: string;
};
export type TTask = {
id: number;
note: string;

Loading…
Cancel
Save