accounting page tabs

dilmurod
Dilmurod 4 weeks ago
parent 8b53f23835
commit 77863b959d

@ -0,0 +1,28 @@
import {
TAccounting,
TAccountingHistory,
} from "../../types/Accounting/TAccounting";
import instance from "../api";
export type TAccountingGetParams = {
month: string;
};
export const AccountingController = {
async read(filterObject: TAccountingGetParams) {
const params = { ...filterObject };
if (!!filterObject.month) params.month = filterObject.month;
const { data } = await instance.get<TAccounting[]>(`/employees-salaries/`, {
params,
});
return data;
},
async history() {
const { data } = await instance.get<TAccountingHistory[]>(
`/employees-salaries-history/`
);
return data;
},
};

@ -1,4 +1,11 @@
import { TCard, TStat, TStatTeam } from "../../types/Statistic/TStat";
import {
TCard,
TGeneralChartData,
TStat,
TStatCreators,
TStatTeam,
TteamChartData,
} from "../../types/Statistic/TStat";
import instance from "../api";
export type TStatGetParams = {
@ -18,6 +25,15 @@ export type TStatTeamGetParams = {
export type TStatCreatorsGetParams = {
start_date?: string;
end_date?: string;
search?: string;
};
export type TteamChartGetParams = {
start_date?: string;
end_date?: string;
};
export type TGeneralChartGetParams = {
start_date?: string;
end_date?: string;
};
export const statController = {
@ -53,10 +69,14 @@ export const statController = {
if (!!filterObject.start_date) params.start_date = filterObject.start_date;
if (!!filterObject.end_date) params.end_date = filterObject.end_date;
if (!!filterObject.search) params.search = filterObject.search;
const { data } = await instance.get<TStatTeam[]>(`stats/task-creators/`, {
const { data } = await instance.get<TStatCreators[]>(
`stats/task-creators/`,
{
params,
});
}
);
return data;
},
async cards(filterObject: TStatCreatorsGetParams) {
@ -70,6 +90,34 @@ export const statController = {
});
return data;
},
async teamChart(filterObject: TteamChartGetParams) {
const params = { ...filterObject };
if (!!filterObject.start_date) params.start_date = filterObject.start_date;
if (!!filterObject.end_date) params.end_date = filterObject.end_date;
const { data } = await instance.get<TteamChartData[]>(
`stats/teams-line-chart/`,
{
params,
}
);
return data;
},
async generalChart(filterObject: TGeneralChartGetParams) {
const params = { ...filterObject };
if (!!filterObject.start_date) params.start_date = filterObject.start_date;
if (!!filterObject.end_date) params.end_date = filterObject.end_date;
const { data } = await instance.get<TGeneralChartData[]>(
`stats/general-stats/`,
{
params,
}
);
return data;
},
async saveUsersStats(
fileName: string,

@ -6,6 +6,8 @@ export type TUsersGetParams = {
name?: string;
team?: string;
role?: string;
page?: number;
page_size?: number;
};
export type TUsersPutParams = {
@ -26,6 +28,10 @@ export const userController = {
async read(filterObject: TUsersGetParams) {
const params = { ...filterObject };
if (!!filterObject.page && filterObject.page !== 0)
params.page = filterObject.page;
params.page_size = filterObject.page_size;
if (!!filterObject.name) params.name = filterObject.name;
if (Array.isArray(filterObject.team)) {
params.team = filterObject.team.join(", ");

@ -894,6 +894,11 @@
}
}
.requests-filter {
justify-content: space-between;
align-items: center;
}
@media (max-width: 768px) {
.btn-add {
padding: 7px 10px 7px 10px;

@ -41,6 +41,9 @@ import driverIcon from "./assets/customersIcon.png";
import requestIcon from "./assets/requestIcon.png";
// @ts-ignore
import callIcon from "./assets/callIcon.png";
import accountingIcon from "./assets/acoountingIcon.svg";
import Register from "./Auth/Register";
import Activate from "./Auth/Activate";
import Invite from "./Auth/Invite";
@ -53,6 +56,7 @@ import Requests from "./Components/Requests/Requests";
import { callController } from "./API/LayoutApi/callrequests";
import Call from "./Components/CallRequests/Call";
import { dark, light } from "./Utils/styles";
import Accounting from "./Components/Accounting/Accounting";
// import Input from "antd/es/input/Input";
const { Header, Sider, Content } = Layout;
const userJSON: any = localStorage.getItem("user");
@ -190,6 +194,11 @@ const App: React.FC = () => {
</Link>,
"calls/",
<img alt="" src={callIcon} />
),
getItem(
<Link to="accounting/">Accounting</Link>,
"accounting/",
<img alt="" src={accountingIcon} />
)
);
}

@ -0,0 +1,62 @@
import { Tabs, Typography } from "antd";
import React, { useState } from "react";
import TabPane from "antd/es/tabs/TabPane";
import AccountingCurrent from "./AccountingCurrent";
import AccountingLast from "./AccountingLast";
import AccountingHistory from "./AccountingHistory";
const Accounting: React.FC = () => {
const [activeTab, setActiveTab] = useState("1");
return (
<div>
<div
className="header d-flex statistics-header"
style={{ marginBottom: 16 }}
>
<Typography className="title">Accounting</Typography>
</div>
<Tabs
defaultActiveKey="1"
activeKey={activeTab}
onChange={(key) => setActiveTab(key)}
>
<TabPane
tab={
<span style={{ display: "flex", alignItems: "center" }}>
Current Month
</span>
}
key="1"
>
<AccountingCurrent />
</TabPane>
<TabPane
tab={
<span style={{ display: "flex", alignItems: "center" }}>
Last Month
</span>
}
key="2"
>
<AccountingLast />
</TabPane>
<TabPane
tab={
<span style={{ display: "flex", alignItems: "center" }}>
History
</span>
}
key="3"
>
<AccountingHistory />
</TabPane>
</Tabs>
</div>
);
};
export default Accounting;

@ -0,0 +1,178 @@
import { Table, Tooltip } from "antd";
import React from "react";
import tagIcon from "../../assets/tagIcon.png";
import { QuestionCircleOutlined } from "@ant-design/icons";
import { theme } from "antd";
import { useAccountingData } from "../../Hooks/Accounting";
const AccountingCurrent: React.FC = () => {
const { data, refetch, isLoading } = useAccountingData({
month: "current",
});
const { token } = theme.useToken();
return (
<div>
<Table
size="small"
loading={isLoading}
dataSource={data?.map((u, i) => ({
no: i + 1,
...u,
}))}
columns={[
{
title: <img src={tagIcon} alt="" />,
dataIndex: "no",
key: "no",
width: "5%",
},
{
title: "Username",
dataIndex: "username",
key: "username",
},
{
title: "Tasks",
dataIndex: "number_of_tasks",
key: "number_of_tasks",
},
{
title: "Points",
dataIndex: "total_points",
key: "total_points",
},
{
title: "Salary Type",
dataIndex: "salary_type",
render: (value: any, record: any) => {
if (record.salary_type === "task_based") {
return <p>Task Based</p>;
} else if (record.salary_type === "hybrid") {
return <p>Hybrid</p>;
} else {
return <p>{record.salary_type}</p>; // Agar boshqa qiymat bo'lsa, oddiy qilib chiqariladi
}
},
filters: [
{
text: "Hybrid",
value: "hybrid",
},
{
text: "Task Based",
value: "task_based",
},
],
filterMultiple: false,
// defaultFilteredValue: ["hybrid"],
onFilter: (value: any, record: any) => {
return record.salary_type === value;
},
},
{
title: "Base Salary",
dataIndex: "salary_base_amount",
render: (text: string, record: any) => (
<p>${record?.salary_base_amount}</p>
),
},
{
title: "Performance Salary",
dataIndex: "performance_salary",
render: (text: string, record: any) => (
<p>${record?.performance_salary}</p>
),
},
{
title: "Charges",
dataIndex: "total_charges",
render: (text: string, record: any) => (
<p>${record?.total_charges}</p>
),
},
{
title: "Bonuses",
dataIndex: "total_bonuses",
render: (text: string, record: any) => (
<p>${record?.total_bonuses}</p>
),
},
{
title: (
<div>
<span>Salary</span> &nbsp;
<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"],
},
]}
rowClassName={(record, index) =>
index % 2 === 0 ? "odd-row" : "even-row"
}
bordered
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,
}}
// onRow={(record) => ({
// onClick: () => {
// if (record.user && record.user.id) {
// navigate(`/accounting/${record.user.id}`); // `user.id`ni olish
// } else {
// console.error("User ID mavjud emas");
// }
// },
// })}
/>
</div>
);
};
export default AccountingCurrent;

@ -0,0 +1,188 @@
import React, { useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import axios from "axios";
import Typography from "antd/es/typography/Typography";
import { DatePicker, DatePickerProps, Table } from "antd";
import tagIcon from "../../assets/tagIcon.png";
import dayjs from "dayjs";
interface User {
id: number;
first_name: string;
last_name: string;
username: string;
}
interface Salary {
id: number;
month: string;
year: number;
number_of_tasks: number;
total_points: number;
salary_type: string;
base_salary: number;
performance_salary: number;
total_salary: number;
}
interface Data {
user: User;
salaries: Salary[];
total: number;
}
const AccountingDetails = () => {
const { id } = useParams();
const [user, setUser] = useState<Data | null>(null);
const [loading, setLoading] = useState(true);
const now = dayjs();
const moment = require("moment");
const currentDate = moment();
// const defaultDate = `${currentDate.format("YYYY")}`;
const disabledDate = (current: any) => {
return current && current.year() > moment().year();
};
const [date, setDate] = useState<string | null>(null);
const onChangeDate: DatePickerProps["onChange"] = (date) => {
if (!date) {
setDate("");
} else {
const year = date.format("YYYY");
setDate(year);
}
};
useEffect(() => {
const fetchUserDetails = async () => {
const API_URL = `https://api.tteld.co/api/v1/user-salaries/${id}`;
const AUTH_TOKEN = localStorage.getItem("access");
try {
const response = await axios.get(API_URL, {
params: {
user_id: id,
year: date,
},
headers: {
Authorization: `Bearer ${AUTH_TOKEN}`,
"Content-Type": "application/json",
},
maxRedirects: 0,
});
setUser(response.data);
setLoading(false);
} catch (error: any) {
console.error("Error:", error.response?.status, error.response?.data);
setLoading(false);
}
};
fetchUserDetails();
}, [date]);
return (
<>
<div
className="header d-flex statistics-header"
style={{ marginBottom: 16 }}
>
<div
style={{
display: "flex",
justifyContent: "center",
flexDirection: "column",
}}
>
{user?.user.first_name && user.user.last_name ? (
<Typography className="title">
{user?.user.first_name} {user?.user.last_name}
</Typography>
) : (
<Typography className="title">{user?.user.username}</Typography>
)}
<Typography style={{ fontSize: 18, fontWeight: 700, marginTop: 24 }}>
Total: ${user?.total}
</Typography>
</div>
<div>
<DatePicker
onChange={onChangeDate}
picker="year"
format={"YYYY"}
disabledDate={disabledDate}
// defaultValue={now}
style={{ marginRight: 10, width: 120, marginBottom: 10 }}
/>
</div>
</div>
<Table
size="small"
loading={loading}
dataSource={user?.salaries?.map((u, i) => ({
no: i + 1,
...u,
}))}
columns={[
{
title: <img src={tagIcon} alt="" />,
dataIndex: "no",
key: "no",
width: "5%",
},
{
title: "Salary",
dataIndex: "total_salary",
render(value, record, index) {
return <p>${record?.total_salary}</p>;
},
},
{
title: "Salary Type",
dataIndex: "salary_type",
render: (value: any, record: any) => {
if (record.salary_type === "task_based") {
return <p>Task Based</p>;
} else if (record.salary_type === "hybrid") {
return <p>Hybrid</p>;
} else {
return <p>{record.salary_type}</p>; // Agar boshqa qiymat bo'lsa, oddiy qilib chiqariladi
}
},
},
{
title: "Base Salary",
dataIndex: "base_salary",
render(value, record, index) {
return <p>${record?.base_salary}</p>;
},
},
{
title: "Performance Salary",
dataIndex: "performance_salary",
render(value, record, index) {
return <p>${record?.performance_salary}</p>;
},
},
{
title: "Month",
dataIndex: "month",
},
{
title: "Year",
dataIndex: "year",
},
]}
bordered
pagination={false}
/>
</>
);
};
export default AccountingDetails;

@ -0,0 +1,158 @@
import { Table, Tooltip } from "antd";
import React from "react";
import tagIcon from "../../assets/tagIcon.png";
import { QuestionCircleOutlined } from "@ant-design/icons";
import { theme } from "antd";
import { useAccountingHistory } from "../../Hooks/Accounting";
const AccountingHistory: React.FC = () => {
const { data, refetch, isLoading } = useAccountingHistory();
const { token } = theme.useToken();
return (
<div>
<Table
size="small"
loading={isLoading}
dataSource={data?.map((u, i) => ({
no: i + 1,
...u,
}))}
columns={[
{
title: <img src={tagIcon} alt="" />,
dataIndex: "no",
key: "no",
width: "5%",
},
{
title: "Username",
dataIndex: "username",
key: "username",
},
{
title: "Total Tasks",
dataIndex: "total_number_of_tasks",
key: "total_number_of_tasks",
},
{
title: "Total Points",
dataIndex: "total_earned_points",
key: "total_earned_points",
},
{
title: "Total worked months",
dataIndex: "salary_months_count",
key: "salary_months_count",
},
{
title: "Salary Type",
dataIndex: "salary_type",
render: (value: any, record: any) => {
if (record.salary_type === "task_based") {
return <p>Task Based</p>;
} else if (record.salary_type === "hybrid") {
return <p>Hybrid</p>;
} else {
return <p>{record.salary_type}</p>;
}
},
filters: [
{
text: "Hybrid",
value: "hybrid",
},
{
text: "Task Based",
value: "task_based",
},
],
filterMultiple: false,
onFilter: (value: any, record: any) => {
return record.salary_type === value;
},
},
{
title: "Base Salary",
dataIndex: "total_base_salary",
render: (text: string, record: any) => (
<p>${record?.total_base_salary}</p>
),
},
{
title: "Performance Salary",
dataIndex: "total_performance_salary",
render: (text: string, record: any) => (
<p>${record?.total_performance_salary}</p>
),
},
{
title: "Total Charges",
dataIndex: "total_charges",
render: (text: string, record: any) => (
<p>${record?.total_charges}</p>
),
},
{
title: "Total Bonuses",
dataIndex: "total_bonuses",
render: (text: string, record: any) => (
<p>${record?.total_bonuses}</p>
),
},
{
title: (
<div>
<span>Total Salary</span>&nbsp;
<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: "total_earned_salary",
key: "total_earned_salary",
render: (text: string, record: any) => (
<span>${record.total_earned_salary}</span>
),
// sorter: (a: any, b: any) => a.salary - b.total_points,
// sortDirections: ["ascend", "descend"],
},
]}
rowClassName={(record, index) =>
index % 2 === 0 ? "odd-row" : "even-row"
}
bordered
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,
}}
// onRow={(record) => ({
// onClick: () => {
// if (record.user && record.user.id) {
// navigate(`/accounting/${record.user.id}`); // `user.id`ni olish
// } else {
// console.error("User ID mavjud emas");
// }
// },
// })}
/>
</div>
);
};
export default AccountingHistory;

@ -0,0 +1,161 @@
import { Button, Table, Tooltip } from "antd";
import React, { useEffect, useState } from "react";
import api from "../../API/api";
import tagIcon from "../../assets/tagIcon.png";
import { QuestionCircleOutlined } from "@ant-design/icons";
import { theme } from "antd";
import { useAccountingData } from "../../Hooks/Accounting";
const AccountingCurrent: React.FC = () => {
const { data, refetch, isLoading } = useAccountingData({
month: "last",
});
const { token } = theme.useToken();
return (
<div>
<Table
size="small"
loading={isLoading}
dataSource={data?.map((u, i) => ({
no: i + 1,
...u,
}))}
columns={[
{
title: <img src={tagIcon} alt="" />,
dataIndex: "no",
key: "no",
width: "5%",
},
{
title: "Username",
dataIndex: "username",
key: "username",
},
{
title: "Tasks",
dataIndex: "number_of_tasks",
key: "number_of_tasks",
},
{
title: "Points",
dataIndex: "total_points",
key: "total_points",
},
{
title: "Salary Type",
dataIndex: "salary_type",
render: (value: any, record: any) => {
if (record.salary_type === "task_based") {
return <p>Task Based</p>;
} else if (record.salary_type === "hybrid") {
return <p>Hybrid</p>;
} else {
return <p>{record.salary_type}</p>; // Agar boshqa qiymat bo'lsa, oddiy qilib chiqariladi
}
},
filters: [
{
text: "Hybrid",
value: "hybrid",
},
{
text: "Task Based",
value: "task_based",
},
],
filterMultiple: false,
// defaultFilteredValue: ["hybrid"],
onFilter: (value: any, record: any) => {
return record.salary_type === value;
},
},
{
title: "Base Salary",
dataIndex: "salary_base_amount",
render: (text: string, record: any) => (
<p>${record?.salary_base_amount}</p>
),
},
{
title: "Performance Salary",
dataIndex: "performance_salary",
render: (text: string, record: any) => (
<p>${record?.performance_salary}</p>
),
},
{
title: "Charges",
dataIndex: "total_charges",
render: (text: string, record: any) => (
<p>${record?.total_charges}</p>
),
},
{
title: "Bonuses",
dataIndex: "total_bonuses",
render: (text: string, record: any) => (
<p>${record?.total_bonuses}</p>
),
},
{
title: (
<div>
<span>Salary</span> &nbsp;
<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) => (
<span>${record.salary}</span>
),
// sorter: (a: any, b: any) => a.salary - b.total_points,
// sortDirections: ["ascend", "descend"],
},
]}
rowClassName={(record, index) =>
index % 2 === 0 ? "odd-row" : "even-row"
}
bordered
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,
}}
// onRow={(record) => ({
// onClick: () => {
// if (record.user && record.user.id) {
// navigate(`/accounting/${record.user.id}`); // `user.id`ni olish
// } else {
// console.error("User ID mavjud emas");
// }
// },
// })}
/>
{/* <Button disabled={data?.some((item) => item.is_confirmed)} type="primary">
Confirm
</Button> */}
</div>
);
};
export default AccountingCurrent;

@ -8,7 +8,7 @@ import {
RightOutlined,
} from "@ant-design/icons";
import { useCompanyPaginated } from "../../Hooks/Companies";
import { Button, Input, Pagination, Space, Typography } from "antd";
import { Button, Input, Space, Typography } from "antd";
import { theme } from "antd";
// @ts-ignore
@ -30,7 +30,7 @@ const Company = () => {
name: search,
is_active: undefined,
page: page,
page_size: 15,
page_size: 10,
});
const timerRef = useRef<NodeJS.Timeout | null>(null);
@ -59,12 +59,6 @@ const Company = () => {
const { token } = theme.useToken();
const page_size = 15;
const handlePageChange = (page: number) => {
setPage(page);
};
const themes = localStorage.getItem("theme") === "true" ? true : false;
return (
@ -152,12 +146,6 @@ const Company = () => {
>
<RightOutlined />
</Button>
{/* <Pagination
current={page}
total={data?.page_size}
pageSize={page_size}
onChange={handlePageChange}
/> */}
</Space>
</Space>
</div>

@ -99,7 +99,7 @@ const RequestsEdit = ({
onCancel={handleCancel}
footer={null}
open={modalOpen}
width={1000}
width={800}
maskClosable={true}
>
<div className="info-div">

@ -6,8 +6,19 @@ import {
} from "react-query";
import { statController } from "../../API/LayoutApi/statistic";
import { useTeamData } from "../../Hooks/Teams/index";
import { useStatTeamData, useStatsData } from "../../Hooks/Stats";
import { TStatTeam } from "../../types/Statistic/TStat";
import {
useCreatorsData,
useGeneralChartData,
useStatTeamData,
useStatsData,
useTeamChartData,
} from "../../Hooks/Stats";
import {
TGeneralChartData,
TStatCreators,
TStatTeam,
TteamChartData,
} from "../../types/Statistic/TStat";
import StatTable from "./StatisticTable";
import StatTeamTable from "./StatisticTeamTable";
import dayjs from "dayjs";
@ -41,8 +52,11 @@ import checkersIcon from "../../assets/checkerIcon.svg";
import chekersIconActive from "../../assets/checkersIconActive.svg";
import teamsIcon from "../../assets/teamsIcon.svg";
import teamsIconActive from "../../assets/teamsIconActive.svg";
import techSupports from "../../assets/techsupportIcon.svg";
import techSupportsActive from "../../assets/techsupportIconActive.svg";
import axios from "axios";
import StatisticsSupportTable from "./StatisticsSupportTable";
const Stat = () => {
const now = dayjs();
@ -57,6 +71,7 @@ const Stat = () => {
.format("YYYY-MM-DD")} 23:59:59`;
const [search, setSearch] = useState<string>("");
const [SupportSearch, setSupportSearch] = useState<string>("");
const [team, setTeam] = useState<any>("");
const [startDate, setStartDate] = useState(start_date);
const [endDate, setEndDate] = useState(end_date);
@ -141,6 +156,42 @@ const Stat = () => {
end_date: endDate,
});
interface TaskCreatorsType {
data?: TStatCreators[];
refetch: <TPageData>(
options?: (RefetchOptions & RefetchQueryFilters<TPageData>) | undefined
) => Promise<QueryObserverResult<TStatCreators[], unknown>>;
isLoading: boolean;
}
const CreatorsData: TaskCreatorsType = useCreatorsData({
search: SupportSearch,
start_date: startDate,
end_date: endDate,
});
interface TeamschartDataType {
data?: TteamChartData[];
refetch: <TPageData>(
options?: (RefetchOptions & RefetchQueryFilters<TPageData>) | undefined
) => Promise<QueryObserverResult<TteamChartData[], unknown>>;
isLoading: boolean;
}
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 TeamschartData: TeamschartDataType = useTeamChartData({
start_date: startDate,
end_date: formattedEndDate,
});
const timerRef = useRef<NodeJS.Timeout | null>(null);
const handleSearchChange = (e: React.ChangeEvent<HTMLInputElement>) => {
if (timerRef.current) {
@ -152,6 +203,20 @@ const Stat = () => {
setSearch(searchText);
}, 1000);
};
const handleTechSupportSearchChange = (
e: React.ChangeEvent<HTMLInputElement>
) => {
if (timerRef.current) {
clearTimeout(timerRef.current);
}
const searchText = e.target.value;
timerRef.current = setTimeout(() => {
setSupportSearch(searchText);
}, 1000);
};
const theme = localStorage.getItem("theme") === "true" ? true : false;
const [activeTab, setActiveTab] = useState("1");
@ -160,81 +225,47 @@ const Stat = () => {
return current && current >= moment().add(1, "month").startOf("month");
};
// const chartData = [
// {
// date: "2024-12-01",
// ABM: { points: 4500, count: 20 },
// ZNX: { points: 2500, count: 18 },
// TYP: { points: 3900, count: 24 },
// LMO: { points: 3100, count: 16 },
// QWE: { points: 2800, count: 21 },
// RST: { points: 4400, count: 19 },
// UVX: { points: 3950, count: 22 },
// WYZ: { points: 4100, count: 20 },
// ERT: { points: 3700, count: 17 },
// KLM: { points: 4200, count: 25 },
// LBS: { points: 1200, count: 25 },
// },
// {
// date: "2024-12-02",
// ABM: { points: 4600, count: 21 },
// ZNX: { points: 2400, count: 17 },
// TYP: { points: 3850, count: 23 },
// LMO: { points: 3200, count: 15 },
// QWE: { points: 2750, count: 20 },
// RST: { points: 4450, count: 18 },
// UVX: { points: 4000, count: 23 },
// WYZ: { points: 4200, count: 19 },
// ERT: { points: 3750, count: 16 },
// KLM: { points: 4250, count: 24 },
// LBS: { points: 1250, count: 26 },
// },
// {
// date: "2024-12-03",
// ABM: { points: 4550, count: 19 },
// ZNX: { points: 2550, count: 19 },
// TYP: { points: 3950, count: 25 },
// LMO: { points: 3150, count: 17 },
// QWE: { points: 2850, count: 22 },
// RST: { points: 4500, count: 20 },
// UVX: { points: 3900, count: 21 },
// WYZ: { points: 4300, count: 21 },
// ERT: { points: 3800, count: 18 },
// KLM: { points: 4300, count: 26 },
// LBS: { points: 1300, count: 27 },
// },
// ];
// const lines = [
// { key: "ABM.points", color: "#FF5733" }, // orange-red
// { key: "ZNX.points", color: "#00b822" }, // lime-green
// { key: "TYP.points", color: "#5733FF" }, // blue-purple
// { key: "LMO.points", color: "#FFC300" }, // gold
// { key: "QWE.points", color: "#009790" }, // turquoise
// { key: "RST.points", color: "#FF33A1" }, // hot pink
// { key: "UVX.points", color: "#8D33FF" }, // violet
// { key: "WYZ.points", color: "#FF8D33" }, // orange
// { key: "ERT.points", color: "#33A1FF" }, // sky blue
// { key: "KLM.points", color: "#4a8a01" }, // light green
// ];
// console.log(chartData, );
// const predefinedColors = ["#FF5733", "#00b822"];
// function updateLines(chartData: any, predefinedColors: any) {
// const keys = Object.keys(chartData[0]).filter((key) => key !== "date");
// const newLines = keys.map((key, index) => {
// const color = predefinedColors[index % predefinedColors.length]; // Ranglarni aylantirib foydalanadi
// return { key: `${key}.points`, color, name: `${key}` };
// });
// return newLines;
// }
// lines massivini yangilash
// const lines = updateLines(chartData, predefinedColors);
const predefinedColors = [
"#ff2600", // Tomato
"#FF4500", // OrangeRed
"#FF1493", // DeepPink
"#006800", // LimeGreen
"#3CB371", // MediumSeaGreen
"#00BFFF", // DeepSkyBlue
"#FFD700", // Gold
"#F08080", // LightCoral
"#8A2BE2", // BlueViolet
"#FFB6C1", // LightPink
];
function updateLines(chartData: any, predefinedColors: any) {
if (!chartData || chartData.length === 0) {
return []; // Agar chartData mavjud bo'lmasa, bo'sh massiv qaytariladi
}
const keys = Object.keys(chartData[0]).filter((key) => key !== "date");
// Har bir key uchun chiziqlar yaratish
const newLines = keys.flatMap((key, index) => {
const color = predefinedColors[index % predefinedColors.length]; // Ranglarni aylantirib foydalanadi
// Har bir "key" uchun ikkita chiziq (biri number_of_tasks, ikkinchisi total_points)
return [
{
key: `${key}.total_points`,
color,
name: `${key} points`,
legend_name: `${key}`,
},
];
});
return newLines;
}
const lines = TeamschartData.data
? updateLines(TeamschartData.data, predefinedColors)
: [];
const [chartData, setChartData] = useState([]);
const [summaryData, setSummaryData] = useState<Record<string, any> | null>(
@ -279,8 +310,6 @@ const Stat = () => {
fetchData();
}, [token, startDate, endDate]);
if (loading) return <p>Loading...</p>;
const formatDate = (dateString: string): string => {
const date = new Date(dateString);
return new Intl.DateTimeFormat("en-EN", {
@ -388,15 +417,7 @@ const Stat = () => {
height={517}
style={{ textTransform: "capitalize" }}
>
<LineChart
data={chartData}
// margin={{
// top: 5,
// right: 30,
// left: 20,
// bottom: 5,
// }}
>
<LineChart data={chartData}>
<CartesianGrid vertical={false} stroke="#D7D8E080" />
<XAxis
dataKey="task_date"
@ -436,18 +457,56 @@ const Stat = () => {
</ResponsiveContainer>
</div>
</TabPane>
<TabPane
tab={
<span style={{ display: "flex", alignItems: "center" }}>
<img
style={{ marginRight: 5 }}
src={activeTab === "2" ? chekersIconActive : checkersIcon}
src={activeTab === "2" ? techSupportsActive : techSupports}
alt="icon"
/>
Checkers
Tech Supports
</span>
}
key="2"
>
<span
style={{
display: "flex",
alignItems: "center",
marginBottom: 10,
}}
>
<div className="search-div" style={{ marginRight: 12 }}>
<img src={IconSearch} alt="" />
<input
className={`search-input-${theme}`}
type="text"
placeholder="Search"
onChange={handleTechSupportSearchChange}
/>
</div>
</span>
<StatisticsSupportTable
data={CreatorsData?.data}
isLoading={CreatorsData?.isLoading}
refetch={CreatorsData?.refetch}
/>
</TabPane>
<TabPane
tab={
<span style={{ display: "flex", alignItems: "center" }}>
<img
style={{ marginRight: 5 }}
src={activeTab === "3" ? chekersIconActive : checkersIcon}
alt="icon"
/>
Checkers
</span>
}
key="3"
>
<span
style={{
@ -490,13 +549,13 @@ const Stat = () => {
<span style={{ display: "flex", alignItems: "center" }}>
<img
style={{ marginRight: 5 }}
src={activeTab === "3" ? teamsIconActive : teamsIcon}
src={activeTab === "4" ? teamsIconActive : teamsIcon}
alt="icon"
/>
Teams
</span>
}
key="3"
key="4"
>
<StatTeamTable
data={TeamData?.data}
@ -511,18 +570,41 @@ const Stat = () => {
Save as file
</Button>
{/* <ResponsiveContainer width="100%" height={400}>
<div style={{ display: "flex", alignItems: "center", marginTop: 30 }}>
<ResponsiveContainer width="100%" height={517}>
<LineChart
width={800}
height={400}
data={chartData}
data={TeamschartData.data || []}
margin={{ top: 20, right: 30, left: 20, bottom: 10 }}
>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="date" />
<YAxis />
<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 />
<Legend
payload={lines.map((line) => ({
value: line.legend_name, // Legenddagi yozuv sifatida `name` beriladi
type: "line",
id: line.key,
color: line.color,
}))}
/>
{lines.map((line, index) => (
<Line
@ -535,7 +617,8 @@ const Stat = () => {
/>
))}
</LineChart>
</ResponsiveContainer> */}
</ResponsiveContainer>
</div>
</TabPane>
</Tabs>
</div>

@ -54,49 +54,55 @@ const StatTable = ({
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> &nbsp;&nbsp;
<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>
),
},
// {
// title: (
// <div>
// <span>Salary</span> &nbsp;&nbsp;
// <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,

@ -0,0 +1,79 @@
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.png";
const StatisticsSupportTable = ({
data,
isLoading,
refetch,
}: {
refetch: <TPageData>(
options?: (RefetchOptions & RefetchQueryFilters<TPageData>) | undefined
) => Promise<QueryObserverResult<TStatCreators[], unknown>>;
data: TStatCreators[] | undefined;
isLoading: boolean;
}) => {
const { token } = theme.useToken();
return (
<div style={{ maxHeight: "400px" }}>
<Table
loading={isLoading}
size="small"
dataSource={data?.map((u, i) => ({
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,
},
}}
rowClassName={(record, index) =>
index % 2 === 0 ? "odd-row" : "even-row"
}
bordered
/>
</div>
);
};
export default StatisticsSupportTable;

@ -241,6 +241,7 @@ const TaskModal = ({
</Dropdown>
</div>
<div className="mdoal-actions">
{role !== "Checker" && (
<Dropdown
disabled={recordTask?.status !== "New"}
menu={{ items: teams }}
@ -260,6 +261,7 @@ const TaskModal = ({
Forward
</button>
</Dropdown>
)}
<button
style={{
marginLeft: 12,

@ -42,8 +42,6 @@ const Task = ({
const [errorModal, setErrorModal] = useState(false);
const [uncomletedData, setUncomletedData] = useState<TTask[]>();
const [pageSize, setPageSize] = useState(10);
useEffect(() => {
if (
socketData &&
@ -129,7 +127,6 @@ const Task = ({
label: item?.name,
value: item?.id,
}));
const page_size = 10;
const { data, isLoading, refetch } = useTasks({
search,
status,
@ -173,11 +170,6 @@ const Task = ({
}
};
const handlePageChange = (page: number, pageSize: number) => {
setPage(page);
setPageSize(pageSize);
};
const timerRef = useRef<NodeJS.Timeout | null>(null);
const handleSearchChange = (e: React.ChangeEvent<HTMLInputElement>) => {
if (timerRef.current) {
@ -357,12 +349,6 @@ const Task = ({
>
<RightOutlined />
</Button>
{/* <Pagination
current={page}
total={data?.data.length}
pageSize={pageSize}
onChange={handlePageChange}
/> */}
</Space>
</Space>
</div>

@ -31,6 +31,8 @@ import attachmentIcon from "../../assets/attachmentIcon.png";
// @ts-ignore
import attachmentIconActive from "../../assets/attachmentIconActive.png";
import uploadImg from "../../assets/upload-file.png";
const { Option } = Select;
const TabPane = Tabs.TabPane;
type params = {
@ -395,15 +397,19 @@ const UpdateEdit = () => {
}}
>
<p className="ant-upload-drag-icon">
<UploadOutlined
style={{ color: "#b5f5ec" }}
/>
<img src={uploadImg} alt="upload image" />
</p>
<p
className="ant-upload-text"
style={{ color: "#9B9DAA", fontSize: 14 }}
>
Drag and drop files or Click to select
</p>
<p
className="ant-upload-text"
style={{ color: "#b5f5ec" }}
style={{ color: "#9B9DAA", fontSize: 13 }}
>
Click or drag file to this area to upload
Maximum file size is 10 MB
</p>
</Upload.Dragger>
<p>{imgname.join(",\n")}</p>

@ -107,7 +107,7 @@ const UserTable = ({
}
scroll={{ x: "768px" }}
pagination={{
pageSize: 15,
pageSize: 10,
size: "default",
style: {
margin: 0,
@ -123,6 +123,7 @@ const UserTable = ({
},
showLessItems: true,
}}
// pagination={false}
bordered
/>
</div>

@ -2,23 +2,39 @@ import { useRef, useState } from "react";
import { useUserData } from "../../Hooks/Users";
import AddUser from "./AddUser";
import UserTable from "./UserTable";
import { LeftOutlined, RightOutlined } from "@ant-design/icons";
import { theme } from "antd";
// @ts-ignore
import IconSearch from "../../assets/searchIcon.png";
//@ts-ignore
import addicon from "../../assets/addiconpng.png";
import { Pagination, Space, Typography } from "antd";
import { Button, Input, Pagination, Space, Typography } from "antd";
const User = () => {
const [page, setPage] = useState(1);
const [open, setOpen] = useState(false);
const [search, setSearch] = useState("");
const showModal = () => {
setOpen(true);
};
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, refetch, isLoading } = useUserData({
name: search,
team: "",
});
const timerRef = useRef<NodeJS.Timeout | null>(null);
const handleSearchChange = (e: React.ChangeEvent<HTMLInputElement>) => {
if (timerRef.current) {
@ -30,7 +46,9 @@ const User = () => {
setSearch(searchText);
}, 1000);
};
const theme = localStorage.getItem("theme") === "true" ? true : false;
const themes = localStorage.getItem("theme") === "true" ? true : false;
const { token } = theme.useToken();
return (
<div>
@ -50,7 +68,7 @@ const User = () => {
<div className="search-div">
<img src={IconSearch} alt="" />
<input
className={`search-input-${theme}`}
className={`search-input-${themes}`}
type="text"
placeholder="Search"
onChange={handleSearchChange}
@ -67,19 +85,35 @@ const User = () => {
bottom: 0,
left: 0,
width: "100%",
// backgroundColor: token.colorBgContainer,
backgroundColor: token.colorBgContainer,
boxShadow: "0 4px 8px rgba(0, 0, 0, 0.4)",
padding: "10px 0",
zIndex: 1000,
}}
wrap
>
<Button onClick={Previos} disabled={data?.previous ? false : true}>
<Button
onClick={Previos}
disabled={data?.previous ? false : true}
style={{
backgroundColor: token.colorBgContainer,
color: token.colorText,
border: "none",
}}
>
<LeftOutlined />
</Button>
<Input
style={{ width: 30, textAlign: "center" }}
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;
@ -89,15 +123,17 @@ const User = () => {
}}
/>
<Button onClick={Next} disabled={data?.next ? false : true}>
<Button
onClick={Next}
// disabled={data?.next ? false : true}
style={{
backgroundColor: token.colorBgContainer,
color: token.colorText,
border: "none",
}}
>
<RightOutlined />
</Button>
<Pagination
// current={page}
// total={}
// pageSize={page_size}
// onChange={handlePageChange}
/>
</Space>
</Space> */}
</div>

@ -0,0 +1,26 @@
import { useQuery } from "react-query";
import {
AccountingController,
TAccountingGetParams,
} from "../../API/LayoutApi/accounting";
export const useAccountingData = ({ month }: TAccountingGetParams) => {
return useQuery(
[`stats/all-users/`, month],
() =>
AccountingController.read({
month,
}),
{ refetchOnWindowFocus: false }
);
};
export const useAccountingHistory = () => {
return useQuery(
[`employees-salaries-history/`],
() => AccountingController.history(),
{
refetchOnWindowFocus: false,
}
);
};

@ -1,3 +1,8 @@
import {
TGeneralChartGetParams,
TStatCreatorsGetParams,
TteamChartGetParams,
} from "./../../API/LayoutApi/statistic";
import { useQuery } from "react-query";
import { TStatGetParams, statController } from "../../API/LayoutApi/statistic";
@ -27,10 +32,34 @@ export const useStatTeamData = ({
);
};
export const useCreatorsData = ({ start_date, end_date }: TStatGetParams) => {
export const useCreatorsData = ({
start_date,
end_date,
search,
}: TStatCreatorsGetParams) => {
return useQuery(
[`stats/task-creators/`, search, start_date, end_date],
() => statController.creators({ start_date, end_date, search }),
{ refetchOnWindowFocus: false }
);
};
export const useTeamChartData = ({
start_date,
end_date,
}: TteamChartGetParams) => {
return useQuery(
[`stats/teams-line-chart/`, start_date, end_date],
() => statController.teamChart({ start_date, end_date }),
{ refetchOnWindowFocus: false }
);
};
export const useGeneralChartData = ({
start_date,
end_date,
}: TGeneralChartGetParams) => {
return useQuery(
[`stats/task-creators/`, start_date, end_date],
() => statController.creators({ start_date, end_date }),
[`stats/general-stats/`, start_date, end_date],
() => statController.generalChart({ start_date, end_date }),
{ refetchOnWindowFocus: false }
);
};

@ -1,10 +1,16 @@
import { useQuery } from "react-query";
import { TUsersGetParams, userController } from "../../API/LayoutApi/users";
export const useUserData = ({ name, team, role }: TUsersGetParams) => {
export const useUserData = ({
name,
team,
role,
page,
page_size,
}: TUsersGetParams) => {
return useQuery(
[`users/admins/`, { name, team, role }],
() => userController.read({ name, team, role }),
[`users/admins/`, { name, team, role, page, page_size }],
() => userController.read({ name, team, role, page, page_size }),
{ refetchOnWindowFocus: false }
);
};

@ -12,6 +12,8 @@ import Stat from "../Components/Statistics/Statistic";
import Profile from "../Components/Profile/Profile";
import Update from "../Components/Updates/Update";
import UpdateEdit from "../Components/Updates/UpdateEdit";
import Accounting from "../Components/Accounting/Accounting";
import AccountingDetails from "../Components/Accounting/AccountingDetails";
type TItems = {
path: string;
@ -50,6 +52,16 @@ export const mainItems: TItems[] = [
component: <ServiceEdit />,
key: "/service/:id/",
},
{
path: "/accounting/",
component: <Accounting />,
key: "/accounting/",
},
{
path: "/accounting/:id/",
component: <AccountingDetails />,
key: "/accounting/:id/",
},
];
export const superItems: TItems[] = [

@ -104,6 +104,16 @@ export const dark = {
colorText: "#fff",
colorTextDisabled: "#fff",
},
Drawer: {
colorBgElevated: "#262626", // Drawer foni
colorText: "#ffffff", // Matn rangi
colorPrimary: "#f99e2c", // Asosiy tugma rangi
colorPrimaryHover: "#ffb347", // Tugmalar hover rangi
paddingLG: 24, // Ichki padding
borderRadiusLG: 10, // Burchaklarni yumshatish
colorIcon: "#ffffff", // Close tugmasining rangi (✖)
colorIconHover: "#ffb347",
},
Empty: {
colorText: "rgba(249, 158, 44, 1)",
colorTextDisabled: "rgba(249, 158, 44, 1)",

@ -0,0 +1,3 @@
<svg width="18" height="18" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.50012 10.0003C1.50012 5.30571 5.30583 1.5 10.0004 1.5C14.695 1.5 18.5007 5.30571 18.5007 10.0003C18.5007 14.6949 14.695 18.5006 10.0004 18.5006C5.30583 18.5006 1.50012 14.6949 1.50012 10.0003ZM10.0004 0C4.4774 0 0.000118256 4.47728 0.000118256 10.0003C0.000118256 15.5233 4.4774 20.0006 10.0004 20.0006C15.5234 20.0006 20.0007 15.5233 20.0007 10.0003C20.0007 4.47728 15.5234 0 10.0004 0ZM10.7507 4.45012C10.7507 4.0359 10.4149 3.70012 10.0007 3.70012C9.58649 3.70012 9.2507 4.0359 9.2507 4.45012V4.69652C7.74232 4.9855 6.47562 6.12826 6.47562 7.68772C6.47562 9.24717 7.74232 10.3899 9.2507 10.6789V13.7668C8.44729 13.5195 7.97562 12.8912 7.97562 12.3129C7.97562 11.8987 7.63983 11.5629 7.22562 11.5629C6.8114 11.5629 6.47562 11.8987 6.47562 12.3129C6.47562 13.8723 7.74232 15.0151 9.2507 15.3041V15.5505C9.2507 15.9647 9.58649 16.3005 10.0007 16.3005C10.4149 16.3005 10.7507 15.9647 10.7507 15.5505V15.3041C12.2591 15.0151 13.5258 13.8723 13.5258 12.3129C13.5258 10.7534 12.2591 9.61065 10.7507 9.32166V6.23376C11.5541 6.48105 12.0258 7.10943 12.0258 7.68772C12.0258 8.10193 12.3616 8.43772 12.7758 8.43772C13.19 8.43772 13.5258 8.10193 13.5258 7.68772C13.5258 6.12826 12.2591 4.9855 10.7507 4.69652V4.45012ZM7.97562 7.68772C7.97562 7.10943 8.44729 6.48105 9.2507 6.23376V9.14167C8.44729 8.89439 7.97562 8.26601 7.97562 7.68772ZM12.0258 12.3129C12.0258 12.8912 11.5541 13.5195 10.7507 13.7668V10.8589C11.5541 11.1062 12.0258 11.7346 12.0258 12.3129Z" fill="#5b5c6a"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

@ -0,0 +1,9 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.29167 6.875C7.29167 7.33524 6.91857 7.70833 6.45833 7.70833C5.9981 7.70833 5.625 7.33524 5.625 6.875C5.625 6.41476 5.9981 6.04167 6.45833 6.04167C6.91857 6.04167 7.29167 6.41476 7.29167 6.875Z" fill="#A1A2AB"/>
<path d="M8.75 6.875C8.75 6.52982 9.02982 6.25 9.375 6.25H13.9583C14.3035 6.25 14.5833 6.52982 14.5833 6.875C14.5833 7.22018 14.3035 7.5 13.9583 7.5H9.375C9.02982 7.5 8.75 7.22018 8.75 6.875Z" fill="#A1A2AB"/>
<path d="M8.75005 10C8.75005 9.65482 9.02987 9.375 9.37505 9.375H13.9583C14.3035 9.375 14.5833 9.65482 14.5833 10C14.5833 10.3452 14.3035 10.625 13.9583 10.625H9.37505C9.02987 10.625 8.75005 10.3452 8.75005 10Z" fill="#A1A2AB"/>
<path d="M9.37505 12.5C9.02987 12.5 8.75005 12.7798 8.75005 13.125C8.75005 13.4702 9.02987 13.75 9.37505 13.75H13.9583C14.3035 13.75 14.5833 13.4702 14.5833 13.125C14.5833 12.7798 14.3035 12.5 13.9583 12.5H9.37505Z" fill="#A1A2AB"/>
<path d="M6.45833 10.8333C6.91857 10.8333 7.29167 10.4602 7.29167 10C7.29167 9.53976 6.91857 9.16667 6.45833 9.16667C5.9981 9.16667 5.625 9.53976 5.625 10C5.625 10.4602 5.9981 10.8333 6.45833 10.8333Z" fill="#A1A2AB"/>
<path d="M7.29167 13.125C7.29167 13.5852 6.91857 13.9583 6.45833 13.9583C5.9981 13.9583 5.625 13.5852 5.625 13.125C5.625 12.6648 5.9981 12.2917 6.45833 12.2917C6.91857 12.2917 7.29167 12.6648 7.29167 13.125Z" fill="#A1A2AB"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.375 2.5C3.33947 2.5 2.5 3.33947 2.5 4.375V15.625C2.5 16.6605 3.33947 17.5 4.375 17.5H15.625C16.6605 17.5 17.5 16.6605 17.5 15.625V4.375C17.5 3.33947 16.6605 2.5 15.625 2.5H4.375ZM3.75 4.375C3.75 4.02982 4.02982 3.75 4.375 3.75H15.625C15.9702 3.75 16.25 4.02982 16.25 4.375V15.625C16.25 15.9702 15.9702 16.25 15.625 16.25H4.375C4.02982 16.25 3.75 15.9702 3.75 15.625V4.375Z" fill="#A1A2AB"/>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.375 2.5C3.33947 2.5 2.5 3.33947 2.5 4.375V15.625C2.5 16.6605 3.33947 17.5 4.375 17.5H15.625C16.6605 17.5 17.5 16.6605 17.5 15.625V4.375C17.5 3.33947 16.6605 2.5 15.625 2.5H4.375ZM7.29167 6.875C7.29167 7.33524 6.91857 7.70833 6.45833 7.70833C5.9981 7.70833 5.625 7.33524 5.625 6.875C5.625 6.41476 5.9981 6.04167 6.45833 6.04167C6.91857 6.04167 7.29167 6.41476 7.29167 6.875ZM8.75 6.875C8.75 6.52982 9.02982 6.25 9.375 6.25H13.9583C14.3035 6.25 14.5833 6.52982 14.5833 6.875C14.5833 7.22018 14.3035 7.5 13.9583 7.5H9.375C9.02982 7.5 8.75 7.22018 8.75 6.875ZM8.75005 10C8.75005 9.65482 9.02987 9.375 9.37505 9.375H13.9583C14.3035 9.375 14.5833 9.65482 14.5833 10C14.5833 10.3452 14.3035 10.625 13.9583 10.625H9.37505C9.02987 10.625 8.75005 10.3452 8.75005 10ZM9.37505 12.5C9.02987 12.5 8.75005 12.7798 8.75005 13.125C8.75005 13.4702 9.02987 13.75 9.37505 13.75H13.9583C14.3035 13.75 14.5833 13.4702 14.5833 13.125C14.5833 12.7798 14.3035 12.5 13.9583 12.5H9.37505ZM6.45833 10.8333C6.91857 10.8333 7.29167 10.4602 7.29167 10C7.29167 9.53976 6.91857 9.16667 6.45833 9.16667C5.9981 9.16667 5.625 9.53976 5.625 10C5.625 10.4602 5.9981 10.8333 6.45833 10.8333ZM7.29167 13.125C7.29167 13.5852 6.91857 13.9583 6.45833 13.9583C5.9981 13.9583 5.625 13.5852 5.625 13.125C5.625 12.6648 5.9981 12.2917 6.45833 12.2917C6.91857 12.2917 7.29167 12.6648 7.29167 13.125Z" fill="#F99E2C"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

@ -0,0 +1,28 @@
export type TAccounting = {
full_name: string;
is_confirmed: boolean;
id: number;
number_of_tasks: number;
performance_based_amount: string;
salary: string;
salary_base_amount: string;
salary_type: string;
total_bonuses: string;
total_charges: string;
total_points: number;
username: string;
};
export type TAccountingHistory = {
id: number;
full_name: string;
username: string;
total_number_of_tasks: number;
salary_type: string;
total_bonuses: string;
total_charges: string;
total_earned_points: string;
total_base_salary: string;
total_performance_salary: string;
total_earned_salary: string;
};

@ -13,6 +13,36 @@ export type TStatTeam = {
total_points: number;
};
export type TStatCreators = {
id: number;
username: string;
full_name: string;
number_of_tasks: number;
};
type TaskPerformance = {
number_of_tasks: number;
total_points: number;
};
export type TteamChartData = {
date: string;
[category: string]: TaskPerformance | string;
};
export type TGeneralDailyStat = {
task_date: string;
total_tasks: number;
completed_tasks: number;
incomplete_tasks: number;
};
export type TGeneralWrapper = {
daily_stats: TGeneralDailyStat[];
};
export type TGeneralChartData = {
daily_stats: TGeneralWrapper[];
};
export type TCard = {
all_tasks: number;
active_tasks: number;

@ -10,3 +10,12 @@ export type TUser = {
salary_type: string;
salary_base_amount: number;
}
export type TUserResponse = {
page: number;
page_size: number;
next: string | null;
previous: string | null;
current_time: string;
data: TUser[];
};
Loading…
Cancel
Save