diff --git a/src/admin/adminUserCard/adminUserCard.css b/src/admin/adminUserCard/adminUserCard.css
new file mode 100644
index 0000000..5a14102
--- /dev/null
+++ b/src/admin/adminUserCard/adminUserCard.css
@@ -0,0 +1,34 @@
+.userCard{
+ padding-top: 200px;
+ width: 100%;
+ height: 100vh;
+ background: linear-gradient(85.91deg, #096DD9 0%, #40A9FF 100%);
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+}
+
+.userCardWrapper{
+ display: flex;
+ flex-direction: column;
+ gap:15px;
+ justify-content: flex-start;
+ align-items: flex-start;
+}
+
+.nameText{
+ font-weight: 400;
+ font-size: 16px;
+ line-height: 24px;
+ color: rgba(0, 0, 0, 0.85)
+}
+.fieldText{
+ font-size: 14px;
+ line-height: 22px;
+ color: rgba(0, 0, 0, 0.45);
+}
+.btnWrapper{
+ display: flex;
+ flex-direction: row;
+ gap: 25px
+}
\ No newline at end of file
diff --git a/src/admin/adminUserCard/index.tsx b/src/admin/adminUserCard/index.tsx
new file mode 100644
index 0000000..7efff1b
--- /dev/null
+++ b/src/admin/adminUserCard/index.tsx
@@ -0,0 +1,84 @@
+import { Button, Input } from "antd";
+import React, { useState } from "react";
+import { useNavigate, useParams } from "react-router-dom";
+import { fetchChangeEmployer, fetchDelEmployer, getEmployerByTg } from "../../app/admin/adminSlice";
+import { RootAdminState } from "../../app/adminStore";
+import { useAppDispatch, useAppSelector } from "../../app/hooks";
+import "./adminUserCard.css"
+
+export const AdminUserCard:React.FC = () => {
+ let {tg} = useParams()
+ let user = useAppSelector((state: RootAdminState) => getEmployerByTg(state, tg as string))
+ let dispatch = useAppDispatch()
+ let navigate = useNavigate()
+
+ const [name, setName] = useState(user.name)
+ const [balance, setbalance] = useState(user.balance)
+ const [respect, setrespect] = useState(user.respect)
+ const [command, setcommand] = useState(user.name)
+ const [jobTittle, setjobTittle] = useState(user.jobTittle)
+
+
+ const onDelete = () =>{
+ fetchDelEmployer(dispatch, user.telegramID)
+ }
+
+ const onSave = () =>{
+ fetchChangeEmployer(dispatch,{
+ name:name,
+ balance:balance,
+ respect: respect,
+ speciality: jobTittle,
+ tg:user.telegramID
+ })
+ alert("Данные сохранены!")
+ navigate("/admin/users")
+ }
+ return(
+
+
+
+ Редактирование пользователя: {user.name}
+
+
+
+
Telegram
+
{user.telegramID}
+
+
+
Кошелек
+
{user.wallet}
+
+
+
+
ФИО
+
setName(e.target.value)} placeholder="ФИО">
+
+
+
Баланс
+
setbalance(Number(e.target.value))} placeholder="баланс">
+
+
+
Респект
+
setrespect(Number(e.target.value))} placeholder="респект">
+
+
+
Команда
+
setcommand(e.target.value)} placeholder="команда">
+
+
+
Специальность
+
setjobTittle(e.target.value)} placeholder="специальность">
+
+
+
+
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/src/admin/userTable/index.tsx b/src/admin/userTable/index.tsx
new file mode 100644
index 0000000..f7dbfd6
--- /dev/null
+++ b/src/admin/userTable/index.tsx
@@ -0,0 +1,224 @@
+import { Button, Space, Table, Tag } from 'antd';
+import type { ColumnsType } from 'antd/es/table';
+import React, { useReducer, useState } from 'react';
+import { Link, useNavigate } from 'react-router-dom';
+import { clearScreenDown } from 'readline';
+import { getClans, getEmployers, getUser, setClans, setEmployers } from '../../app/admin/adminSlice';
+import { RootAdminState } from '../../app/adminStore';
+import { adminFetcher, useAppDispatch, useAppSelector } from '../../app/hooks';
+import { ClanIE, EmployerIE } from '../../app/interfaces';
+import { Header } from '../../components/Header';
+import "./userTable.css"
+
+interface DataTypeIE{
+ key:string,
+ name:string,
+ telegram:string,
+ command:string,
+ respect:number,
+ balance:number,
+ speciality: string,
+}
+interface ClanColumnsIE{
+ key: string,
+ name: string,
+ tg: string,
+ members: number
+}
+
+
+export const UserTable:React.FC = () =>{
+ let [clans, setclans] = useState(false)
+
+ let navigator = useNavigate()
+ const collator = new Intl.Collator('ru');
+ let user = useAppSelector((state:RootAdminState)=>getUser(state))
+ const clanColums:ColumnsType
= [
+ {
+ title: "+",
+ key: "action",
+ render: (_, record) =>(
+ navigator("/admin/clans/" + record.name)} src="/gear.svg">
+ )
+ },
+ {
+ title: 'Название клана',
+ dataIndex: 'name',
+ key: 'name',
+ defaultSortOrder: 'descend',
+ sorter: (a, b) => collator.compare(a.name, b.name) ,
+ },
+ {
+ title: 'Telegram клана',
+ dataIndex: 'name',
+ key: 'name',
+ render: (_, record) => (@{record.name.split(" ").join("")}
),
+ sorter: (a, b) => collator.compare(a.name, b.name) ,
+ },
+ {
+ title: 'Количество участников',
+ dataIndex: 'members',
+ key: 'members',
+ sorter: (a, b) => a.members - b.members ,
+ },
+ {
+ title: 'Участники',
+ dataIndex: 'members',
+ key: 'members',
+ render: (_, record) => (Посмотреть
),
+ sorter: (a, b) => collator.compare(a.name, b.name) ,
+ },
+ ]
+ const columns: ColumnsType = [
+ {
+ title: "+",
+ key: "action",
+ render: (_, record) =>(
+ navigator("/admin/users/" + record.key)} src="/gear.svg">
+ )
+ },
+ {
+ title: 'ФИО',
+ dataIndex: 'name',
+ key: 'name',
+ defaultSortOrder: 'descend',
+ sorter: (a, b) => collator.compare(a.name, b.name) ,
+ },
+ {
+ title: 'Telegram',
+ dataIndex: 'telegram',
+ key: 'telegram',
+ sorter: (a, b) => collator.compare(a.telegram, b.telegram) ,
+
+ },
+ {
+ title: 'Название клана',
+ dataIndex: 'command',
+ key: 'command',
+ sorter: (a, b) => Number(a.command) - Number(b.command) ,
+
+ },
+ {
+ title: 'Респект',
+ dataIndex: 'respect',
+ key: 'respect',
+ sorter: (a, b) => a.respect - b.respect,
+ render: text=>{text + " "}
+ },
+ {
+ title: 'Баланс',
+ dataIndex: 'balance',
+ key: 'balance',
+ sorter: (a, b) => a.balance - b.balance,
+
+ render: text=>{text + " "}
+ },
+ {
+ title: 'Специальность',
+ dataIndex: 'speciality',
+ key: 'speciality',
+ sorter: (a, b) => collator.compare(a.speciality, b.speciality) ,
+ render: text=>
+ {text}
+
+ },
+ ]
+
+ let dispatch = useAppDispatch()
+
+ const data:DataTypeIE[] = []
+ const clanData:ClanColumnsIE[] = []
+
+ let users = useAppSelector((state:RootAdminState)=>getEmployers(state))
+ let clansData = useAppSelector((state:RootAdminState)=>getClans(state))
+ if (users.length == 0){
+ adminFetcher.get("users/").then((response)=>{
+ dispatch(setEmployers(response.data.map(((user:any)=>({
+ name: user.name,
+ wallet: user.wallet_public_key,
+ telegramID: user.telegram,
+ command: user.command,
+ role: user.type,
+ respect: user.respect,
+ balance: user.salary,
+ jobTittle: user.department,
+ }) as EmployerIE ))))
+ })
+ adminFetcher.get("season/clans/").then((response:any)=>{
+ dispatch(setClans(
+ response.data.map((clan:any)=>({
+ name: clan.name,
+ users: clan.users.map(((user:any)=>({
+ name: user.name,
+ wallet: user.wallet_public_key,
+ telegramID: user.telegram,
+ command: user.command,
+ role: user.type,
+ respect: user.respect,
+ balance: user.salary,
+ jobTittle: user.department,
+ }) ))
+ } as ClanIE))
+ ))
+ })
+ }
+ if (users.length > 0){
+ users.forEach((user)=>{
+ data.push(
+ {
+ name: user.name,
+ balance: user.balance,
+ command: user.command,
+ speciality: user.jobTittle,
+ key: user.telegramID,
+ telegram: user.telegramID,
+ respect: user.respect,
+ } as DataTypeIE
+ )
+ })
+ clansData.forEach((clan)=>{
+ clanData.push({
+ key:clan.name,
+ name:clan.name,
+ tg: clan.name,
+ members:clan.users.length
+ } as ClanColumnsIE)
+ })
+ }
+
+ return(
+
+
+
+
Участники VTB Games
+
+
+
+
+
+
+
+
+ {
+ clans?
:
+ }
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/src/admin/userTable/userTable.css b/src/admin/userTable/userTable.css
new file mode 100644
index 0000000..5c1bc49
--- /dev/null
+++ b/src/admin/userTable/userTable.css
@@ -0,0 +1,17 @@
+.userTable{
+ padding-top: 200px;
+ width: 100%;
+ height: 100vh;
+ background: linear-gradient(85.91deg, #096DD9 0%, #40A9FF 100%);
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+}
+
+.userTableWrapper{
+ display: flex;
+ flex-direction: column;
+ gap:15px;
+ justify-content: flex-start;
+ align-items: flex-start;
+}
\ No newline at end of file
diff --git a/src/app/admin/adminSlice.ts b/src/app/admin/adminSlice.ts
index 21042a3..fd5904d 100644
--- a/src/app/admin/adminSlice.ts
+++ b/src/app/admin/adminSlice.ts
@@ -1,9 +1,10 @@
import { createAsyncThunk, createSlice, createSelector, PayloadAction } from '@reduxjs/toolkit'
import { stat } from 'fs'
-import { Market, Roles, SortTypes, UserIE, ProductIE} from '../interfaces'
+import { Market, Roles, SortTypes, UserIE, ProductIE, EmployerIE, ClanIE} from '../interfaces'
import { AppAdminDispatch, RootAdminState } from '../adminStore'
import { adminFetcher, useAppDispatch } from '../hooks'
import { host, token } from '../consts'
+import { triggerAsyncId } from 'async_hooks'
@@ -13,14 +14,16 @@ const initState = {
user: {
wallet:"123214",
balance: 100,
- id:1,
+ id:"1",
role:Roles.admin,
name:"Firesieht"
} as UserIE, //потом достается запросом
market: {
sortType: SortTypes.sortByPriceSmaller,
products: []
- } as Market // потом достается запросом
+ } as Market, // потом достается запросом
+ employers: [] as EmployerIE[],
+ clans: [] as ClanIE[]
}
const adminSlice = createSlice(
@@ -28,6 +31,28 @@ const adminSlice = createSlice(
name: "adminSlice",
initialState: initState,
reducers:{
+ setClans(state, action:PayloadAction){
+ state.clans = action.payload
+ },
+ setEmployers(state, action:PayloadAction){
+ state.employers = action.payload
+ },
+ addEmployer(state, action:PayloadAction){
+ if(state.employers.length == 0){
+ state.employers = state.employers.concat([action.payload])
+ }
+ },
+ delEmployer(state, action:PayloadAction){
+ let employers = state.employers
+ let ind = 0
+ employers.forEach((employer, index)=>{
+ if (employer.telegramID == action.payload){
+ ind = index
+ }
+ })
+ employers.splice(ind, 1)
+ state.employers = employers
+ },
sendCoins(state, action: PayloadAction){
state.user.balance = state.user.balance - action.payload
},
@@ -52,9 +77,17 @@ const adminSlice = createSlice(
products.splice(ind, 1)
state.market.products = products
},
+ changeEmployer(state, action:PayloadAction){
+ let employers = state.employers
+ employers.forEach((product,index)=>{
+ if (product.telegramID == action.payload.telegramID){
+ employers[index] = action.payload
+ }
+ })
+ state.employers = employers
+ },
changeProduct(state, action:PayloadAction){
let products = state.market.products
- console.log(action.payload)
products.forEach((product,index)=>{
if (product.id == action.payload.id){
products[index] = action.payload
@@ -118,6 +151,55 @@ export async function fetchChangeProduct(dispatch:AppAdminDispatch, params:{id:s
}
+export async function fetchAddEmployer(dispatch:AppAdminDispatch, params:{pass:string, name: string, respect: string, salary:string, command:string, tg:string, type: string}) {
+ const formData = new FormData()
+ formData.append("name",params.name)
+ formData.append("type",params.type)
+ formData.append("respect", params.respect.toString())
+ formData.append("salary",params.salary.toString())
+ formData.append("telegram",params.tg)
+ formData.append("command", params.command)
+ formData.append("password", params.pass)
+ adminFetcher.post("users/", formData).then((user)=>{
+ dispatch(addEmployer({
+ name: user.data.name,
+ wallet: user.data.wallet_public_key,
+ telegramID: user.data.telegram,
+ command: user.data.command,
+ role: user.data.type,
+ respect: user.data.respect,
+ balance: user.data.salary,
+ jobTittle: user.data.department,
+ } as EmployerIE))
+
+ })
+}
+
+export async function fetchDelEmployer(dispatch:AppAdminDispatch, tg:string) {
+ adminFetcher.delete("users/"+tg).then(()=>dispatch(delProduct(tg)))
+}
+
+export async function fetchChangeEmployer(dispatch:AppAdminDispatch, params:{name: string, respect: number, balance:number, speciality:string, tg:string}) {
+ const formData = new FormData()
+ formData.append("name",params.name)
+ formData.append("respect", params.respect.toString())
+ formData.append("salary",params.balance.toString())
+ adminFetcher.patch("users/" + params.tg, formData).then((user)=>{
+ console.log(user)
+ dispatch(changeEmployer({
+ name: user.data.name,
+ wallet: user.data.wallet_public_key,
+ telegramID: user.data.telegram,
+ command: user.data.command,
+ role: user.data.type,
+ respect: user.data.respect,
+ balance: user.data.salary,
+ jobTittle: user.data.department,
+ } as EmployerIE))
+
+ })
+}
+
export const getProducts = createSelector(
(state:RootAdminState) => state.adminSlice.market.products,
(field)=>field
@@ -128,6 +210,11 @@ export const getProductByID = createSelector(
(field)=>field
)
+export const getEmployerByTg = createSelector(
+ (state:RootAdminState, tg:string) => state.adminSlice.employers.filter((product)=>product.telegramID == tg)[0],
+ (field)=>field
+)
+
export const getSortType = createSelector(
(state:RootAdminState) => state.adminSlice.market.sortType,
(field)=>field
@@ -136,13 +223,30 @@ export const getUser = createSelector(
(state:RootAdminState) => state.adminSlice.user,
(field)=>field
)
+export const getEmployers = createSelector(
+ (state:RootAdminState) => state.adminSlice.employers,
+ (field)=>field
+)
+export const getClans = createSelector(
+ (state:RootAdminState) => state.adminSlice.clans,
+ (field)=>field
+)
+export const getClanByName = createSelector(
+ (state:RootAdminState, name: string) => state.adminSlice.clans.filter((clan)=>clan.name == name)[0],
+ (field)=>field
+)
export const {
sendCoins,
addProduct,
delProduct,
changeProduct,
- addProducts
+ addProducts,
+ setEmployers,
+ addEmployer,
+ delEmployer,
+ changeEmployer,
+ setClans
} = adminSlice.actions
export default adminSlice.reducer
\ No newline at end of file
diff --git a/src/app/hooks.ts b/src/app/hooks.ts
index 350cd1c..4ea28b1 100644
--- a/src/app/hooks.ts
+++ b/src/app/hooks.ts
@@ -9,7 +9,7 @@ export const useAppSelector: TypedUseSelectorHook = useSelector;
export const adminFetcher = axios.create(
{
baseURL: host,
- timeout: 1000,
+ timeout: 5000,
headers: {
Authorization: 'Bearer ' + token
}
diff --git a/src/app/interfaces.ts b/src/app/interfaces.ts
index 6efc594..a9aafeb 100644
--- a/src/app/interfaces.ts
+++ b/src/app/interfaces.ts
@@ -14,7 +14,6 @@ export enum Specialities{
export interface UserIE{
wallet:string,
- id:number
role:Roles
balance: number,
name:string,
@@ -23,8 +22,10 @@ export interface UserIE{
export interface EmployerIE extends UserIE{
- jobTittle:Specialities,
+ jobTittle:string,
respect: number,
+ telegramID: string,
+ command: string,
}
@@ -55,3 +56,8 @@ export interface Market{
sortType: SortTypes,
products: ProductIE[],
}
+
+export interface ClanIE{
+ name:string,
+ users:UserIE[]
+}
\ No newline at end of file
diff --git a/src/index.tsx b/src/index.tsx
index 392ee03..375d983 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -17,6 +17,10 @@ import {
Link,
Routes
} from "react-router-dom";
+import { UserTable } from './admin/userTable';
+import { AdminUserCard } from './admin/adminUserCard';
+import { AdminClans } from './admin/adminClans';
+import { AddUser } from './admin/addUser';
const container = document.getElementById('root')!;
const root = createRoot(container);
@@ -27,6 +31,11 @@ const router = createBrowserRouter(
}>
}>
}>
+ }>
+ }>
+ }>
+ }>
+