parent
be0f954ff3
commit
0fde4aa50e
@ -0,0 +1,121 @@
|
|||||||
|
import { Input, Modal, Form, Select, message, Spin } from "antd";
|
||||||
|
|
||||||
|
import { useCompanyData } from "../../../Hooks/Companies";
|
||||||
|
import { useMemo, useState } from "react";
|
||||||
|
import { debounce } from "lodash";
|
||||||
|
import { useCreateTeamMonitor } from "../../../Hooks/Teams";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
open: boolean;
|
||||||
|
setOpen(open: boolean): void;
|
||||||
|
refetch(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CreateModal = ({ open, setOpen, refetch }: Props) => {
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
|
||||||
|
const [search, setSearch] = useState("");
|
||||||
|
|
||||||
|
const debounceSearch = useMemo(
|
||||||
|
() =>
|
||||||
|
debounce((val: string) => {
|
||||||
|
setSearch(val);
|
||||||
|
}, 500),
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleSearch = (val: string) => {
|
||||||
|
debounceSearch(val);
|
||||||
|
};
|
||||||
|
|
||||||
|
const { data: companies, isLoading } = useCompanyData({ name: search });
|
||||||
|
|
||||||
|
//@ts-ignores
|
||||||
|
const ComaniesOptions = companies?.map((company) => ({
|
||||||
|
label: company.name,
|
||||||
|
value: company.id,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const { mutate, isLoading: isSubmitting } = useCreateTeamMonitor();
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
form.resetFields();
|
||||||
|
setOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
try {
|
||||||
|
const values = await form.validateFields();
|
||||||
|
|
||||||
|
mutate(
|
||||||
|
{
|
||||||
|
name: values.name,
|
||||||
|
company_ids: values.company_ids || [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
onSuccess: () => {
|
||||||
|
message.success("Team successfully created");
|
||||||
|
handleCancel();
|
||||||
|
refetch();
|
||||||
|
},
|
||||||
|
onError: (error: any) => {
|
||||||
|
message.error("Failed to create team");
|
||||||
|
console.error(error);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} catch (validationError: any) {
|
||||||
|
if (validationError.errorFields) {
|
||||||
|
message.error("Please fill all required fields correctly");
|
||||||
|
} else {
|
||||||
|
message.error("An unexpected error occurred");
|
||||||
|
console.error(validationError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
open={open}
|
||||||
|
title="Add New Team"
|
||||||
|
okText="Create"
|
||||||
|
cancelText="Cancel"
|
||||||
|
onCancel={handleCancel}
|
||||||
|
onOk={handleSubmit}
|
||||||
|
confirmLoading={isSubmitting}
|
||||||
|
okButtonProps={{ disabled: isSubmitting }}
|
||||||
|
destroyOnClose
|
||||||
|
>
|
||||||
|
<Form form={form} layout="vertical" preserve={false}>
|
||||||
|
<Form.Item
|
||||||
|
label="Team Name"
|
||||||
|
name="name"
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: "Please enter team name",
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Input placeholder="Enter team name" />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item label="Assign Companies" name="company_ids">
|
||||||
|
<Select
|
||||||
|
mode="multiple"
|
||||||
|
showSearch
|
||||||
|
placeholder="Select companies"
|
||||||
|
onSearch={handleSearch}
|
||||||
|
filterOption={false}
|
||||||
|
notFoundContent={isLoading ? <Spin size="small" /> : null}
|
||||||
|
options={ComaniesOptions}
|
||||||
|
optionLabelProp="label"
|
||||||
|
allowClear
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CreateModal;
|
||||||
@ -0,0 +1,133 @@
|
|||||||
|
import { Modal, Form, Input, Button, Spin, message, Select } from "antd";
|
||||||
|
import { useEffect, useMemo, useState } from "react";
|
||||||
|
import {
|
||||||
|
useTeamsMonitorGetOne,
|
||||||
|
useUpdateTeamMonitor,
|
||||||
|
} from "../../../Hooks/Teams";
|
||||||
|
import { debounce } from "lodash";
|
||||||
|
import { useCompanyData } from "../../../Hooks/Companies";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
open: boolean;
|
||||||
|
id: any;
|
||||||
|
onClose(): void;
|
||||||
|
onSuccess(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const EditTeamMonitorModal = ({ open, id, onClose, onSuccess }: Props) => {
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
|
||||||
|
const { data, isLoading } = useTeamsMonitorGetOne(id);
|
||||||
|
const { mutate, isLoading: isSubmitting } = useUpdateTeamMonitor();
|
||||||
|
|
||||||
|
/* ===== company search ===== */
|
||||||
|
const [search, setSearch] = useState("");
|
||||||
|
|
||||||
|
const debounceSearch = useMemo(
|
||||||
|
() =>
|
||||||
|
debounce((val: string) => {
|
||||||
|
setSearch(val);
|
||||||
|
}, 500),
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
const { data: companies, isLoading: companiesLoading } = useCompanyData({
|
||||||
|
name: search,
|
||||||
|
});
|
||||||
|
|
||||||
|
const companyOptions = companies?.map((company) => ({
|
||||||
|
label: company.name,
|
||||||
|
value: company.id,
|
||||||
|
}));
|
||||||
|
|
||||||
|
/* ===== backend → form mapping ===== */
|
||||||
|
useEffect(() => {
|
||||||
|
if (data) {
|
||||||
|
form.setFieldsValue({
|
||||||
|
name: data.name,
|
||||||
|
company_ids: data.companies?.map((c: any) => c.id),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [data, form]);
|
||||||
|
|
||||||
|
/* ===== submit ===== */
|
||||||
|
const handleSubmit = (values: any) => {
|
||||||
|
mutate(
|
||||||
|
{
|
||||||
|
id,
|
||||||
|
payload: {
|
||||||
|
name: values.name,
|
||||||
|
company_ids: values.company_ids,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
onSuccess: () => {
|
||||||
|
message.success("Team updated successfully");
|
||||||
|
onSuccess();
|
||||||
|
},
|
||||||
|
onError: () => {
|
||||||
|
message.error("Failed to update team");
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
open={open}
|
||||||
|
title="Edit Monitoring Team"
|
||||||
|
onCancel={onClose}
|
||||||
|
destroyOnClose
|
||||||
|
confirmLoading={isSubmitting}
|
||||||
|
onOk={() => form.submit()}
|
||||||
|
footer={[
|
||||||
|
<Button key="cancel" onClick={onClose}>
|
||||||
|
Cancel
|
||||||
|
</Button>,
|
||||||
|
<Button
|
||||||
|
key="submit"
|
||||||
|
type="primary"
|
||||||
|
loading={isSubmitting}
|
||||||
|
onClick={() => form.submit()}
|
||||||
|
>
|
||||||
|
Save
|
||||||
|
</Button>,
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
{isLoading ? (
|
||||||
|
<Spin />
|
||||||
|
) : (
|
||||||
|
<Form
|
||||||
|
form={form}
|
||||||
|
key={id}
|
||||||
|
layout="vertical"
|
||||||
|
onFinish={handleSubmit}
|
||||||
|
preserve={false}
|
||||||
|
>
|
||||||
|
<Form.Item
|
||||||
|
label="Team name"
|
||||||
|
name="name"
|
||||||
|
rules={[{ required: true, message: "Please enter team name" }]}
|
||||||
|
>
|
||||||
|
<Input placeholder="Team name" />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item label="Assign Companies" name="company_ids">
|
||||||
|
<Select
|
||||||
|
mode="multiple"
|
||||||
|
showSearch
|
||||||
|
placeholder="Select companies"
|
||||||
|
onSearch={debounceSearch}
|
||||||
|
filterOption={false}
|
||||||
|
options={companyOptions}
|
||||||
|
notFoundContent={companiesLoading ? <Spin size="small" /> : null}
|
||||||
|
allowClear
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
)}
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default EditTeamMonitorModal;
|
||||||
@ -0,0 +1,175 @@
|
|||||||
|
import { useEffect, useMemo, useState } from "react";
|
||||||
|
import { useTeamData, useTeamsMonitorData } from "../../../Hooks/Teams";
|
||||||
|
|
||||||
|
//@ts-ignore
|
||||||
|
import addicon from "../../../assets/addiconpng.png";
|
||||||
|
import { Button, Input, Select, Space, theme } from "antd";
|
||||||
|
import {
|
||||||
|
LeftOutlined,
|
||||||
|
PlusOutlined,
|
||||||
|
RightOutlined,
|
||||||
|
SearchOutlined,
|
||||||
|
} from "@ant-design/icons";
|
||||||
|
import TeamMonitoringTable from "./TeamMonitoringTable";
|
||||||
|
import { debounce } from "lodash";
|
||||||
|
import CreateModal from "./CreateModal";
|
||||||
|
|
||||||
|
const TeamMonitoring = () => {
|
||||||
|
const [page, setPage] = useState(1);
|
||||||
|
const [pageSize, setPageSize] = useState<number>(() => {
|
||||||
|
const saved = localStorage.getItem("general_pageSize");
|
||||||
|
return saved ? Number(saved) : 15;
|
||||||
|
});
|
||||||
|
|
||||||
|
const [search, setSearch] = useState("");
|
||||||
|
|
||||||
|
const debouncedSearch = useMemo(
|
||||||
|
() =>
|
||||||
|
debounce((value: string) => {
|
||||||
|
setSearch(value);
|
||||||
|
}, 500),
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleSearchChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const value = e.target.value;
|
||||||
|
debouncedSearch(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const { data, isLoading, refetch } = useTeamsMonitorData({
|
||||||
|
page: page,
|
||||||
|
page_size: pageSize,
|
||||||
|
name: search,
|
||||||
|
});
|
||||||
|
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
const showModal = () => {
|
||||||
|
setOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const pageSizeOptions = [15, 20, 30, 40, 50];
|
||||||
|
|
||||||
|
const handlePageSizeChange = (value: number) => {
|
||||||
|
setPageSize(value);
|
||||||
|
setPage(1);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
localStorage.setItem("general_pageSize", String(pageSize));
|
||||||
|
}, [pageSize]);
|
||||||
|
|
||||||
|
const Next = () => {
|
||||||
|
const a = Number(page) + 1;
|
||||||
|
setPage(a);
|
||||||
|
};
|
||||||
|
const Previos = () => {
|
||||||
|
Number(page);
|
||||||
|
if (page > 1) {
|
||||||
|
const a = Number(page) - 1;
|
||||||
|
setPage(a);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const { token } = theme.useToken();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{open && <CreateModal refetch={refetch} open={open} setOpen={setOpen} />}
|
||||||
|
<div className="header d-flex" style={{ marginBottom: 16 }}>
|
||||||
|
<div>
|
||||||
|
<Input
|
||||||
|
placeholder="Search"
|
||||||
|
prefix={<SearchOutlined />}
|
||||||
|
onChange={handleSearchChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
className="btn-add d-flex"
|
||||||
|
style={{
|
||||||
|
backgroundColor: "#f99e2c",
|
||||||
|
color: "white",
|
||||||
|
padding: 18,
|
||||||
|
}}
|
||||||
|
onClick={showModal}
|
||||||
|
icon={<PlusOutlined />}
|
||||||
|
>
|
||||||
|
Add Team
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<TeamMonitoringTable
|
||||||
|
data={data?.data}
|
||||||
|
isLoading={isLoading}
|
||||||
|
refetch={refetch}
|
||||||
|
/>
|
||||||
|
<Space style={{ width: "100%", marginTop: 40 }} direction="vertical">
|
||||||
|
<Space
|
||||||
|
style={{
|
||||||
|
justifyContent: "end",
|
||||||
|
position: "fixed",
|
||||||
|
bottom: 0,
|
||||||
|
left: 0,
|
||||||
|
width: "100%",
|
||||||
|
backgroundColor: token.colorBgContainer,
|
||||||
|
boxShadow: "0 4px 8px rgba(0, 0, 0, 0.4)",
|
||||||
|
padding: "10px 0",
|
||||||
|
zIndex: 1000,
|
||||||
|
}}
|
||||||
|
wrap
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
value={pageSize}
|
||||||
|
onChange={handlePageSizeChange}
|
||||||
|
style={{ width: 65, marginRight: 16 }}
|
||||||
|
options={pageSizeOptions.map((size) => ({
|
||||||
|
label: `${size}`,
|
||||||
|
value: size,
|
||||||
|
}))}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
onClick={Previos}
|
||||||
|
disabled={data?.previous ? false : true}
|
||||||
|
style={{
|
||||||
|
backgroundColor: token.colorBgContainer,
|
||||||
|
color: token.colorText,
|
||||||
|
border: "none",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<LeftOutlined />
|
||||||
|
</Button>
|
||||||
|
<Input
|
||||||
|
disabled
|
||||||
|
style={{
|
||||||
|
width: 40,
|
||||||
|
textAlign: "center",
|
||||||
|
background: token.colorBgContainer,
|
||||||
|
border: "1px solid",
|
||||||
|
borderColor: token.colorText,
|
||||||
|
color: token.colorText,
|
||||||
|
}}
|
||||||
|
value={page}
|
||||||
|
onChange={(e) => {
|
||||||
|
let num = e.target.value;
|
||||||
|
if (Number(num) && num !== "0") {
|
||||||
|
setPage(Number(num));
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
onClick={Next}
|
||||||
|
disabled={data?.next ? false : true}
|
||||||
|
style={{
|
||||||
|
backgroundColor: token.colorBgContainer,
|
||||||
|
color: token.colorText,
|
||||||
|
border: "none",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<RightOutlined />
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
|
</Space>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TeamMonitoring;
|
||||||
@ -0,0 +1,150 @@
|
|||||||
|
import { useNavigate, useParams } from "react-router-dom";
|
||||||
|
import type { ColumnsType } from "antd/es/table";
|
||||||
|
import {
|
||||||
|
useDeleteTeamMonitor,
|
||||||
|
useTeamsMonitorGetOne,
|
||||||
|
useUpdateTeamMonitor,
|
||||||
|
} from "../../../Hooks/Teams";
|
||||||
|
import {
|
||||||
|
Form,
|
||||||
|
Spin,
|
||||||
|
Space,
|
||||||
|
Row,
|
||||||
|
Col,
|
||||||
|
Input,
|
||||||
|
Button,
|
||||||
|
Table,
|
||||||
|
Modal,
|
||||||
|
message,
|
||||||
|
Typography,
|
||||||
|
} from "antd";
|
||||||
|
import Notfound from "../../../Utils/Notfound";
|
||||||
|
import tagIcon from "../../../assets/tagIcon.svg";
|
||||||
|
import { role } from "../../../App";
|
||||||
|
import moment from "moment";
|
||||||
|
|
||||||
|
const { Title, Text } = Typography;
|
||||||
|
|
||||||
|
type Params = {
|
||||||
|
id: any;
|
||||||
|
};
|
||||||
|
|
||||||
|
const TeamMonitoringPreview = () => {
|
||||||
|
const { id } = useParams<Params>();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const { data, isLoading, refetch } = useTeamsMonitorGetOne(id);
|
||||||
|
const { mutate: deleteTeam, isLoading: deleteLoading } =
|
||||||
|
useDeleteTeamMonitor();
|
||||||
|
const { mutateAsync: updateTeam, isLoading: isSubmitting } =
|
||||||
|
useUpdateTeamMonitor();
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return <Spin size="large" style={{ marginTop: 100 }} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
return <Notfound />;
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSubmit = async (values: any) => {
|
||||||
|
try {
|
||||||
|
await updateTeam({
|
||||||
|
id,
|
||||||
|
payload: { name: values.name },
|
||||||
|
});
|
||||||
|
|
||||||
|
message.success("Team updated successfully");
|
||||||
|
refetch();
|
||||||
|
} catch {
|
||||||
|
message.error("Failed to update team");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDelete = () => {
|
||||||
|
if (deleteLoading) return;
|
||||||
|
|
||||||
|
Modal.confirm({
|
||||||
|
title: "Are you sure you want to delete this team?",
|
||||||
|
okText: "Yes",
|
||||||
|
okType: "danger",
|
||||||
|
cancelText: "No",
|
||||||
|
onOk: () =>
|
||||||
|
new Promise<void>((resolve, reject) => {
|
||||||
|
deleteTeam(id, {
|
||||||
|
onSuccess: () => {
|
||||||
|
message.success("Team deleted successfully");
|
||||||
|
navigate(-1);
|
||||||
|
resolve();
|
||||||
|
},
|
||||||
|
onError: () => {
|
||||||
|
message.error("Failed to delete team");
|
||||||
|
reject();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const columns: ColumnsType<any> = [
|
||||||
|
{
|
||||||
|
title: <img src={tagIcon} alt="" />,
|
||||||
|
key: "no",
|
||||||
|
width: 60,
|
||||||
|
align: "center",
|
||||||
|
render: (_: any, __: any, index: number) => index + 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Company name",
|
||||||
|
dataIndex: "name",
|
||||||
|
key: "name",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Space direction="vertical" size="large" style={{ width: "100%" }}>
|
||||||
|
<div>
|
||||||
|
<Title level={3} style={{ marginBottom: 0 }}>
|
||||||
|
{data.name}
|
||||||
|
</Title>
|
||||||
|
<Text type="secondary">
|
||||||
|
Created at: {moment(data.created_at).format("DD.MM.YYYY HH:mm")}
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* EDIT FORM */}
|
||||||
|
<Form
|
||||||
|
layout="vertical"
|
||||||
|
initialValues={{ name: data.name }}
|
||||||
|
onFinish={handleSubmit}
|
||||||
|
>
|
||||||
|
{/* COMPANIES TABLE */}
|
||||||
|
<Table
|
||||||
|
rowKey="id"
|
||||||
|
columns={columns}
|
||||||
|
dataSource={data.companies}
|
||||||
|
// pagination={false}
|
||||||
|
size="middle"
|
||||||
|
bordered
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* ACTION BUTTONS */}
|
||||||
|
<Form.Item style={{ marginTop: 24 }}>
|
||||||
|
{role !== "Checker" && (
|
||||||
|
<Button
|
||||||
|
danger
|
||||||
|
type="primary"
|
||||||
|
onClick={handleDelete}
|
||||||
|
loading={deleteLoading}
|
||||||
|
style={{ marginRight: 8 }}
|
||||||
|
>
|
||||||
|
Delete
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
</Space>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TeamMonitoringPreview;
|
||||||
@ -0,0 +1,108 @@
|
|||||||
|
import { Button, Table } from "antd";
|
||||||
|
import { EditOutlined } from "@ant-design/icons";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import {
|
||||||
|
QueryObserverResult,
|
||||||
|
RefetchOptions,
|
||||||
|
RefetchQueryFilters,
|
||||||
|
} from "react-query";
|
||||||
|
import tagIcon from "../../../assets/tagIcon.svg";
|
||||||
|
import { TTeam } from "../../../types/Team/TTeam";
|
||||||
|
import { timeZone } from "../../../App";
|
||||||
|
import { useState } from "react";
|
||||||
|
import EditTeamMonitorModal from "./EditTeamMonitorModal";
|
||||||
|
|
||||||
|
const TeamMonitoringTable = ({
|
||||||
|
data,
|
||||||
|
isLoading,
|
||||||
|
refetch,
|
||||||
|
}: {
|
||||||
|
data: TTeam[] | undefined;
|
||||||
|
isLoading: boolean | undefined;
|
||||||
|
refetch: <TPageData>(
|
||||||
|
options?: (RefetchOptions & RefetchQueryFilters<TPageData>) | undefined
|
||||||
|
) => Promise<QueryObserverResult<TTeam[], unknown>>;
|
||||||
|
}) => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const moment = require("moment-timezone");
|
||||||
|
const [editOpen, setEditOpen] = useState(false);
|
||||||
|
const [selectedId, setSelectedId] = useState<number | null>(null);
|
||||||
|
|
||||||
|
const openEditModal = (id: number) => {
|
||||||
|
setSelectedId(id);
|
||||||
|
setEditOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{editOpen && selectedId && (
|
||||||
|
<EditTeamMonitorModal
|
||||||
|
open={editOpen}
|
||||||
|
id={selectedId}
|
||||||
|
onClose={() => setEditOpen(false)}
|
||||||
|
onSuccess={() => {
|
||||||
|
setEditOpen(false);
|
||||||
|
refetch();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<Table
|
||||||
|
loading={isLoading}
|
||||||
|
onRow={(record) => {
|
||||||
|
return {
|
||||||
|
onClick: () => {
|
||||||
|
navigate(`/teams-monitoring/${record.id}`);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}}
|
||||||
|
dataSource={data?.map((u, i) => ({
|
||||||
|
...u,
|
||||||
|
no: i + 1,
|
||||||
|
action: { id: u.id },
|
||||||
|
created: moment(u?.created_at)
|
||||||
|
.tz(timeZone)
|
||||||
|
.format("DD.MM.YYYY HH:mm"),
|
||||||
|
key: u.id,
|
||||||
|
}))}
|
||||||
|
size="middle"
|
||||||
|
columns={[
|
||||||
|
{
|
||||||
|
title: <img src={tagIcon} alt="" />,
|
||||||
|
dataIndex: "no",
|
||||||
|
width: "5%",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Name",
|
||||||
|
dataIndex: "name",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Created at",
|
||||||
|
dataIndex: "created",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Actions",
|
||||||
|
key: "actions",
|
||||||
|
align: "center",
|
||||||
|
width: "10%",
|
||||||
|
render: (_: any, record: TTeam) => (
|
||||||
|
<Button
|
||||||
|
icon={<EditOutlined />}
|
||||||
|
type="default"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
openEditModal(record.id);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Edit
|
||||||
|
</Button>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
pagination={false}
|
||||||
|
bordered
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TeamMonitoringTable;
|
||||||
@ -1,6 +1,6 @@
|
|||||||
import { Input, Modal, Form as FormAnt, Select } from "antd";
|
import { Input, Modal, Form as FormAnt, Select } from "antd";
|
||||||
import { teamController } from "../../API/LayoutApi/teams";
|
import { useUserData } from "../../../Hooks/Users";
|
||||||
import { useUserData } from "../../Hooks/Users";
|
import { teamController } from "../../../API/LayoutApi/teams";
|
||||||
|
|
||||||
const AddTeam = ({
|
const AddTeam = ({
|
||||||
open,
|
open,
|
||||||
@ -1,16 +1,17 @@
|
|||||||
import { Input, Modal, Form as FormAnt, Select } from "antd";
|
import { Input, Modal, Form as FormAnt, Select } from "antd";
|
||||||
import { userController } from "../../API/LayoutApi/users";
|
|
||||||
import { useTeamOne } from "../../Hooks/Teams";
|
|
||||||
|
|
||||||
import { message } from "antd";
|
import { message } from "antd";
|
||||||
import { useRoleData } from "../../Hooks/Role";
|
|
||||||
import { common } from "../../Utils/common";
|
|
||||||
import {
|
import {
|
||||||
QueryObserverResult,
|
QueryObserverResult,
|
||||||
RefetchOptions,
|
RefetchOptions,
|
||||||
RefetchQueryFilters,
|
RefetchQueryFilters,
|
||||||
} from "react-query";
|
} from "react-query";
|
||||||
import { TUser } from "../../types/User/TUser";
|
import { TUser } from "../../../types/User/TUser";
|
||||||
|
import { common } from "../../../Utils/common";
|
||||||
|
import { useRoleData } from "../../../Hooks/Role";
|
||||||
|
import { useTeamOne } from "../../../Hooks/Teams";
|
||||||
|
import { userController } from "../../../API/LayoutApi/users";
|
||||||
|
|
||||||
const AddUserToTeam = ({
|
const AddUserToTeam = ({
|
||||||
open,
|
open,
|
||||||
@ -0,0 +1,169 @@
|
|||||||
|
import { useEffect, useMemo, useState } from "react";
|
||||||
|
import { useTeamData } from "../../../Hooks/Teams";
|
||||||
|
import TeamTable from "./TeamTable";
|
||||||
|
//@ts-ignore
|
||||||
|
import addicon from "../../../assets/addiconpng.png";
|
||||||
|
import AddTeam from "./AddTeam";
|
||||||
|
import { Button, Input, Select, Space, Typography, theme } from "antd";
|
||||||
|
import {
|
||||||
|
LeftOutlined,
|
||||||
|
PlusOutlined,
|
||||||
|
RightOutlined,
|
||||||
|
SearchOutlined,
|
||||||
|
} from "@ant-design/icons";
|
||||||
|
import { debounce } from "lodash";
|
||||||
|
|
||||||
|
const TeamServices = () => {
|
||||||
|
const [page, setPage] = useState(1);
|
||||||
|
const [pageSize, setPageSize] = useState<number>(() => {
|
||||||
|
const saved = localStorage.getItem("general_pageSize");
|
||||||
|
return saved ? Number(saved) : 15;
|
||||||
|
});
|
||||||
|
const [search, setSearch] = useState("");
|
||||||
|
|
||||||
|
const { data, isLoading, refetch } = useTeamData({
|
||||||
|
page: page,
|
||||||
|
page_size: pageSize,
|
||||||
|
name: search,
|
||||||
|
});
|
||||||
|
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
const showModal = () => {
|
||||||
|
setOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const pageSizeOptions = [15, 20, 30, 40, 50];
|
||||||
|
|
||||||
|
const handlePageSizeChange = (value: number) => {
|
||||||
|
setPageSize(value);
|
||||||
|
setPage(1);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
localStorage.setItem("general_pageSize", String(pageSize));
|
||||||
|
}, [pageSize]);
|
||||||
|
|
||||||
|
const Next = () => {
|
||||||
|
const a = Number(page) + 1;
|
||||||
|
setPage(a);
|
||||||
|
};
|
||||||
|
const Previos = () => {
|
||||||
|
Number(page);
|
||||||
|
if (page > 1) {
|
||||||
|
const a = Number(page) - 1;
|
||||||
|
setPage(a);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const debouncedSearch = useMemo(
|
||||||
|
() =>
|
||||||
|
debounce((value: string) => {
|
||||||
|
setSearch(value);
|
||||||
|
}, 500),
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleSearchChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const value = e.target.value;
|
||||||
|
debouncedSearch(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const { token } = theme.useToken();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{open && <AddTeam refetch={refetch} open={open} setOpen={setOpen} />}
|
||||||
|
<div className="header d-flex" style={{ marginBottom: 16 }}>
|
||||||
|
<div>
|
||||||
|
<Input
|
||||||
|
placeholder="Search"
|
||||||
|
prefix={<SearchOutlined />}
|
||||||
|
onChange={handleSearchChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
className="btn-add d-flex"
|
||||||
|
style={{
|
||||||
|
backgroundColor: "#f99e2c",
|
||||||
|
color: "white",
|
||||||
|
padding: 18,
|
||||||
|
}}
|
||||||
|
onClick={showModal}
|
||||||
|
icon={<PlusOutlined />}
|
||||||
|
>
|
||||||
|
Add Team
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<TeamTable data={data?.data} isLoading={isLoading} refetch={refetch} />
|
||||||
|
<Space style={{ width: "100%", marginTop: 40 }} direction="vertical">
|
||||||
|
<Space
|
||||||
|
style={{
|
||||||
|
justifyContent: "end",
|
||||||
|
position: "fixed",
|
||||||
|
bottom: 0,
|
||||||
|
left: 0,
|
||||||
|
width: "100%",
|
||||||
|
backgroundColor: token.colorBgContainer,
|
||||||
|
boxShadow: "0 4px 8px rgba(0, 0, 0, 0.4)",
|
||||||
|
padding: "10px 0",
|
||||||
|
zIndex: 1000,
|
||||||
|
}}
|
||||||
|
wrap
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
value={pageSize}
|
||||||
|
onChange={handlePageSizeChange}
|
||||||
|
style={{ width: 65, marginRight: 16 }}
|
||||||
|
options={pageSizeOptions.map((size) => ({
|
||||||
|
label: `${size}`,
|
||||||
|
value: size,
|
||||||
|
}))}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
onClick={Previos}
|
||||||
|
disabled={data?.previous ? false : true}
|
||||||
|
style={{
|
||||||
|
backgroundColor: token.colorBgContainer,
|
||||||
|
color: token.colorText,
|
||||||
|
border: "none",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<LeftOutlined />
|
||||||
|
</Button>
|
||||||
|
<Input
|
||||||
|
disabled
|
||||||
|
style={{
|
||||||
|
width: 40,
|
||||||
|
textAlign: "center",
|
||||||
|
background: token.colorBgContainer,
|
||||||
|
border: "1px solid",
|
||||||
|
borderColor: token.colorText,
|
||||||
|
color: token.colorText,
|
||||||
|
}}
|
||||||
|
value={page}
|
||||||
|
onChange={(e) => {
|
||||||
|
let num = e.target.value;
|
||||||
|
if (Number(num) && num !== "0") {
|
||||||
|
setPage(Number(num));
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
onClick={Next}
|
||||||
|
disabled={data?.next ? false : true}
|
||||||
|
style={{
|
||||||
|
backgroundColor: token.colorBgContainer,
|
||||||
|
color: token.colorText,
|
||||||
|
border: "none",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<RightOutlined />
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
|
</Space>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TeamServices;
|
||||||
@ -1,151 +1,32 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { Tabs, Typography } from "antd";
|
||||||
import { useTeamData } from "../../Hooks/Teams";
|
import TeamServices from "./TeamServices/TeamServices";
|
||||||
import TeamTable from "./TeamTable";
|
import TeamMonitoring from "./TeamMonitoring/TeamMonitoring";
|
||||||
//@ts-ignore
|
|
||||||
import addicon from "../../assets/addiconpng.png";
|
const Teams = () => {
|
||||||
import AddTeam from "./AddTeam";
|
const items = [
|
||||||
import { Button, Input, Select, Space, Typography, theme } from "antd";
|
{
|
||||||
import { LeftOutlined, PlusOutlined, RightOutlined } from "@ant-design/icons";
|
key: "services",
|
||||||
|
label: "Service Team",
|
||||||
const Team = () => {
|
children: <TeamServices />,
|
||||||
const [page, setPage] = useState(1);
|
},
|
||||||
const [pageSize, setPageSize] = useState<number>(() => {
|
{
|
||||||
const saved = localStorage.getItem("general_pageSize");
|
key: "monitoring",
|
||||||
return saved ? Number(saved) : 15;
|
label: "Monitoring Team",
|
||||||
});
|
children: <TeamMonitoring />,
|
||||||
|
},
|
||||||
const { data, isLoading, refetch } = useTeamData({
|
];
|
||||||
page: page,
|
|
||||||
page_size: pageSize,
|
|
||||||
});
|
|
||||||
|
|
||||||
const [open, setOpen] = useState(false);
|
|
||||||
const showModal = () => {
|
|
||||||
setOpen(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const pageSizeOptions = [15, 20, 30, 40, 50];
|
|
||||||
|
|
||||||
const handlePageSizeChange = (value: number) => {
|
|
||||||
setPageSize(value);
|
|
||||||
setPage(1);
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
localStorage.setItem("general_pageSize", String(pageSize));
|
|
||||||
}, [pageSize]);
|
|
||||||
|
|
||||||
const Next = () => {
|
|
||||||
const a = Number(page) + 1;
|
|
||||||
setPage(a);
|
|
||||||
};
|
|
||||||
const Previos = () => {
|
|
||||||
Number(page);
|
|
||||||
if (page > 1) {
|
|
||||||
const a = Number(page) - 1;
|
|
||||||
setPage(a);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const { token } = theme.useToken();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{open && <AddTeam refetch={refetch} open={open} setOpen={setOpen} />}
|
<div
|
||||||
<div className="header d-flex" style={{ marginBottom: 16 }}>
|
className="header d-flex statistics-header"
|
||||||
<Typography className="title">Teams</Typography>
|
style={{ marginBottom: 16 }}
|
||||||
{/* <button
|
|
||||||
className="btn-add d-flex"
|
|
||||||
style={{ marginRight: 0 }}
|
|
||||||
onClick={showModal}
|
|
||||||
>
|
|
||||||
<img src={addicon} style={{ marginRight: 8 }} alt="" />
|
|
||||||
Add Team
|
|
||||||
</button> */}
|
|
||||||
|
|
||||||
<Button
|
|
||||||
className="btn-add d-flex"
|
|
||||||
style={{
|
|
||||||
backgroundColor: "#f99e2c",
|
|
||||||
color: "white",
|
|
||||||
padding: 18,
|
|
||||||
}}
|
|
||||||
onClick={showModal}
|
|
||||||
icon={<PlusOutlined />} // Ant-design ikonkasi
|
|
||||||
>
|
>
|
||||||
Add Team
|
<Typography className="title">Teams</Typography>
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
<TeamTable data={data?.data} isLoading={isLoading} refetch={refetch} />
|
<Tabs items={items} />
|
||||||
<Space style={{ width: "100%", marginTop: 40 }} direction="vertical">
|
|
||||||
<Space
|
|
||||||
style={{
|
|
||||||
justifyContent: "end",
|
|
||||||
position: "fixed",
|
|
||||||
bottom: 0,
|
|
||||||
left: 0,
|
|
||||||
width: "100%",
|
|
||||||
backgroundColor: token.colorBgContainer,
|
|
||||||
boxShadow: "0 4px 8px rgba(0, 0, 0, 0.4)",
|
|
||||||
padding: "10px 0",
|
|
||||||
zIndex: 1000,
|
|
||||||
}}
|
|
||||||
wrap
|
|
||||||
>
|
|
||||||
<Select
|
|
||||||
value={pageSize}
|
|
||||||
onChange={handlePageSizeChange}
|
|
||||||
style={{ width: 65, marginRight: 16 }}
|
|
||||||
options={pageSizeOptions.map((size) => ({
|
|
||||||
label: `${size}`,
|
|
||||||
value: size,
|
|
||||||
}))}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
onClick={Previos}
|
|
||||||
disabled={data?.previous ? false : true}
|
|
||||||
style={{
|
|
||||||
backgroundColor: token.colorBgContainer,
|
|
||||||
color: token.colorText,
|
|
||||||
border: "none",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<LeftOutlined />
|
|
||||||
</Button>
|
|
||||||
<Input
|
|
||||||
disabled
|
|
||||||
style={{
|
|
||||||
width: 40,
|
|
||||||
textAlign: "center",
|
|
||||||
background: token.colorBgContainer,
|
|
||||||
border: "1px solid",
|
|
||||||
borderColor: token.colorText,
|
|
||||||
color: token.colorText,
|
|
||||||
}}
|
|
||||||
value={page}
|
|
||||||
onChange={(e) => {
|
|
||||||
let num = e.target.value;
|
|
||||||
if (Number(num) && num !== "0") {
|
|
||||||
setPage(Number(num));
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
onClick={Next}
|
|
||||||
disabled={data?.next ? false : true}
|
|
||||||
style={{
|
|
||||||
backgroundColor: token.colorBgContainer,
|
|
||||||
color: token.colorText,
|
|
||||||
border: "none",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<RightOutlined />
|
|
||||||
</Button>
|
|
||||||
</Space>
|
|
||||||
</Space>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Team;
|
export default Teams;
|
||||||
|
|||||||
Loading…
Reference in new issue