parent
							
								
									f7c8743ed9
								
							
						
					
					
						commit
						8188dab2b4
					
				@ -0,0 +1,146 @@
 | 
				
			||||
import dayjs from "dayjs";
 | 
				
			||||
import {
 | 
				
			||||
  CartesianGrid,
 | 
				
			||||
  Legend,
 | 
				
			||||
  Line,
 | 
				
			||||
  LineChart,
 | 
				
			||||
  ResponsiveContainer,
 | 
				
			||||
  Tooltip,
 | 
				
			||||
  XAxis,
 | 
				
			||||
  YAxis,
 | 
				
			||||
} from "recharts";
 | 
				
			||||
 | 
				
			||||
import { useGeneralStats } from "../../Hooks/Statistics";
 | 
				
			||||
 | 
				
			||||
interface StatisticGeneralProps {
 | 
				
			||||
  startDate: string;
 | 
				
			||||
  endDate: string;
 | 
				
			||||
}
 | 
				
			||||
 | 
				
			||||
const StatisticGeneral: React.FC<StatisticGeneralProps> = ({
 | 
				
			||||
  startDate,
 | 
				
			||||
  endDate,
 | 
				
			||||
}) => {
 | 
				
			||||
  const formatDate = (dateString: string): string => {
 | 
				
			||||
    const date = new Date(dateString);
 | 
				
			||||
    return new Intl.DateTimeFormat("en-EN", {
 | 
				
			||||
      day: "2-digit",
 | 
				
			||||
      month: "short",
 | 
				
			||||
    }).format(date);
 | 
				
			||||
  };
 | 
				
			||||
 | 
				
			||||
  const today = dayjs().endOf("day");
 | 
				
			||||
  let finalEndDate = dayjs(endDate);
 | 
				
			||||
 | 
				
			||||
  if (finalEndDate.isAfter(today)) {
 | 
				
			||||
    finalEndDate = today;
 | 
				
			||||
  }
 | 
				
			||||
  const formattedEndDate = finalEndDate.format("YYYY-MM-DD HH:mm:ss");
 | 
				
			||||
 | 
				
			||||
  const { data, isLoading, refetch } = useGeneralStats({
 | 
				
			||||
    start_date: startDate,
 | 
				
			||||
    end_date: formattedEndDate,
 | 
				
			||||
  });
 | 
				
			||||
 | 
				
			||||
  return (
 | 
				
			||||
    <div>
 | 
				
			||||
      <div
 | 
				
			||||
        style={{
 | 
				
			||||
          display: "flex",
 | 
				
			||||
          alignItems: "center",
 | 
				
			||||
          gap: 20,
 | 
				
			||||
          marginTop: 40,
 | 
				
			||||
        }}
 | 
				
			||||
      >
 | 
				
			||||
        <div
 | 
				
			||||
          style={{
 | 
				
			||||
            display: "flex",
 | 
				
			||||
            alignItems: "center",
 | 
				
			||||
            justifyContent: "space-around",
 | 
				
			||||
            flexDirection: "column",
 | 
				
			||||
            gap: 20,
 | 
				
			||||
            flexWrap: "wrap",
 | 
				
			||||
          }}
 | 
				
			||||
        >
 | 
				
			||||
          {data?.summary && (
 | 
				
			||||
            <div
 | 
				
			||||
              className="card_stat"
 | 
				
			||||
              style={{
 | 
				
			||||
                backgroundColor: "#F99E2C",
 | 
				
			||||
              }}
 | 
				
			||||
            >
 | 
				
			||||
              <p>Total</p>
 | 
				
			||||
              <span>{data?.summary?.total}</span>
 | 
				
			||||
              <p>Tasks</p>
 | 
				
			||||
            </div>
 | 
				
			||||
          )}
 | 
				
			||||
          {data?.summary && (
 | 
				
			||||
            <div
 | 
				
			||||
              className="card_stat"
 | 
				
			||||
              style={{
 | 
				
			||||
                backgroundColor: "#27AE60",
 | 
				
			||||
              }}
 | 
				
			||||
            >
 | 
				
			||||
              <p>Active</p>
 | 
				
			||||
              <span>{data?.summary?.total_completed}</span>
 | 
				
			||||
              <p>Tasks</p>
 | 
				
			||||
            </div>
 | 
				
			||||
          )}
 | 
				
			||||
          {data?.summary && (
 | 
				
			||||
            <div
 | 
				
			||||
              className="card_stat"
 | 
				
			||||
              style={{
 | 
				
			||||
                backgroundColor: "#F64747",
 | 
				
			||||
              }}
 | 
				
			||||
            >
 | 
				
			||||
              <p>Inactive</p>
 | 
				
			||||
              <span>{data?.summary?.total_incomplete}</span>
 | 
				
			||||
              <p>Tasks</p>
 | 
				
			||||
            </div>
 | 
				
			||||
          )}
 | 
				
			||||
        </div>
 | 
				
			||||
        <ResponsiveContainer
 | 
				
			||||
          width="100%"
 | 
				
			||||
          height={517}
 | 
				
			||||
          style={{ textTransform: "capitalize" }}
 | 
				
			||||
        >
 | 
				
			||||
          <LineChart data={data?.daily_stats}>
 | 
				
			||||
            <CartesianGrid vertical={false} stroke="#D7D8E080" />
 | 
				
			||||
            <XAxis
 | 
				
			||||
              dataKey="task_date"
 | 
				
			||||
              style={{
 | 
				
			||||
                color: "#9B9DAA",
 | 
				
			||||
                fontSize: 10,
 | 
				
			||||
                lineHeight: "12.4px",
 | 
				
			||||
                fontWeight: 400,
 | 
				
			||||
              }}
 | 
				
			||||
              tickFormatter={formatDate}
 | 
				
			||||
            />
 | 
				
			||||
            <YAxis
 | 
				
			||||
              style={{
 | 
				
			||||
                color: "#9B9DAA",
 | 
				
			||||
                fontSize: 10,
 | 
				
			||||
                fontWeight: 400,
 | 
				
			||||
              }}
 | 
				
			||||
            />
 | 
				
			||||
            <Tooltip />
 | 
				
			||||
            <Legend />
 | 
				
			||||
            <Line dataKey="total_tasks" stroke="#F99E2C" name="Total Tasks" />
 | 
				
			||||
            <Line
 | 
				
			||||
              dataKey="completed_tasks"
 | 
				
			||||
              stroke="#27AE60"
 | 
				
			||||
              name="Active tasks"
 | 
				
			||||
            />
 | 
				
			||||
            <Line
 | 
				
			||||
              dataKey="incomplete_tasks"
 | 
				
			||||
              stroke="#F64747"
 | 
				
			||||
              name="Inactive Tasks"
 | 
				
			||||
            />
 | 
				
			||||
          </LineChart>
 | 
				
			||||
        </ResponsiveContainer>
 | 
				
			||||
      </div>
 | 
				
			||||
    </div>
 | 
				
			||||
  );
 | 
				
			||||
};
 | 
				
			||||
 | 
				
			||||
export default StatisticGeneral;
 | 
				
			||||
@ -1,136 +0,0 @@
 | 
				
			||||
import { Table, Tooltip } from "antd";
 | 
				
			||||
import { TStat } from "../../types/Statistic/TStat";
 | 
				
			||||
import {
 | 
				
			||||
  QueryObserverResult,
 | 
				
			||||
  RefetchOptions,
 | 
				
			||||
  RefetchQueryFilters,
 | 
				
			||||
} from "react-query";
 | 
				
			||||
import { QuestionCircleOutlined, QuestionOutlined } from "@ant-design/icons";
 | 
				
			||||
// @ts-ignore
 | 
				
			||||
import tagIcon from "../../assets/tagIcon.svg";
 | 
				
			||||
 | 
				
			||||
import { theme } from "antd";
 | 
				
			||||
 | 
				
			||||
const StatTable = ({
 | 
				
			||||
  data,
 | 
				
			||||
  isLoading,
 | 
				
			||||
  refetch,
 | 
				
			||||
}: {
 | 
				
			||||
  refetch: <TPageData>(
 | 
				
			||||
    options?: (RefetchOptions & RefetchQueryFilters<TPageData>) | undefined
 | 
				
			||||
  ) => Promise<QueryObserverResult<TStat[], unknown>>;
 | 
				
			||||
  data: any;
 | 
				
			||||
  isLoading: boolean;
 | 
				
			||||
}) => {
 | 
				
			||||
  const { token } = theme.useToken();
 | 
				
			||||
 | 
				
			||||
  return (
 | 
				
			||||
    <div>
 | 
				
			||||
      <Table
 | 
				
			||||
        size="small"
 | 
				
			||||
        loading={isLoading}
 | 
				
			||||
        dataSource={data?.map((u: any, i: any) => ({
 | 
				
			||||
          no: i + 1,
 | 
				
			||||
          ...u,
 | 
				
			||||
        }))}
 | 
				
			||||
        columns={[
 | 
				
			||||
          {
 | 
				
			||||
            title: <img src={tagIcon} alt="" />,
 | 
				
			||||
            dataIndex: "no",
 | 
				
			||||
            key: "no",
 | 
				
			||||
            width: "5%",
 | 
				
			||||
          },
 | 
				
			||||
          {
 | 
				
			||||
            title: "Support specialist",
 | 
				
			||||
            dataIndex: "username",
 | 
				
			||||
            key: "username",
 | 
				
			||||
          },
 | 
				
			||||
          {
 | 
				
			||||
            title: "Team",
 | 
				
			||||
            dataIndex: "team_name",
 | 
				
			||||
            key: "team_name ",
 | 
				
			||||
          },
 | 
				
			||||
          {
 | 
				
			||||
            title: "Tasks",
 | 
				
			||||
            dataIndex: "number_of_tasks",
 | 
				
			||||
            key: "number_of_tasks",
 | 
				
			||||
            sorter: (a: any, b: any) => a.number_of_tasks - b.number_of_tasks,
 | 
				
			||||
            sortDirections: ["ascend", "descend"],
 | 
				
			||||
          },
 | 
				
			||||
          {
 | 
				
			||||
            title: "Points",
 | 
				
			||||
            dataIndex: "total_points",
 | 
				
			||||
            key: "total_points",
 | 
				
			||||
            sorter: (a: any, b: any) => a.total_points - b.total_points,
 | 
				
			||||
            sortDirections: ["ascend", "descend"],
 | 
				
			||||
          },
 | 
				
			||||
          // {
 | 
				
			||||
          //   title: (
 | 
				
			||||
          //     <div>
 | 
				
			||||
          //       <span>Salary</span>   
 | 
				
			||||
          //       <Tooltip title="The calculation of salary begins at the start of the month and continues to the current day. Select a month to review salary details for prior periods.">
 | 
				
			||||
          //         <QuestionCircleOutlined />
 | 
				
			||||
          //       </Tooltip>
 | 
				
			||||
          //     </div>
 | 
				
			||||
          //   ),
 | 
				
			||||
          //   dataIndex: "salary",
 | 
				
			||||
          //   key: "salary",
 | 
				
			||||
          //   render: (text: string, record: any) => (
 | 
				
			||||
          //     <Tooltip
 | 
				
			||||
          //       title={
 | 
				
			||||
          //         <div>
 | 
				
			||||
          //           {record.salary_type === "hybrid" ? (
 | 
				
			||||
          //             <p>
 | 
				
			||||
          //               <strong>Fixed Amount:</strong> $
 | 
				
			||||
          //               {record.salary_base_amount}
 | 
				
			||||
          //             </p>
 | 
				
			||||
          //           ) : (
 | 
				
			||||
          //             ""
 | 
				
			||||
          //           )}
 | 
				
			||||
          //           <p>
 | 
				
			||||
          //             <strong>Performance based amount:</strong> $
 | 
				
			||||
          //             {record.performance_based_amount}
 | 
				
			||||
          //           </p>
 | 
				
			||||
          //         </div>
 | 
				
			||||
          //       }
 | 
				
			||||
          //       overlayStyle={{
 | 
				
			||||
          //         maxWidth: "700px",
 | 
				
			||||
          //       }}
 | 
				
			||||
          //     >
 | 
				
			||||
          //       <span>${record.salary}</span>
 | 
				
			||||
          //     </Tooltip>
 | 
				
			||||
          //   ),
 | 
				
			||||
          //   sorter: (a: any, b: any) => a.salary - b.total_points,
 | 
				
			||||
          //   sortDirections: ["ascend", "descend"],
 | 
				
			||||
          // },
 | 
				
			||||
        ]}
 | 
				
			||||
        // pagination={{
 | 
				
			||||
        //   pageSize: 10,
 | 
				
			||||
        //   size: "default",
 | 
				
			||||
        //   style: {
 | 
				
			||||
        //     margin: 0,
 | 
				
			||||
        //     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,
 | 
				
			||||
        //   },
 | 
				
			||||
        //   showLessItems: true,
 | 
				
			||||
        // }}
 | 
				
			||||
        pagination={false}
 | 
				
			||||
        rowClassName={(record, index) =>
 | 
				
			||||
          index % 2 === 0 ? "odd-row" : "even-row"
 | 
				
			||||
        }
 | 
				
			||||
        bordered
 | 
				
			||||
      />
 | 
				
			||||
    </div>
 | 
				
			||||
  );
 | 
				
			||||
};
 | 
				
			||||
 | 
				
			||||
export default StatTable;
 | 
				
			||||
 | 
				
			||||
// The calculation of salary begins at the start of the month and continues to the current day. Select a month to review salary details for prior periods.
 | 
				
			||||
@ -0,0 +1,324 @@
 | 
				
			||||
import { Button, Input, Select, Space, Table, Tag } from "antd";
 | 
				
			||||
import { TStatTeam, TteamChartData } from "../../types/Statistic/TStat";
 | 
				
			||||
import {
 | 
				
			||||
  QueryObserverResult,
 | 
				
			||||
  RefetchOptions,
 | 
				
			||||
  RefetchQueryFilters,
 | 
				
			||||
} from "react-query";
 | 
				
			||||
import {
 | 
				
			||||
  CartesianGrid,
 | 
				
			||||
  Legend,
 | 
				
			||||
  Line,
 | 
				
			||||
  LineChart,
 | 
				
			||||
  ResponsiveContainer,
 | 
				
			||||
  Tooltip,
 | 
				
			||||
  XAxis,
 | 
				
			||||
  YAxis,
 | 
				
			||||
} from "recharts";
 | 
				
			||||
 | 
				
			||||
import { RightOutlined, LeftOutlined } from "@ant-design/icons";
 | 
				
			||||
 | 
				
			||||
import { theme } from "antd";
 | 
				
			||||
import tagIcon from "../../assets/tagIcon.svg";
 | 
				
			||||
import { useStatTeamData, useTeamChartData } from "../../Hooks/Statistics";
 | 
				
			||||
import { useEffect, useState } from "react";
 | 
				
			||||
import dayjs from "dayjs";
 | 
				
			||||
 | 
				
			||||
interface StatisticTeamProps {
 | 
				
			||||
  startDate: string;
 | 
				
			||||
  endDate: string;
 | 
				
			||||
}
 | 
				
			||||
 | 
				
			||||
const StatisticTeam: React.FC<StatisticTeamProps> = ({
 | 
				
			||||
  startDate,
 | 
				
			||||
  endDate,
 | 
				
			||||
}) => {
 | 
				
			||||
  const [page, setPage] = useState(1);
 | 
				
			||||
 | 
				
			||||
  const [pageSize, setPageSize] = useState<number>(() => {
 | 
				
			||||
    const saved = localStorage.getItem("general_pageSize");
 | 
				
			||||
    return saved ? Number(saved) : 15;
 | 
				
			||||
  });
 | 
				
			||||
 | 
				
			||||
  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 { data, isLoading, refetch } = useStatTeamData({
 | 
				
			||||
    page: page,
 | 
				
			||||
    page_size: pageSize,
 | 
				
			||||
    start_date: startDate,
 | 
				
			||||
    end_date: endDate,
 | 
				
			||||
  });
 | 
				
			||||
 | 
				
			||||
  const today = dayjs().endOf("day");
 | 
				
			||||
 | 
				
			||||
  let finalEndDate = dayjs(endDate);
 | 
				
			||||
 | 
				
			||||
  if (finalEndDate.isAfter(today)) {
 | 
				
			||||
    finalEndDate = today;
 | 
				
			||||
  }
 | 
				
			||||
  const formattedEndDate = finalEndDate.format("YYYY-MM-DD HH:mm:ss");
 | 
				
			||||
 | 
				
			||||
  interface TeamschartDataType {
 | 
				
			||||
    data?: TteamChartData[];
 | 
				
			||||
    refetch: <TPageData>(
 | 
				
			||||
      options?: (RefetchOptions & RefetchQueryFilters<TPageData>) | undefined
 | 
				
			||||
    ) => Promise<QueryObserverResult<TteamChartData[], unknown>>;
 | 
				
			||||
    isLoading: boolean;
 | 
				
			||||
  }
 | 
				
			||||
 | 
				
			||||
  const TeamschartData: TeamschartDataType = useTeamChartData({
 | 
				
			||||
    start_date: startDate,
 | 
				
			||||
    end_date: formattedEndDate,
 | 
				
			||||
  });
 | 
				
			||||
 | 
				
			||||
  const predefinedColors = [
 | 
				
			||||
    "#ff2600",
 | 
				
			||||
    "#FF4500",
 | 
				
			||||
    "#FF1493",
 | 
				
			||||
    "#006800",
 | 
				
			||||
    "#3CB371",
 | 
				
			||||
    "#00BFFF",
 | 
				
			||||
    "#FFD700",
 | 
				
			||||
    "#F08080",
 | 
				
			||||
    "#8A2BE2",
 | 
				
			||||
    "#FFB6C1",
 | 
				
			||||
  ];
 | 
				
			||||
 | 
				
			||||
  function updateLines(chartData: any, predefinedColors: any) {
 | 
				
			||||
    if (!chartData || chartData.length === 0) {
 | 
				
			||||
      return [];
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    const keys = Object.keys(chartData[0]).filter((key) => key !== "date");
 | 
				
			||||
 | 
				
			||||
    const newLines = keys.flatMap((key, index) => {
 | 
				
			||||
      const color = predefinedColors[index % predefinedColors.length];
 | 
				
			||||
 | 
				
			||||
      return [
 | 
				
			||||
        {
 | 
				
			||||
          key: `${key}.total_points`,
 | 
				
			||||
          color,
 | 
				
			||||
          name: `${key} points`,
 | 
				
			||||
          legend_name: `${key}`,
 | 
				
			||||
        },
 | 
				
			||||
      ];
 | 
				
			||||
    });
 | 
				
			||||
 | 
				
			||||
    return newLines;
 | 
				
			||||
  }
 | 
				
			||||
 | 
				
			||||
  const lines = TeamschartData.data
 | 
				
			||||
    ? updateLines(TeamschartData.data, predefinedColors)
 | 
				
			||||
    : [];
 | 
				
			||||
 | 
				
			||||
  const formatDate = (dateString: string): string => {
 | 
				
			||||
    const date = new Date(dateString);
 | 
				
			||||
    return new Intl.DateTimeFormat("en-EN", {
 | 
				
			||||
      day: "2-digit",
 | 
				
			||||
      month: "short",
 | 
				
			||||
    }).format(date);
 | 
				
			||||
  };
 | 
				
			||||
 | 
				
			||||
  const { token } = theme.useToken();
 | 
				
			||||
  return (
 | 
				
			||||
    <>
 | 
				
			||||
      <div>
 | 
				
			||||
        <Table
 | 
				
			||||
          loading={isLoading}
 | 
				
			||||
          size="small"
 | 
				
			||||
          dataSource={data?.data?.map((u: any, i: any) => ({
 | 
				
			||||
            no: i + 1,
 | 
				
			||||
            ...u,
 | 
				
			||||
          }))}
 | 
				
			||||
          columns={[
 | 
				
			||||
            {
 | 
				
			||||
              title: <img src={tagIcon} alt="" />,
 | 
				
			||||
              dataIndex: "no",
 | 
				
			||||
            },
 | 
				
			||||
            {
 | 
				
			||||
              title: "Team",
 | 
				
			||||
              dataIndex: "name",
 | 
				
			||||
            },
 | 
				
			||||
            {
 | 
				
			||||
              title: "Total tasks",
 | 
				
			||||
              dataIndex: "number_of_tasks",
 | 
				
			||||
            },
 | 
				
			||||
            {
 | 
				
			||||
              title: "Total points",
 | 
				
			||||
              dataIndex: "total_points",
 | 
				
			||||
            },
 | 
				
			||||
            {
 | 
				
			||||
              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: TStatTeam
 | 
				
			||||
              ) => {
 | 
				
			||||
                return record.is_active === value;
 | 
				
			||||
              },
 | 
				
			||||
            },
 | 
				
			||||
          ]}
 | 
				
			||||
          pagination={false}
 | 
				
			||||
          rowClassName={(record, index) =>
 | 
				
			||||
            index % 2 === 0 ? "odd-row" : "even-row"
 | 
				
			||||
          }
 | 
				
			||||
          bordered
 | 
				
			||||
        />
 | 
				
			||||
      </div>
 | 
				
			||||
      <div style={{ display: "flex", alignItems: "center", marginTop: 30 }}>
 | 
				
			||||
        <ResponsiveContainer width="100%" height={517}>
 | 
				
			||||
          <LineChart
 | 
				
			||||
            data={TeamschartData.data || []}
 | 
				
			||||
            margin={{ top: 20, right: 30, left: 20, bottom: 10 }}
 | 
				
			||||
          >
 | 
				
			||||
            <CartesianGrid vertical={false} stroke="#D7D8E080" />
 | 
				
			||||
            <XAxis
 | 
				
			||||
              dataKey="date"
 | 
				
			||||
              style={{
 | 
				
			||||
                color: "#9B9DAA",
 | 
				
			||||
                fontSize: 12,
 | 
				
			||||
                lineHeight: "15.4px",
 | 
				
			||||
                fontWeight: 400,
 | 
				
			||||
 | 
				
			||||
                letterSpacing: 0.8,
 | 
				
			||||
              }}
 | 
				
			||||
              tickFormatter={formatDate}
 | 
				
			||||
            />
 | 
				
			||||
            <YAxis
 | 
				
			||||
              style={{
 | 
				
			||||
                color: "#9B9DAA",
 | 
				
			||||
                fontSize: 10,
 | 
				
			||||
                fontWeight: 400,
 | 
				
			||||
              }}
 | 
				
			||||
            />
 | 
				
			||||
            <Tooltip />
 | 
				
			||||
            <Legend
 | 
				
			||||
              payload={lines.map((line) => ({
 | 
				
			||||
                value: line.legend_name,
 | 
				
			||||
                type: "line",
 | 
				
			||||
                id: line.key,
 | 
				
			||||
                color: line.color,
 | 
				
			||||
              }))}
 | 
				
			||||
            />
 | 
				
			||||
 | 
				
			||||
            {lines.map((line, index) => (
 | 
				
			||||
              <Line
 | 
				
			||||
                key={index}
 | 
				
			||||
                type="linear"
 | 
				
			||||
                dataKey={line.key}
 | 
				
			||||
                name={line.name}
 | 
				
			||||
                stroke={line.color}
 | 
				
			||||
                activeDot={{ r: 7 }}
 | 
				
			||||
              />
 | 
				
			||||
            ))}
 | 
				
			||||
          </LineChart>
 | 
				
			||||
        </ResponsiveContainer>
 | 
				
			||||
      </div>
 | 
				
			||||
      <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?.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?.data?.next ? false : true}
 | 
				
			||||
            style={{
 | 
				
			||||
              backgroundColor: token.colorBgContainer,
 | 
				
			||||
              color: token.colorText,
 | 
				
			||||
              border: "none",
 | 
				
			||||
            }}
 | 
				
			||||
          >
 | 
				
			||||
            <RightOutlined />
 | 
				
			||||
          </Button>
 | 
				
			||||
        </Space>
 | 
				
			||||
      </Space>
 | 
				
			||||
    </>
 | 
				
			||||
  );
 | 
				
			||||
};
 | 
				
			||||
 | 
				
			||||
export default StatisticTeam;
 | 
				
			||||
@ -1,98 +0,0 @@
 | 
				
			||||
import { Table, Tag } from "antd";
 | 
				
			||||
import { TStatTeam } from "../../types/Statistic/TStat";
 | 
				
			||||
import {
 | 
				
			||||
  QueryObserverResult,
 | 
				
			||||
  RefetchOptions,
 | 
				
			||||
  RefetchQueryFilters,
 | 
				
			||||
} from "react-query";
 | 
				
			||||
 | 
				
			||||
import { theme } from "antd";
 | 
				
			||||
// @ts-ignore
 | 
				
			||||
import tagIcon from "../../assets/tagIcon.svg";
 | 
				
			||||
const StatTeamTable = ({
 | 
				
			||||
  data,
 | 
				
			||||
  isLoading,
 | 
				
			||||
  refetch,
 | 
				
			||||
}: {
 | 
				
			||||
  refetch: <TPageData>(
 | 
				
			||||
    options?: (RefetchOptions & RefetchQueryFilters<TPageData>) | undefined
 | 
				
			||||
  ) => Promise<QueryObserverResult<TStatTeam[], unknown>>;
 | 
				
			||||
  data: any;
 | 
				
			||||
  isLoading: boolean;
 | 
				
			||||
}) => {
 | 
				
			||||
  const { token } = theme.useToken();
 | 
				
			||||
  return (
 | 
				
			||||
    <div style={{ maxHeight: "400px", overflow: "auto" }}>
 | 
				
			||||
      <Table
 | 
				
			||||
        loading={isLoading}
 | 
				
			||||
        size="small"
 | 
				
			||||
        dataSource={data?.map((u: any, i: any) => ({
 | 
				
			||||
          no: i + 1,
 | 
				
			||||
          ...u,
 | 
				
			||||
        }))}
 | 
				
			||||
        columns={[
 | 
				
			||||
          {
 | 
				
			||||
            title: <img src={tagIcon} alt="" />,
 | 
				
			||||
            dataIndex: "no",
 | 
				
			||||
          },
 | 
				
			||||
          {
 | 
				
			||||
            title: "Team",
 | 
				
			||||
            dataIndex: "name",
 | 
				
			||||
          },
 | 
				
			||||
          {
 | 
				
			||||
            title: "Total tasks",
 | 
				
			||||
            dataIndex: "number_of_tasks",
 | 
				
			||||
          },
 | 
				
			||||
          {
 | 
				
			||||
            title: "Total points",
 | 
				
			||||
            dataIndex: "total_points",
 | 
				
			||||
          },
 | 
				
			||||
          {
 | 
				
			||||
            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: TStatTeam) => {
 | 
				
			||||
              return record.is_active === value;
 | 
				
			||||
            },
 | 
				
			||||
          },
 | 
				
			||||
        ]}
 | 
				
			||||
        // pagination={{
 | 
				
			||||
        //   pageSize: 10,
 | 
				
			||||
        //   size: "default",
 | 
				
			||||
        //   style: {
 | 
				
			||||
        //     margin: 0,
 | 
				
			||||
        //     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,
 | 
				
			||||
        //   },
 | 
				
			||||
        // }}
 | 
				
			||||
        pagination={false}
 | 
				
			||||
        rowClassName={(record, index) =>
 | 
				
			||||
          index % 2 === 0 ? "odd-row" : "even-row"
 | 
				
			||||
        }
 | 
				
			||||
      />
 | 
				
			||||
    </div>
 | 
				
			||||
  );
 | 
				
			||||
};
 | 
				
			||||
 | 
				
			||||
export default StatTeamTable;
 | 
				
			||||
@ -0,0 +1,249 @@
 | 
				
			||||
import { Button, Input, Select, Space, Table, Tooltip } from "antd";
 | 
				
			||||
import { LeftOutlined, RightOutlined, SearchOutlined } from "@ant-design/icons";
 | 
				
			||||
import tagIcon from "../../assets/tagIcon.svg";
 | 
				
			||||
 | 
				
			||||
import { theme } from "antd";
 | 
				
			||||
import { useEffect, useMemo, useState } from "react";
 | 
				
			||||
import { useTeamData } from "../../Hooks/Teams";
 | 
				
			||||
import { useStatsData } from "../../Hooks/Statistics";
 | 
				
			||||
import { statController } from "../../API/LayoutApi/statistic";
 | 
				
			||||
import { debounce } from "lodash";
 | 
				
			||||
 | 
				
			||||
interface StatisticsChekersProps {
 | 
				
			||||
  startDate: string;
 | 
				
			||||
  endDate: string;
 | 
				
			||||
}
 | 
				
			||||
 | 
				
			||||
const StatisticsChekers: React.FC<StatisticsChekersProps> = ({
 | 
				
			||||
  startDate,
 | 
				
			||||
  endDate,
 | 
				
			||||
}) => {
 | 
				
			||||
  const [team, setTeam] = useState<any>("");
 | 
				
			||||
  const [search, setSearch] = useState<string>("");
 | 
				
			||||
  const { token } = theme.useToken();
 | 
				
			||||
  const teamData = useTeamData({});
 | 
				
			||||
  const teamOptions: { label: string; value: any }[] | undefined =
 | 
				
			||||
    teamData?.data?.map((item: any) => ({
 | 
				
			||||
      label: item?.name,
 | 
				
			||||
      value: item?.name,
 | 
				
			||||
    }));
 | 
				
			||||
 | 
				
			||||
  const [page, setPage] = useState(1);
 | 
				
			||||
 | 
				
			||||
  const [pageSize, setPageSize] = useState<number>(() => {
 | 
				
			||||
    const saved = localStorage.getItem("general_pageSize");
 | 
				
			||||
    return saved ? Number(saved) : 15;
 | 
				
			||||
  });
 | 
				
			||||
 | 
				
			||||
  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 handleSearchChange = (e: React.ChangeEvent<HTMLInputElement>) => {
 | 
				
			||||
    debouncedSearch(e.target.value);
 | 
				
			||||
  };
 | 
				
			||||
 | 
				
			||||
  const debouncedSearch = useMemo(
 | 
				
			||||
    () =>
 | 
				
			||||
      debounce((value: string) => {
 | 
				
			||||
        setSearch(value);
 | 
				
			||||
      }, 1000),
 | 
				
			||||
    []
 | 
				
			||||
  );
 | 
				
			||||
 | 
				
			||||
  useEffect(() => {
 | 
				
			||||
    return () => {
 | 
				
			||||
      debouncedSearch.cancel();
 | 
				
			||||
    };
 | 
				
			||||
  }, [debouncedSearch]);
 | 
				
			||||
 | 
				
			||||
  const handleSave = (a: string) => {
 | 
				
			||||
    const trimmedStartDate = startDate.slice(0, 10);
 | 
				
			||||
    const trimmedEndDate = endDate.slice(0, 10);
 | 
				
			||||
    const fileName = `${trimmedStartDate}-${trimmedEndDate}`;
 | 
				
			||||
    if (a === "team") {
 | 
				
			||||
      const teamName = `${team}_${fileName}`;
 | 
				
			||||
      statController.saveUsersStats(teamName, startDate, endDate, team);
 | 
				
			||||
    } else {
 | 
				
			||||
      statController.saveTeamStats(fileName, startDate, endDate);
 | 
				
			||||
    }
 | 
				
			||||
  };
 | 
				
			||||
 | 
				
			||||
  const { data, refetch, isLoading } = useStatsData({
 | 
				
			||||
    search: search,
 | 
				
			||||
    team: team,
 | 
				
			||||
    start_date: startDate,
 | 
				
			||||
    end_date: endDate,
 | 
				
			||||
    page: page,
 | 
				
			||||
    page_size: pageSize,
 | 
				
			||||
  });
 | 
				
			||||
 | 
				
			||||
  return (
 | 
				
			||||
    <>
 | 
				
			||||
      <div
 | 
				
			||||
        style={{
 | 
				
			||||
          display: "flex",
 | 
				
			||||
          alignItems: "center",
 | 
				
			||||
          marginBottom: 10,
 | 
				
			||||
        }}
 | 
				
			||||
      >
 | 
				
			||||
        <div style={{ marginRight: 12 }}>
 | 
				
			||||
          <Input
 | 
				
			||||
            placeholder="Search"
 | 
				
			||||
            prefix={<SearchOutlined />}
 | 
				
			||||
            onChange={handleSearchChange}
 | 
				
			||||
          />
 | 
				
			||||
        </div>
 | 
				
			||||
        <Select
 | 
				
			||||
          style={{ width: 260 }}
 | 
				
			||||
          placeholder="Team"
 | 
				
			||||
          onChange={(value: any) => setTeam(value)}
 | 
				
			||||
          options={teamOptions}
 | 
				
			||||
          allowClear
 | 
				
			||||
        />
 | 
				
			||||
      </div>
 | 
				
			||||
 | 
				
			||||
      <Table
 | 
				
			||||
        size="small"
 | 
				
			||||
        loading={isLoading}
 | 
				
			||||
        dataSource={data?.data?.map((u: any, i: any) => ({
 | 
				
			||||
          no: i + 1,
 | 
				
			||||
          ...u,
 | 
				
			||||
        }))}
 | 
				
			||||
        columns={[
 | 
				
			||||
          {
 | 
				
			||||
            title: <img src={tagIcon} alt="" />,
 | 
				
			||||
            dataIndex: "no",
 | 
				
			||||
            key: "no",
 | 
				
			||||
            width: "5%",
 | 
				
			||||
          },
 | 
				
			||||
          {
 | 
				
			||||
            title: "Support specialist",
 | 
				
			||||
            dataIndex: "username",
 | 
				
			||||
            key: "username",
 | 
				
			||||
          },
 | 
				
			||||
          {
 | 
				
			||||
            title: "Team",
 | 
				
			||||
            dataIndex: "team_name",
 | 
				
			||||
            key: "team_name ",
 | 
				
			||||
          },
 | 
				
			||||
          {
 | 
				
			||||
            title: "Tasks",
 | 
				
			||||
            dataIndex: "number_of_tasks",
 | 
				
			||||
            key: "number_of_tasks",
 | 
				
			||||
            sorter: (a: any, b: any) => a.number_of_tasks - b.number_of_tasks,
 | 
				
			||||
            sortDirections: ["ascend", "descend"],
 | 
				
			||||
          },
 | 
				
			||||
          {
 | 
				
			||||
            title: "Points",
 | 
				
			||||
            dataIndex: "total_points",
 | 
				
			||||
            key: "total_points",
 | 
				
			||||
            sorter: (a: any, b: any) => a.total_points - b.total_points,
 | 
				
			||||
            sortDirections: ["ascend", "descend"],
 | 
				
			||||
          },
 | 
				
			||||
        ]}
 | 
				
			||||
        pagination={false}
 | 
				
			||||
        rowClassName={(record, index) =>
 | 
				
			||||
          index % 2 === 0 ? "odd-row" : "even-row"
 | 
				
			||||
        }
 | 
				
			||||
        bordered
 | 
				
			||||
      />
 | 
				
			||||
      <Button
 | 
				
			||||
        type="primary"
 | 
				
			||||
        onClick={(e) => handleSave("team")}
 | 
				
			||||
        style={{ marginTop: 10, marginBottom: 40 }}
 | 
				
			||||
      >
 | 
				
			||||
        Save as file
 | 
				
			||||
      </Button>
 | 
				
			||||
 | 
				
			||||
      <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?.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?.data?.next ? false : true}
 | 
				
			||||
            style={{
 | 
				
			||||
              backgroundColor: token.colorBgContainer,
 | 
				
			||||
              color: token.colorText,
 | 
				
			||||
              border: "none",
 | 
				
			||||
            }}
 | 
				
			||||
          >
 | 
				
			||||
            <RightOutlined />
 | 
				
			||||
          </Button>
 | 
				
			||||
        </Space>
 | 
				
			||||
      </Space>
 | 
				
			||||
    </>
 | 
				
			||||
  );
 | 
				
			||||
};
 | 
				
			||||
 | 
				
			||||
export default StatisticsChekers;
 | 
				
			||||
@ -1,81 +0,0 @@
 | 
				
			||||
import React from "react";
 | 
				
			||||
import { Table, Tag, Tooltip } from "antd";
 | 
				
			||||
import { theme } from "antd";
 | 
				
			||||
import {
 | 
				
			||||
  QueryObserverResult,
 | 
				
			||||
  RefetchOptions,
 | 
				
			||||
  RefetchQueryFilters,
 | 
				
			||||
} from "react-query";
 | 
				
			||||
import { TStatCreators } from "../../types/Statistic/TStat";
 | 
				
			||||
import tagIcon from "../../assets/tagIcon.svg";
 | 
				
			||||
 | 
				
			||||
const StatisticsSupportTable = ({
 | 
				
			||||
  data,
 | 
				
			||||
  isLoading,
 | 
				
			||||
  refetch,
 | 
				
			||||
}: {
 | 
				
			||||
  refetch: <TPageData>(
 | 
				
			||||
    options?: (RefetchOptions & RefetchQueryFilters<TPageData>) | undefined
 | 
				
			||||
  ) => Promise<QueryObserverResult<TStatCreators[], unknown>>;
 | 
				
			||||
  data: any;
 | 
				
			||||
  isLoading: boolean;
 | 
				
			||||
}) => {
 | 
				
			||||
  const { token } = theme.useToken();
 | 
				
			||||
  return (
 | 
				
			||||
    <div style={{ paddingBottom: 40 }}>
 | 
				
			||||
      <Table
 | 
				
			||||
        loading={isLoading}
 | 
				
			||||
        size="small"
 | 
				
			||||
        dataSource={data?.data?.map((u: any, i: any) => ({
 | 
				
			||||
          no: i + 1,
 | 
				
			||||
          ...u,
 | 
				
			||||
        }))}
 | 
				
			||||
        columns={[
 | 
				
			||||
          {
 | 
				
			||||
            title: <img src={tagIcon} alt="" />,
 | 
				
			||||
            dataIndex: "no",
 | 
				
			||||
            key: "no",
 | 
				
			||||
            width: "5%",
 | 
				
			||||
          },
 | 
				
			||||
          {
 | 
				
			||||
            title: "Support specialist",
 | 
				
			||||
            dataIndex: "username",
 | 
				
			||||
            render: (text, record) => {
 | 
				
			||||
              return record.full_name.trim()
 | 
				
			||||
                ? record.full_name
 | 
				
			||||
                : record.username;
 | 
				
			||||
            },
 | 
				
			||||
          },
 | 
				
			||||
          {
 | 
				
			||||
            title: "Total tasks",
 | 
				
			||||
            dataIndex: "number_of_tasks",
 | 
				
			||||
          },
 | 
				
			||||
        ]}
 | 
				
			||||
        // pagination={{
 | 
				
			||||
        //   pageSize: 10,
 | 
				
			||||
        //   size: "default",
 | 
				
			||||
        //   style: {
 | 
				
			||||
        //     margin: 0,
 | 
				
			||||
        //     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,
 | 
				
			||||
        //   },
 | 
				
			||||
        //   showLessItems: true,
 | 
				
			||||
        // }}
 | 
				
			||||
        pagination={false}
 | 
				
			||||
        rowClassName={(record, index) =>
 | 
				
			||||
          index % 2 === 0 ? "odd-row" : "even-row"
 | 
				
			||||
        }
 | 
				
			||||
        bordered
 | 
				
			||||
      />
 | 
				
			||||
    </div>
 | 
				
			||||
  );
 | 
				
			||||
};
 | 
				
			||||
 | 
				
			||||
export default StatisticsSupportTable;
 | 
				
			||||
@ -0,0 +1,206 @@
 | 
				
			||||
import React, { useEffect, useMemo, useRef, useState } from "react";
 | 
				
			||||
import { Button, Input, Select, Space, Table, Tag, Tooltip } from "antd";
 | 
				
			||||
import { theme } from "antd";
 | 
				
			||||
import tagIcon from "../../assets/tagIcon.svg";
 | 
				
			||||
import { useCreatorsData } from "../../Hooks/Statistics";
 | 
				
			||||
 | 
				
			||||
import { debounce } from "lodash";
 | 
				
			||||
 | 
				
			||||
import { SearchOutlined, RightOutlined, LeftOutlined } from "@ant-design/icons";
 | 
				
			||||
 | 
				
			||||
interface StatisticsTechSupportProps {
 | 
				
			||||
  startDate: string;
 | 
				
			||||
  endDate: string;
 | 
				
			||||
}
 | 
				
			||||
 | 
				
			||||
const StatisticsTechSupport: React.FC<StatisticsTechSupportProps> = ({
 | 
				
			||||
  startDate,
 | 
				
			||||
  endDate,
 | 
				
			||||
}) => {
 | 
				
			||||
  const { token } = theme.useToken();
 | 
				
			||||
 | 
				
			||||
  const [SupportSearch, setSupportSearch] = useState<string>("");
 | 
				
			||||
 | 
				
			||||
  const [page, setPage] = useState(1);
 | 
				
			||||
 | 
				
			||||
  const [pageSize, setPageSize] = useState<number>(() => {
 | 
				
			||||
    const saved = localStorage.getItem("general_pageSize");
 | 
				
			||||
    return saved ? Number(saved) : 15;
 | 
				
			||||
  });
 | 
				
			||||
 | 
				
			||||
  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 handleTechSupportSearchChange = (
 | 
				
			||||
    e: React.ChangeEvent<HTMLInputElement>
 | 
				
			||||
  ) => {
 | 
				
			||||
    debouncedSearch(e.target.value);
 | 
				
			||||
  };
 | 
				
			||||
 | 
				
			||||
  const debouncedSearch = useMemo(
 | 
				
			||||
    () =>
 | 
				
			||||
      debounce((value: string) => {
 | 
				
			||||
        setSupportSearch(value);
 | 
				
			||||
      }, 1000),
 | 
				
			||||
    []
 | 
				
			||||
  );
 | 
				
			||||
 | 
				
			||||
  useEffect(() => {
 | 
				
			||||
    return () => {
 | 
				
			||||
      debouncedSearch.cancel();
 | 
				
			||||
    };
 | 
				
			||||
  }, [debouncedSearch]);
 | 
				
			||||
 | 
				
			||||
  const { data, isLoading } = useCreatorsData({
 | 
				
			||||
    page: page,
 | 
				
			||||
    page_size: pageSize,
 | 
				
			||||
    search: SupportSearch,
 | 
				
			||||
    start_date: startDate,
 | 
				
			||||
    end_date: endDate,
 | 
				
			||||
  });
 | 
				
			||||
 | 
				
			||||
  return (
 | 
				
			||||
    <>
 | 
				
			||||
      <div
 | 
				
			||||
        style={{
 | 
				
			||||
          display: "flex",
 | 
				
			||||
          alignItems: "center",
 | 
				
			||||
          marginBottom: 10,
 | 
				
			||||
        }}
 | 
				
			||||
      >
 | 
				
			||||
        <div>
 | 
				
			||||
          <Input
 | 
				
			||||
            placeholder="Search"
 | 
				
			||||
            prefix={<SearchOutlined />}
 | 
				
			||||
            onChange={handleTechSupportSearchChange}
 | 
				
			||||
          />
 | 
				
			||||
        </div>
 | 
				
			||||
      </div>
 | 
				
			||||
 | 
				
			||||
      <Table
 | 
				
			||||
        loading={isLoading}
 | 
				
			||||
        size="small"
 | 
				
			||||
        dataSource={data?.data?.map((u: any, i: any) => ({
 | 
				
			||||
          no: i + 1,
 | 
				
			||||
          ...u,
 | 
				
			||||
        }))}
 | 
				
			||||
        columns={[
 | 
				
			||||
          {
 | 
				
			||||
            title: <img src={tagIcon} alt="" />,
 | 
				
			||||
            dataIndex: "no",
 | 
				
			||||
            key: "no",
 | 
				
			||||
            width: "5%",
 | 
				
			||||
          },
 | 
				
			||||
          {
 | 
				
			||||
            title: "Support specialist",
 | 
				
			||||
            dataIndex: "username",
 | 
				
			||||
            render: (text, record) => {
 | 
				
			||||
              return record.full_name.trim()
 | 
				
			||||
                ? record.full_name
 | 
				
			||||
                : record.username;
 | 
				
			||||
            },
 | 
				
			||||
          },
 | 
				
			||||
          {
 | 
				
			||||
            title: "Total tasks",
 | 
				
			||||
            dataIndex: "number_of_tasks",
 | 
				
			||||
          },
 | 
				
			||||
        ]}
 | 
				
			||||
        pagination={false}
 | 
				
			||||
        rowClassName={(record, index) =>
 | 
				
			||||
          index % 2 === 0 ? "odd-row" : "even-row"
 | 
				
			||||
        }
 | 
				
			||||
        bordered
 | 
				
			||||
      />
 | 
				
			||||
 | 
				
			||||
      <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?.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?.data?.next ? false : true}
 | 
				
			||||
            style={{
 | 
				
			||||
              backgroundColor: token.colorBgContainer,
 | 
				
			||||
              color: token.colorText,
 | 
				
			||||
              border: "none",
 | 
				
			||||
            }}
 | 
				
			||||
          >
 | 
				
			||||
            <RightOutlined />
 | 
				
			||||
          </Button>
 | 
				
			||||
        </Space>
 | 
				
			||||
      </Space>
 | 
				
			||||
    </>
 | 
				
			||||
  );
 | 
				
			||||
};
 | 
				
			||||
 | 
				
			||||
export default StatisticsTechSupport;
 | 
				
			||||
					Loading…
					
					
				
		Reference in new issue