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 { teamController } from "../../API/LayoutApi/teams";
|
||||
import { useUserData } from "../../Hooks/Users";
|
||||
import { useUserData } from "../../../Hooks/Users";
|
||||
import { teamController } from "../../../API/LayoutApi/teams";
|
||||
|
||||
const AddTeam = ({
|
||||
open,
|
||||
@ -1,16 +1,17 @@
|
||||
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 { useRoleData } from "../../Hooks/Role";
|
||||
import { common } from "../../Utils/common";
|
||||
|
||||
import {
|
||||
QueryObserverResult,
|
||||
RefetchOptions,
|
||||
RefetchQueryFilters,
|
||||
} 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 = ({
|
||||
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 { 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 } from "@ant-design/icons";
|
||||
|
||||
const Team = () => {
|
||||
const [page, setPage] = useState(1);
|
||||
const [pageSize, setPageSize] = useState<number>(() => {
|
||||
const saved = localStorage.getItem("general_pageSize");
|
||||
return saved ? Number(saved) : 15;
|
||||
});
|
||||
|
||||
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();
|
||||
import { Tabs, Typography } from "antd";
|
||||
import TeamServices from "./TeamServices/TeamServices";
|
||||
import TeamMonitoring from "./TeamMonitoring/TeamMonitoring";
|
||||
|
||||
const Teams = () => {
|
||||
const items = [
|
||||
{
|
||||
key: "services",
|
||||
label: "Service Team",
|
||||
children: <TeamServices />,
|
||||
},
|
||||
{
|
||||
key: "monitoring",
|
||||
label: "Monitoring Team",
|
||||
children: <TeamMonitoring />,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div>
|
||||
{open && <AddTeam refetch={refetch} open={open} setOpen={setOpen} />}
|
||||
<div className="header d-flex" style={{ marginBottom: 16 }}>
|
||||
<Typography className="title">Teams</Typography>
|
||||
{/* <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
|
||||
<div
|
||||
className="header d-flex statistics-header"
|
||||
style={{ marginBottom: 16 }}
|
||||
>
|
||||
Add Team
|
||||
</Button>
|
||||
<Typography className="title">Teams</Typography>
|
||||
</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>
|
||||
<Tabs items={items} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Team;
|
||||
export default Teams;
|
||||
|
||||
Loading…
Reference in new issue