From fa344c09e4942b0b5815820f485bf672414d1d7e Mon Sep 17 00:00:00 2001 From: Firesieht Date: Sun, 9 Oct 2022 01:47:55 +0300 Subject: [PATCH] add ALLL --- public/gear.svg | 3 + public/respect.svg | 10 + src/admin/addAdminMarketCard/index.tsx | 8 +- src/admin/addUser/index.tsx | 88 +++++++++ src/admin/adminClans/adminClans.css | 0 src/admin/adminClans/index.tsx | 101 ++++++++++ src/admin/adminMarket/index.tsx | 8 +- src/admin/adminUserCard/adminUserCard.css | 34 ++++ src/admin/adminUserCard/index.tsx | 84 ++++++++ src/admin/userTable/index.tsx | 224 ++++++++++++++++++++++ src/admin/userTable/userTable.css | 17 ++ src/app/admin/adminSlice.ts | 114 ++++++++++- src/app/hooks.ts | 2 +- src/app/interfaces.ts | 10 +- src/index.tsx | 9 + 15 files changed, 696 insertions(+), 16 deletions(-) create mode 100644 public/gear.svg create mode 100644 public/respect.svg create mode 100644 src/admin/addUser/index.tsx create mode 100644 src/admin/adminClans/adminClans.css create mode 100644 src/admin/adminClans/index.tsx create mode 100644 src/admin/adminUserCard/adminUserCard.css create mode 100644 src/admin/adminUserCard/index.tsx create mode 100644 src/admin/userTable/index.tsx create mode 100644 src/admin/userTable/userTable.css diff --git a/public/gear.svg b/public/gear.svg new file mode 100644 index 0000000..3e563cd --- /dev/null +++ b/public/gear.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/respect.svg b/public/respect.svg new file mode 100644 index 0000000..a7180cb --- /dev/null +++ b/public/respect.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/admin/addAdminMarketCard/index.tsx b/src/admin/addAdminMarketCard/index.tsx index 7814332..ba85bf1 100644 --- a/src/admin/addAdminMarketCard/index.tsx +++ b/src/admin/addAdminMarketCard/index.tsx @@ -35,6 +35,10 @@ export const AddAdminMarketProduct:React.FC = () =>{ return(
{ link:"/admin/market/add", name:"Cоздать товар (NFT)" }, - { - link:"/admin/users", - name:"Участники" - } ]} name={user.name}>
diff --git a/src/admin/addUser/index.tsx b/src/admin/addUser/index.tsx new file mode 100644 index 0000000..ed87661 --- /dev/null +++ b/src/admin/addUser/index.tsx @@ -0,0 +1,88 @@ +import { Button, Input } from "antd"; +import { Dropdown, Menu, Space } from 'antd'; + +import React, { useState } from "react"; +import { useNavigate, useParams } from "react-router-dom"; +import { fetchAddEmployer, fetchChangeEmployer, fetchDelEmployer, getEmployerByTg } from "../../app/admin/adminSlice"; +import { RootAdminState } from "../../app/adminStore"; +import { useAppDispatch, useAppSelector } from "../../app/hooks"; +import "../adminUserCard/adminUserCard.css" + +export const AddUser:React.FC = () => { + let dispatch = useAppDispatch() + let navigate = useNavigate() + const [type, setType] = useState("WORKER") //WORKER HR ADMIN + const [salary, setSalary] = useState("10") + const [tg, setTg] = useState("") + const [pass, setPass] = useState("") + const [name, setName] = useState("") + const [respect, setrespect] = useState("100") + const [command, setcommand] = useState("") + + + + const onSave = () =>{ + console.log(type) + fetchAddEmployer(dispatch,{ + name:name, + salary:salary, + respect: respect, + type: type, + pass:pass, + tg:tg, + command: command + }) + alert("Пользователь добавлен!") + navigate("/admin/users") + } + + + + return( +
+
+
+ Создание пользователя +
+
+
+
Telegram
+ setTg(e.target.value)} placeholder="Telegram"> +
+
+
Роль
+ +
+
+
ФИО
+ setName(e.target.value)} placeholder="ФИО"> +
+
+
Пароль
+ setPass(e.target.value)} placeholder="ФИО"> +
+ +
+
Зарплата
+ setSalary(e.target.value)} placeholder="баланс"> +
+
+
Респект
+ setrespect(e.target.value)} placeholder="респект"> +
+
+
Команда
+ setcommand(e.target.value)} placeholder="команда"> +
+
+
+ +
+
+
+ ); +} \ No newline at end of file diff --git a/src/admin/adminClans/adminClans.css b/src/admin/adminClans/adminClans.css new file mode 100644 index 0000000..e69de29 diff --git a/src/admin/adminClans/index.tsx b/src/admin/adminClans/index.tsx new file mode 100644 index 0000000..87f92e8 --- /dev/null +++ b/src/admin/adminClans/index.tsx @@ -0,0 +1,101 @@ +import "./adminClans.css" +import { Button, Space, Table, Tag } from 'antd'; +import { useNavigate, useParams } from "react-router-dom"; +import { getClanByName, getEmployers } from "../../app/admin/adminSlice"; +import { useAppSelector } from "../../app/hooks"; +import type { ColumnsType } from 'antd/es/table'; +import { RootAdminState } from "../../app/adminStore"; + + +interface DataTypeIE{ + key:string, + name:string, + telegram:string, + respect:number, + balance:number, + speciality: string, +} + + +export const AdminClans:React.FC = () =>{ + let {name} = useParams() + let clan = useAppSelector((state)=>getClanByName(state, name as string)) + let navigator = useNavigate() + const collator = new Intl.Collator('ru'); + let users = useAppSelector((state:RootAdminState)=>getEmployers(state)) + const data:DataTypeIE[] = [] + + 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: '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} + + }, + ] + + 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 + ) + }) + + } + return( +
+
+
Участники клана {name}
+ + + + + ); +} \ No newline at end of file diff --git a/src/admin/adminMarket/index.tsx b/src/admin/adminMarket/index.tsx index 4c17077..c420c54 100644 --- a/src/admin/adminMarket/index.tsx +++ b/src/admin/adminMarket/index.tsx @@ -48,6 +48,10 @@ export const AdminMarket:React.FC = () =>{ return(
{ link:"/admin/market/add", name:"Cоздать товар (NFT)" }, - { - link:"/admin/users", - name:"Участники" - } ]} name={user.name}>
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}
+
+
+
Роль
+
{user.role}
+
+
+
ФИО
+ 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( }> }> }> + }> + }> + }> + }> +