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;
|
||||||
|
},
|
||||||
|
};
|
@ -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>
|
||||||
|
<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>
|
||||||
|
<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>
|
||||||
|
<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;
|
@ -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;
|
@ -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,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.8 KiB |
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;
|
||||||
|
};
|
Loading…
Reference in new issue