mirror of
https://github.com/more-tech4-magnum-opus/frontend.git
synced 2024-11-22 00:26:35 +03:00
Merge pull request #2 from more-tech4-magnum-opus/clansAdmin
Clans admin
This commit is contained in:
commit
f9b70590c0
3
public/gear.svg
Normal file
3
public/gear.svg
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<svg width="15" height="14" viewBox="0 0 15 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M14.3151 8.77952L13.234 7.90445C13.2852 7.60756 13.3116 7.30441 13.3116 7.00127C13.3116 6.69812 13.2852 6.39497 13.234 6.09808L14.3151 5.22302C14.3967 5.15693 14.455 5.06891 14.4825 4.97066C14.5099 4.8724 14.5051 4.76858 14.4686 4.67298L14.4538 4.63235C14.1562 3.84484 13.7105 3.1148 13.1383 2.47752L13.1086 2.4447C13.0392 2.36743 12.9467 2.31189 12.8432 2.28539C12.7398 2.25889 12.6303 2.26268 12.5292 2.29625L11.1873 2.74785C10.6922 2.36345 10.1392 2.0603 9.54173 1.84778L9.2826 0.519567C9.26305 0.419626 9.21184 0.327683 9.13578 0.255952C9.05971 0.184221 8.9624 0.136098 8.85675 0.117977L8.81219 0.110164C7.95225 -0.0367213 7.04775 -0.0367213 6.18781 0.110164L6.14325 0.117977C6.0376 0.136098 5.94029 0.184221 5.86422 0.255952C5.78816 0.327683 5.73695 0.419626 5.7174 0.519567L5.45662 1.85403C4.86388 2.06659 4.3119 2.36958 3.82257 2.75097L2.47076 2.29625C2.36969 2.26241 2.26014 2.25849 2.15666 2.285C2.05317 2.31152 1.96066 2.36722 1.89142 2.4447L1.86171 2.47752C1.2902 3.11525 0.844599 3.84517 0.546219 4.63235L0.531364 4.67298C0.457089 4.86831 0.518159 5.08707 0.684865 5.22302L1.77918 6.10746C1.72802 6.40123 1.70326 6.70125 1.70326 6.9997C1.70326 7.29972 1.72802 7.59975 1.77918 7.89195L0.684865 8.77639C0.603321 8.84248 0.544959 8.9305 0.517539 9.02875C0.490119 9.12701 0.494941 9.23083 0.531364 9.32643L0.546219 9.36706C0.844969 10.1546 1.28732 10.8812 1.86171 11.5219L1.89142 11.5547C1.96083 11.632 2.05335 11.6875 2.15677 11.714C2.26019 11.7405 2.36966 11.7367 2.47076 11.7032L3.82257 11.2484C4.31443 11.6313 4.86407 11.9344 5.45662 12.1454L5.7174 13.4798C5.73695 13.5798 5.78816 13.6717 5.86422 13.7435C5.94029 13.8152 6.0376 13.8633 6.14325 13.8814L6.18781 13.8892C7.05565 14.0369 7.94435 14.0369 8.81219 13.8892L8.85675 13.8814C8.9624 13.8633 9.05971 13.8152 9.13578 13.7435C9.21184 13.6717 9.26305 13.5798 9.2826 13.4798L9.54173 12.1516C10.139 11.9397 10.695 11.6355 11.1873 11.2516L12.5292 11.7032C12.6303 11.737 12.7399 11.7409 12.8433 11.7144C12.9468 11.6879 13.0393 11.6322 13.1086 11.5547L13.1383 11.5219C13.7127 10.8797 14.155 10.1546 14.4538 9.36706L14.4686 9.32643C14.5429 9.13423 14.4818 8.91546 14.3151 8.77952V8.77952ZM12.0621 6.28247C12.1034 6.51842 12.1249 6.76063 12.1249 7.00283C12.1249 7.24503 12.1034 7.48724 12.0621 7.72319L11.9532 8.3498L13.1862 9.3483C12.9992 9.75597 12.7633 10.1419 12.483 10.4984L10.9513 9.98429L10.433 10.3874C10.0386 10.6937 9.5995 10.9344 9.12414 11.1031L8.49528 11.3266L8.19983 12.8423C7.73367 12.8923 7.26303 12.8923 6.79686 12.8423L6.50142 11.3234L5.87751 11.0969C5.4071 10.9281 4.9697 10.6875 4.57852 10.3828L4.06025 9.97804L2.51863 10.4968C2.23804 10.139 2.00366 9.75302 1.81549 9.34674L3.06166 8.33886L2.95438 7.71382C2.91476 7.48099 2.89331 7.24035 2.89331 7.00283C2.89331 6.76375 2.91311 6.52467 2.95438 6.29184L3.06166 5.6668L1.81549 4.65892C2.00201 4.25108 2.23804 3.86667 2.51863 3.50884L4.06025 4.02762L4.57852 3.62291C4.9697 3.3182 5.4071 3.07756 5.87751 2.9088L6.50307 2.68534L6.79851 1.16649C7.26232 1.11648 7.73603 1.11648 8.20149 1.16649L8.49693 2.68222L9.1258 2.90567C9.5995 3.07443 10.0402 3.31507 10.4347 3.62134L10.953 4.0245L12.4847 3.5104C12.7653 3.86824 12.9996 4.2542 13.1878 4.66048L11.9548 5.65899L12.0621 6.28247ZM7.50165 4.09638C5.89731 4.09638 4.59668 5.32771 4.59668 6.84657C4.59668 8.36542 5.89731 9.59676 7.50165 9.59676C9.10599 9.59676 10.4066 8.36542 10.4066 6.84657C10.4066 5.32771 9.10599 4.09638 7.50165 4.09638ZM8.80889 8.08416C8.63743 8.24694 8.43367 8.37603 8.20932 8.46399C7.98498 8.55195 7.74447 8.59705 7.50165 8.59669C7.00814 8.59669 6.54433 8.41387 6.19441 8.08416C6.02246 7.92183 5.88611 7.72893 5.7932 7.51654C5.70029 7.30414 5.65265 7.07645 5.65303 6.84657C5.65303 6.37935 5.84615 5.94026 6.19441 5.60898C6.54433 5.27771 7.00814 5.09645 7.50165 5.09645C7.99517 5.09645 8.45897 5.27771 8.80889 5.60898C8.98084 5.7713 9.11719 5.96421 9.2101 6.1766C9.30301 6.38899 9.35065 6.61668 9.35027 6.84657C9.35027 7.31379 9.15716 7.75288 8.80889 8.08416Z" fill="#1890FF"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 4.0 KiB |
10
public/respect.svg
Normal file
10
public/respect.svg
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<svg width="15" height="14" viewBox="0 0 15 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect x="0.5" width="14" height="14" rx="7" fill="url(#paint0_linear_31_10595)"/>
|
||||||
|
<path d="M5.5 3.00005H9.53316C9.62502 2.99894 9.71616 3.01622 9.80124 3.05085C9.88631 3.08549 9.9636 3.13679 10.0286 3.20174C10.0935 3.2667 10.1448 3.34399 10.1795 3.42907C10.2141 3.51414 10.2314 3.6053 10.2303 3.69715V4.92535H7.38379V6.07058H10.2303V7.96268H7.38379V11H5.5V3.00005Z" fill="white"/>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="paint0_linear_31_10595" x1="0.5" y1="14" x2="15.4243" y2="12.9335" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#8637E9"/>
|
||||||
|
<stop offset="1" stop-color="#D33BE0"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 705 B |
|
@ -1,7 +1,7 @@
|
||||||
.addProduct{
|
.addProduct{
|
||||||
padding-top: 200px;
|
padding-top: 200px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100vh;
|
min-height: 100vh;
|
||||||
background: linear-gradient(85.91deg, #096DD9 0%, #40A9FF 100%);
|
background: linear-gradient(85.91deg, #096DD9 0%, #40A9FF 100%);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
|
@ -35,6 +35,10 @@ export const AddAdminMarketProduct:React.FC = () =>{
|
||||||
return(
|
return(
|
||||||
<div className="addProduct">
|
<div className="addProduct">
|
||||||
<Header links={[
|
<Header links={[
|
||||||
|
{
|
||||||
|
link:"/admin/users",
|
||||||
|
name:"Участники"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
link:"/admin/market",
|
link:"/admin/market",
|
||||||
name:"Market place"
|
name:"Market place"
|
||||||
|
@ -43,10 +47,6 @@ export const AddAdminMarketProduct:React.FC = () =>{
|
||||||
link:"/admin/market/add",
|
link:"/admin/market/add",
|
||||||
name:"Cоздать товар (NFT)"
|
name:"Cоздать товар (NFT)"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
link:"/admin/users",
|
|
||||||
name:"Участники"
|
|
||||||
}
|
|
||||||
]}
|
]}
|
||||||
name={user.name}></Header>
|
name={user.name}></Header>
|
||||||
<div className="addProductCard">
|
<div className="addProductCard">
|
||||||
|
|
88
src/admin/addUser/index.tsx
Normal file
88
src/admin/addUser/index.tsx
Normal file
|
@ -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(
|
||||||
|
<div className="userCard">
|
||||||
|
<div className="changeProductCard">
|
||||||
|
<div className="addProductH1">
|
||||||
|
Создание пользователя
|
||||||
|
</div>
|
||||||
|
<div className="userCardWrapper">
|
||||||
|
<div className="InpWrapper">
|
||||||
|
<div className="nameText">Telegram</div>
|
||||||
|
<Input value={tg} onChange={(e)=>setTg(e.target.value)} placeholder="Telegram"></Input>
|
||||||
|
</div>
|
||||||
|
<div className="InpWrapper">
|
||||||
|
<div className="nameText">Роль</div>
|
||||||
|
<select onChange={(e)=>setType(e.target.value)}>
|
||||||
|
<option>WORKER</option>
|
||||||
|
<option>HR</option>
|
||||||
|
<option>ADMIN</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div className="InpWrapper">
|
||||||
|
<div className="nameText">ФИО</div>
|
||||||
|
<Input value={name} onChange={(e)=>setName(e.target.value)} placeholder="ФИО"></Input>
|
||||||
|
</div>
|
||||||
|
<div className="InpWrapper">
|
||||||
|
<div className="nameText">Пароль</div>
|
||||||
|
<Input value={pass} onChange={(e)=>setPass(e.target.value)} placeholder="ФИО"></Input>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="InpWrapper">
|
||||||
|
<div className="nameText">Зарплата</div>
|
||||||
|
<Input type="number" value={salary} onChange={(e)=>setSalary(e.target.value)} placeholder="баланс"></Input>
|
||||||
|
</div>
|
||||||
|
<div className="InpWrapper">
|
||||||
|
<div className="nameText"> Респект</div>
|
||||||
|
<Input type="number" value={respect} onChange={(e)=>setrespect(e.target.value)} placeholder="респект"></Input>
|
||||||
|
</div>
|
||||||
|
<div className="InpWrapper">
|
||||||
|
<div className="nameText">Команда</div>
|
||||||
|
<Input type="number" value={command} onChange={(e)=>setcommand(e.target.value)} placeholder="команда"></Input>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="btnWrapper">
|
||||||
|
<Button className="btn2" onClick={()=>onSave()}>Добавить</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
0
src/admin/adminClans/adminClans.css
Normal file
0
src/admin/adminClans/adminClans.css
Normal file
101
src/admin/adminClans/index.tsx
Normal file
101
src/admin/adminClans/index.tsx
Normal file
|
@ -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<DataTypeIE> = [
|
||||||
|
{
|
||||||
|
title: "+",
|
||||||
|
key: "action",
|
||||||
|
render: (_, record) =>(
|
||||||
|
<img style={{cursor:"pointer"}} onClick={()=>navigator("/admin/users/" + record.key)} src="/gear.svg"></img>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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=><div>{text + " "} <img src="/respect.svg"></img></div>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Баланс',
|
||||||
|
dataIndex: 'balance',
|
||||||
|
key: 'balance',
|
||||||
|
sorter: (a, b) => a.balance - b.balance,
|
||||||
|
|
||||||
|
render: text=><div>{text + " "} <img src="/rub.svg"></img></div>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Специальность',
|
||||||
|
dataIndex: 'speciality',
|
||||||
|
key: 'speciality',
|
||||||
|
sorter: (a, b) => collator.compare(a.speciality, b.speciality) ,
|
||||||
|
render: text=><Tag color="green" key={text}>
|
||||||
|
{text}
|
||||||
|
</Tag>
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
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(
|
||||||
|
<div className="userTable">
|
||||||
|
<div className="changeProductCard">
|
||||||
|
<div className="addProductH1">Участники клана {name}</div>
|
||||||
|
<Table columns={columns} dataSource={data} />
|
||||||
|
<Button onClick={()=>navigator("/admin/users")} className="btn2">Назад</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -19,7 +19,7 @@
|
||||||
.market{
|
.market{
|
||||||
padding-top: 200px;
|
padding-top: 200px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100vh;
|
min-height: 100vh;
|
||||||
background: linear-gradient(85.91deg, #096DD9 0%, #40A9FF 100%);
|
background: linear-gradient(85.91deg, #096DD9 0%, #40A9FF 100%);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
|
@ -1,23 +1,18 @@
|
||||||
import React, { useState } from "react"
|
import React, { useState } from "react"
|
||||||
import { getProducts, getUser } from "../../app/admin/adminSlice";
|
import { addProduct, addProducts, fetchAddProduct, getProducts, getUser } from "../../app/admin/adminSlice";
|
||||||
import { RootAdminState } from "../../app/adminStore";
|
import { RootAdminState } from "../../app/adminStore";
|
||||||
import { useAppSelector } from "../../app/hooks";
|
import { adminFetcher, useAppDispatch, useAppSelector } from "../../app/hooks";
|
||||||
import { Header } from "../../components/Header";
|
import { Header } from "../../components/Header";
|
||||||
import { AdminMarketCard } from "../adminMarketCard";
|
import { AdminMarketCard } from "../adminMarketCard";
|
||||||
import "./adminMarket.css"
|
import "./adminMarket.css"
|
||||||
import {
|
import { ProductIE } from "../../app/interfaces";
|
||||||
BrowserRouter as Router,
|
|
||||||
Route,
|
|
||||||
Routes,
|
|
||||||
Link
|
|
||||||
} from "react-router-dom";
|
|
||||||
import { AdminMarketPopUp } from "../adminMarketPopUp";
|
|
||||||
|
|
||||||
export const AdminMarket:React.FC = () =>{
|
export const AdminMarket:React.FC = () =>{
|
||||||
const [opened, setOpened] = useState(false);
|
const [first, setFirst] = useState(true)
|
||||||
|
|
||||||
let user = useAppSelector((state:RootAdminState)=>getUser(state))
|
let user = useAppSelector((state:RootAdminState)=>getUser(state))
|
||||||
let cards: JSX.Element[] = []
|
let cards: JSX.Element[] = []
|
||||||
let products = useAppSelector(
|
let prod = useAppSelector(
|
||||||
(state: RootAdminState)=>getProducts(state)).forEach(
|
(state: RootAdminState)=>getProducts(state)).forEach(
|
||||||
product=>cards.push(<AdminMarketCard
|
product=>cards.push(<AdminMarketCard
|
||||||
name={product.name}
|
name={product.name}
|
||||||
|
@ -26,10 +21,37 @@ export const AdminMarket:React.FC = () =>{
|
||||||
cost={product.cost}
|
cost={product.cost}
|
||||||
id={product.id}
|
id={product.id}
|
||||||
></AdminMarketCard>))
|
></AdminMarketCard>))
|
||||||
|
let products = useAppSelector((state:RootAdminState)=>getProducts(state))
|
||||||
|
|
||||||
|
let dispatch = useAppDispatch()
|
||||||
|
|
||||||
|
if (products.length == 0 && first ){
|
||||||
|
setFirst(false)
|
||||||
|
adminFetcher.get("marketplace/product/").then(
|
||||||
|
(response)=>{
|
||||||
|
dispatch(addProducts(
|
||||||
|
response.data.map((params:any)=>
|
||||||
|
({
|
||||||
|
name: params.name,
|
||||||
|
description:params.description,
|
||||||
|
image: params.image_cropped,
|
||||||
|
cost:Number(params.price),
|
||||||
|
id:params.slug
|
||||||
|
} as ProductIE)
|
||||||
|
)
|
||||||
|
))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return(
|
return(
|
||||||
<div className="market">
|
<div className="market">
|
||||||
<Header links={[
|
<Header links={[
|
||||||
|
{
|
||||||
|
link:"/admin/users",
|
||||||
|
name:"Участники"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
link:"/admin/market",
|
link:"/admin/market",
|
||||||
name:"Market place"
|
name:"Market place"
|
||||||
|
@ -38,10 +60,6 @@ export const AdminMarket:React.FC = () =>{
|
||||||
link:"/admin/market/add",
|
link:"/admin/market/add",
|
||||||
name:"Cоздать товар (NFT)"
|
name:"Cоздать товар (NFT)"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
link:"/admin/users",
|
|
||||||
name:"Участники"
|
|
||||||
}
|
|
||||||
]}
|
]}
|
||||||
name={user.name}></Header>
|
name={user.name}></Header>
|
||||||
<div className="marketCard">
|
<div className="marketCard">
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
.adminCard{
|
.adminCard{
|
||||||
width: 240px;
|
width: 240px;
|
||||||
max-height: 400px;
|
max-height: 450px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
background: #FFFFFF;
|
background: #FFFFFF;
|
||||||
|
@ -37,7 +37,7 @@
|
||||||
.adminCardH2{
|
.adminCardH2{
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
line-height: 24px;
|
line-height: 18px;
|
||||||
color: rgba(0, 0, 0, 0.85)
|
color: rgba(0, 0, 0, 0.85)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,15 +14,18 @@ export const AdminMarketCard:React.FC<ProductIE> = (props) =>{
|
||||||
|
|
||||||
return(
|
return(
|
||||||
<div className="adminCard">
|
<div className="adminCard">
|
||||||
<img src={props.image}></img>
|
<img className="adminImg" src={props.image}></img>
|
||||||
<div className="adminCost">
|
<div className="adminCardWrapper">
|
||||||
<img src="/rub.svg"></img>
|
<div className="adminCost">
|
||||||
<div>{props.cost}</div>
|
<img src="/rub.svg"></img>
|
||||||
|
<div>{props.cost}</div>
|
||||||
|
</div>
|
||||||
|
<div className="adminCardH2">{props.name}</div>
|
||||||
|
<div className="adminCardDescr">{props.description.split(" ").slice(0,6).join(" ")}</div>
|
||||||
|
<Button className="btn1" style={{width:"100%"}} onClick={()=>navigate("/admin/market/" + props.id) }>Редактировать</Button>
|
||||||
|
<Button className="btn2" style={{width:"100%"}} onClick={()=>fetchDelProduct(dispatch, props.id)}>Удалить</Button>
|
||||||
</div>
|
</div>
|
||||||
<div>{props.name}</div>
|
|
||||||
<div>{props.description}</div>
|
|
||||||
<Button className="btn1" onClick={()=>navigate("/admin/market/" + props.id) }>Редактировать</Button>
|
|
||||||
<Button className="btn2" onClick={()=>fetchDelProduct(dispatch, props.id)}>Удалить</Button>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
.changeProduct{
|
.changeProduct{
|
||||||
padding-top: 200px;
|
padding-top: 200px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100vh;
|
min-height: 100vh;
|
||||||
background: linear-gradient(85.91deg, #096DD9 0%, #40A9FF 100%);
|
background: linear-gradient(85.91deg, #096DD9 0%, #40A9FF 100%);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
|
@ -11,7 +11,7 @@ import { Header } from "../../components/Header"
|
||||||
|
|
||||||
export const AdminMarketPopUp:React.FC = () =>{
|
export const AdminMarketPopUp:React.FC = () =>{
|
||||||
let {id} = useParams()
|
let {id} = useParams()
|
||||||
let product = useAppSelector((state: RootAdminState)=>getProductByID(state, Number(id)))
|
let product = useAppSelector((state: RootAdminState)=>getProductByID(state, id as string))
|
||||||
const [cost, setCost] = useState(product.cost)
|
const [cost, setCost] = useState(product.cost)
|
||||||
const [name, setName] = useState(product.name)
|
const [name, setName] = useState(product.name)
|
||||||
const [descr, setDescr] = useState(product.description)
|
const [descr, setDescr] = useState(product.description)
|
||||||
|
@ -23,10 +23,9 @@ export const AdminMarketPopUp:React.FC = () =>{
|
||||||
fetchChangeProduct(dispatch, {
|
fetchChangeProduct(dispatch, {
|
||||||
cost: cost,
|
cost: cost,
|
||||||
name: name,
|
name: name,
|
||||||
description: descr,
|
descr: descr,
|
||||||
id:product.id,
|
id: product.id
|
||||||
image: product.image
|
})
|
||||||
} as ProductIE)
|
|
||||||
navigate("/admin/market")
|
navigate("/admin/market")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
34
src/admin/adminUserCard/adminUserCard.css
Normal file
34
src/admin/adminUserCard/adminUserCard.css
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
.userCard{
|
||||||
|
padding-top: 200px;
|
||||||
|
width: 100%;
|
||||||
|
min-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
|
||||||
|
}
|
84
src/admin/adminUserCard/index.tsx
Normal file
84
src/admin/adminUserCard/index.tsx
Normal file
|
@ -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(
|
||||||
|
<div className="userCard">
|
||||||
|
<div className="changeProductCard">
|
||||||
|
<div className="addProductH1">
|
||||||
|
Редактирование пользователя: {user.name}
|
||||||
|
</div>
|
||||||
|
<div className="userCardWrapper">
|
||||||
|
<div className="InpWrapper">
|
||||||
|
<div className="nameText">Telegram</div>
|
||||||
|
<div className="fieldText">{user.telegramID}</div>
|
||||||
|
</div>
|
||||||
|
<div className="InpWrapper">
|
||||||
|
<div className="nameText">Кошелек</div>
|
||||||
|
<div className="fieldText">{user.wallet}</div>
|
||||||
|
</div>
|
||||||
|
<div className="InpWrapper">
|
||||||
|
<div className="nameText">Роль</div>
|
||||||
|
<div className="fieldText">{user.role}</div>
|
||||||
|
</div>
|
||||||
|
<div className="InpWrapper">
|
||||||
|
<div className="nameText">ФИО</div>
|
||||||
|
<Input value={name} onChange={(e)=>setName(e.target.value)} placeholder="ФИО"></Input>
|
||||||
|
</div>
|
||||||
|
<div className="InpWrapper">
|
||||||
|
<div className="nameText">Баланс</div>
|
||||||
|
<Input value={balance} onChange={(e)=>setbalance(Number(e.target.value))} placeholder="баланс"></Input>
|
||||||
|
</div>
|
||||||
|
<div className="InpWrapper">
|
||||||
|
<div className="nameText"> Респект</div>
|
||||||
|
<Input value={respect} onChange={(e)=>setrespect(Number(e.target.value))} placeholder="респект"></Input>
|
||||||
|
</div>
|
||||||
|
<div className="InpWrapper">
|
||||||
|
<div className="nameText">Команда</div>
|
||||||
|
<Input value={command} onChange={(e)=>setcommand(e.target.value)} placeholder="команда"></Input>
|
||||||
|
</div>
|
||||||
|
<div className="InpWrapper">
|
||||||
|
<div className="nameText">Специальность</div>
|
||||||
|
<Input value={jobTittle} onChange={(e)=>setjobTittle(e.target.value)} placeholder="специальность"></Input>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="btnWrapper">
|
||||||
|
<Button className="btn1" onClick={()=>onDelete()}>Удалить пользователя</Button>
|
||||||
|
<Button className="btn2" onClick={()=>onSave()}>Сохранить</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -8,21 +8,8 @@ import { RootAdminState } from "../app/adminStore";
|
||||||
import { Header } from "../components/Header";
|
import { Header } from "../components/Header";
|
||||||
|
|
||||||
export const AdminPage:React.FC = () => {
|
export const AdminPage:React.FC = () => {
|
||||||
const [respProducts, setRespProducts] = useState([-1 as any])
|
|
||||||
let products = useAppSelector((state:RootAdminState)=>getProducts(state))
|
|
||||||
let user = useAppSelector((state:RootAdminState)=>getUser(state))
|
|
||||||
let dispatch = useAppDispatch()
|
|
||||||
if (products.length == 0){
|
|
||||||
adminFetcher.get("marketplace/product/").then(
|
|
||||||
(response)=> setRespProducts(response.data as any)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (respProducts[0] != -1){
|
let user = useAppSelector((state:RootAdminState)=>getUser(state))
|
||||||
respProducts.forEach((product)=>{
|
|
||||||
fetchAddProduct(dispatch, product)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return(
|
return(
|
||||||
|
|
225
src/admin/userTable/index.tsx
Normal file
225
src/admin/userTable/index.tsx
Normal file
|
@ -0,0 +1,225 @@
|
||||||
|
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<ClanColumnsIE> = [
|
||||||
|
{
|
||||||
|
title: "+",
|
||||||
|
key: "action",
|
||||||
|
render: (_, record) =>(
|
||||||
|
<img style={{cursor:"pointer"}} onClick={()=>navigator("/admin/clans/" + record.name)} src="/gear.svg"></img>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Название клана',
|
||||||
|
dataIndex: 'name',
|
||||||
|
key: 'name',
|
||||||
|
defaultSortOrder: 'descend',
|
||||||
|
sorter: (a, b) => collator.compare(a.name, b.name) ,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Telegram клана',
|
||||||
|
dataIndex: 'name',
|
||||||
|
key: 'name',
|
||||||
|
render: (_, record) => (<div>@{record.name.split(" ").join("")}</div>),
|
||||||
|
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) => (<Link to={"/admin/clans/" + record.name} ><div style={{color:"#1890FF"}}>Посмотреть</div></Link>),
|
||||||
|
sorter: (a, b) => collator.compare(a.name, b.name) ,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
const columns: ColumnsType<DataTypeIE> = [
|
||||||
|
{
|
||||||
|
title: "+",
|
||||||
|
key: "action",
|
||||||
|
render: (_, record) =>(
|
||||||
|
<img style={{cursor:"pointer"}} onClick={()=>navigator("/admin/users/" + record.key)} src="/gear.svg"></img>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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=><div>{text + " "} <img src="/respect.svg"></img></div>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Баланс',
|
||||||
|
dataIndex: 'balance',
|
||||||
|
key: 'balance',
|
||||||
|
sorter: (a, b) => a.balance - b.balance,
|
||||||
|
|
||||||
|
render: text=><div>{text + " "} <img src="/rub.svg"></img></div>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Специальность',
|
||||||
|
dataIndex: 'speciality',
|
||||||
|
key: 'speciality',
|
||||||
|
sorter: (a, b) => collator.compare(a.speciality, b.speciality) ,
|
||||||
|
render: text=><Tag color="green" key={text}>
|
||||||
|
{text}
|
||||||
|
</Tag>
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
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,
|
||||||
|
clan: user.clan_name
|
||||||
|
}) 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.clan,
|
||||||
|
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(
|
||||||
|
<div className='userTable'>
|
||||||
|
<Header links={[
|
||||||
|
{
|
||||||
|
link:"/admin/users",
|
||||||
|
name:"Участники"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
link:"/admin/market",
|
||||||
|
name:"Market place"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
link:"/admin/market/add",
|
||||||
|
name:"Cоздать товар (NFT)"
|
||||||
|
},
|
||||||
|
|
||||||
|
]}
|
||||||
|
name={user.name}></Header>
|
||||||
|
<div className='changeProductCard'>
|
||||||
|
<div className="addProductH1">Участники VTB Games</div>
|
||||||
|
<div className='btnWrapper'>
|
||||||
|
<Button onClick={()=>setclans(false)} className={clans? "btn1":"btn2"}>Пользователи</Button>
|
||||||
|
|
||||||
|
<Button onClick={()=>setclans(true)} className={clans? "btn2":"btn1"}>Кланы</Button>
|
||||||
|
|
||||||
|
<Button onClick={()=>navigator("add/")} className='btn1'>Новый пользователь</Button>
|
||||||
|
<Button onClick={()=>adminFetcher.post("season/")} className='btn1'>Начать сезон</Button>
|
||||||
|
</div>
|
||||||
|
{
|
||||||
|
clans? <Table columns={clanColums} dataSource={clanData} />:<Table columns={columns} dataSource={data} />
|
||||||
|
}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
17
src/admin/userTable/userTable.css
Normal file
17
src/admin/userTable/userTable.css
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
.userTable{
|
||||||
|
padding-top: 200px;
|
||||||
|
width: 100%;
|
||||||
|
min-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;
|
||||||
|
}
|
|
@ -1,9 +1,10 @@
|
||||||
import { createAsyncThunk, createSlice, createSelector, PayloadAction } from '@reduxjs/toolkit'
|
import { createAsyncThunk, createSlice, createSelector, PayloadAction } from '@reduxjs/toolkit'
|
||||||
import { stat } from 'fs'
|
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 { AppAdminDispatch, RootAdminState } from '../adminStore'
|
||||||
import { adminFetcher, useAppDispatch } from '../hooks'
|
import { adminFetcher, useAppDispatch } from '../hooks'
|
||||||
import { host, token } from '../consts'
|
import { host, token } from '../consts'
|
||||||
|
import { triggerAsyncId } from 'async_hooks'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -13,14 +14,16 @@ const initState = {
|
||||||
user: {
|
user: {
|
||||||
wallet:"123214",
|
wallet:"123214",
|
||||||
balance: 100,
|
balance: 100,
|
||||||
id:1,
|
id:"1",
|
||||||
role:Roles.admin,
|
role:Roles.admin,
|
||||||
name:"Firesieht"
|
name:"Firesieht"
|
||||||
} as UserIE, //потом достается запросом
|
} as UserIE, //потом достается запросом
|
||||||
market: {
|
market: {
|
||||||
sortType: SortTypes.sortByPriceSmaller,
|
sortType: SortTypes.sortByPriceSmaller,
|
||||||
products: []
|
products: []
|
||||||
} as Market // потом достается запросом
|
} as Market, // потом достается запросом
|
||||||
|
employers: [] as EmployerIE[],
|
||||||
|
clans: [] as ClanIE[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const adminSlice = createSlice(
|
const adminSlice = createSlice(
|
||||||
|
@ -28,13 +31,42 @@ const adminSlice = createSlice(
|
||||||
name: "adminSlice",
|
name: "adminSlice",
|
||||||
initialState: initState,
|
initialState: initState,
|
||||||
reducers:{
|
reducers:{
|
||||||
|
setClans(state, action:PayloadAction<ClanIE[]>){
|
||||||
|
state.clans = action.payload
|
||||||
|
},
|
||||||
|
setEmployers(state, action:PayloadAction<EmployerIE[]>){
|
||||||
|
state.employers = action.payload
|
||||||
|
},
|
||||||
|
addEmployer(state, action:PayloadAction<EmployerIE>){
|
||||||
|
if(state.employers.length == 0){
|
||||||
|
state.employers = state.employers.concat([action.payload])
|
||||||
|
}
|
||||||
|
},
|
||||||
|
delEmployer(state, action:PayloadAction<string>){
|
||||||
|
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<number>){
|
sendCoins(state, action: PayloadAction<number>){
|
||||||
state.user.balance = state.user.balance - action.payload
|
state.user.balance = state.user.balance - action.payload
|
||||||
},
|
},
|
||||||
addProduct(state, action: PayloadAction<ProductIE>){
|
addProduct(state, action: PayloadAction<ProductIE>){
|
||||||
state.market.products = state.market.products.concat([action.payload])
|
if(state.market.products.indexOf(action.payload) == -1){
|
||||||
|
state.market.products = state.market.products.concat([action.payload])
|
||||||
|
}
|
||||||
},
|
},
|
||||||
delProduct(state, action:PayloadAction<number>){
|
addProducts(state, action: PayloadAction<ProductIE[]>){
|
||||||
|
if(state.market.products.length == 0){
|
||||||
|
state.market.products = state.market.products.concat(action.payload)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
delProduct(state, action:PayloadAction<string>){
|
||||||
let products = state.market.products
|
let products = state.market.products
|
||||||
let ind = 0
|
let ind = 0
|
||||||
products.forEach((product, index)=>{
|
products.forEach((product, index)=>{
|
||||||
|
@ -45,6 +77,15 @@ const adminSlice = createSlice(
|
||||||
products.splice(ind, 1)
|
products.splice(ind, 1)
|
||||||
state.market.products = products
|
state.market.products = products
|
||||||
},
|
},
|
||||||
|
changeEmployer(state, action:PayloadAction<EmployerIE>){
|
||||||
|
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<ProductIE>){
|
changeProduct(state, action:PayloadAction<ProductIE>){
|
||||||
let products = state.market.products
|
let products = state.market.products
|
||||||
products.forEach((product,index)=>{
|
products.forEach((product,index)=>{
|
||||||
|
@ -64,29 +105,99 @@ export async function fetchSendCoins(dispatch:AppAdminDispatch, params:{userID:n
|
||||||
dispatch(sendCoins(params.amount))
|
dispatch(sendCoins(params.amount))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchAddProduct(dispatch:AppAdminDispatch, params:{image: FormData, descr: string, name:string, cost:number}) {
|
export async function fetchAddProduct(dispatch:AppAdminDispatch, params:{image: File, descr: string, name:string, cost:number}) {
|
||||||
//тут идет фетч
|
//тут идет фетч
|
||||||
let data = {
|
const formData = new FormData()
|
||||||
image: "",
|
formData.append("name",params.name)
|
||||||
description: params.descr,
|
formData.append("description",params.descr)
|
||||||
name: params.name,
|
formData.append("image", params.image)
|
||||||
cost: params.cost,
|
formData.append("price",params.cost.toString())
|
||||||
id: params.cost
|
adminFetcher.post("marketplace/product/", formData).then((response)=>{
|
||||||
} as ProductIE //vмоковая даата
|
dispatch(addProduct({
|
||||||
adminFetcher.post("marketplace/product/", {
|
name: response.data.name,
|
||||||
|
description:response.data.description,
|
||||||
|
image: response.data.image_cropped,
|
||||||
|
cost:Number(response.data.price),
|
||||||
|
id:response.data.slug
|
||||||
|
} as ProductIE))
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
dispatch(addProduct(data))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchDelProduct(dispatch:AppAdminDispatch, id:number) {
|
export async function fetchDelProduct(dispatch:AppAdminDispatch, id:string) {
|
||||||
//тут идет фетч
|
//тут идет фетч
|
||||||
dispatch(delProduct(id))
|
adminFetcher.delete("marketplace/product/"+id).then(()=>dispatch(delProduct(id)))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchChangeProduct(dispatch:AppAdminDispatch, params:ProductIE) {
|
export async function fetchChangeProduct(dispatch:AppAdminDispatch, params:{id:string, descr: string, name:string, cost:number}) {
|
||||||
dispatch(changeProduct(params))
|
const formData = new FormData()
|
||||||
|
formData.append("name",params.name)
|
||||||
|
formData.append("description",params.descr)
|
||||||
|
formData.append("price",params.cost.toString())
|
||||||
|
adminFetcher.patch("marketplace/product/" + params.id, formData).then((response)=>{
|
||||||
|
dispatch(delProduct(params.id))
|
||||||
|
dispatch(addProduct({
|
||||||
|
name: response.data.name,
|
||||||
|
description:response.data.description,
|
||||||
|
image: response.data.image_cropped,
|
||||||
|
cost:Number(response.data.price),
|
||||||
|
id:response.data.slug
|
||||||
|
} as ProductIE))
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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(
|
export const getProducts = createSelector(
|
||||||
|
@ -95,7 +206,12 @@ export const getProducts = createSelector(
|
||||||
)
|
)
|
||||||
|
|
||||||
export const getProductByID = createSelector(
|
export const getProductByID = createSelector(
|
||||||
(state:RootAdminState, id:number) => state.adminSlice.market.products.filter((product)=>product.id == id)[0],
|
(state:RootAdminState, id:string) => state.adminSlice.market.products.filter((product)=>product.id == id)[0],
|
||||||
|
(field)=>field
|
||||||
|
)
|
||||||
|
|
||||||
|
export const getEmployerByTg = createSelector(
|
||||||
|
(state:RootAdminState, tg:string) => state.adminSlice.employers.filter((product)=>product.telegramID == tg)[0],
|
||||||
(field)=>field
|
(field)=>field
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -107,12 +223,30 @@ export const getUser = createSelector(
|
||||||
(state:RootAdminState) => state.adminSlice.user,
|
(state:RootAdminState) => state.adminSlice.user,
|
||||||
(field)=>field
|
(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 {
|
export const {
|
||||||
sendCoins,
|
sendCoins,
|
||||||
addProduct,
|
addProduct,
|
||||||
delProduct,
|
delProduct,
|
||||||
changeProduct,
|
changeProduct,
|
||||||
|
addProducts,
|
||||||
|
setEmployers,
|
||||||
|
addEmployer,
|
||||||
|
delEmployer,
|
||||||
|
changeEmployer,
|
||||||
|
setClans
|
||||||
} = adminSlice.actions
|
} = adminSlice.actions
|
||||||
|
|
||||||
export default adminSlice.reducer
|
export default adminSlice.reducer
|
|
@ -9,7 +9,7 @@ export const useAppSelector: TypedUseSelectorHook<RootAdminState> = useSelector;
|
||||||
export const adminFetcher = axios.create(
|
export const adminFetcher = axios.create(
|
||||||
{
|
{
|
||||||
baseURL: host,
|
baseURL: host,
|
||||||
timeout: 1000,
|
timeout: 5000,
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: 'Bearer ' + token
|
Authorization: 'Bearer ' + token
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,6 @@ export enum Specialities{
|
||||||
|
|
||||||
export interface UserIE{
|
export interface UserIE{
|
||||||
wallet:string,
|
wallet:string,
|
||||||
id:number
|
|
||||||
role:Roles
|
role:Roles
|
||||||
balance: number,
|
balance: number,
|
||||||
name:string,
|
name:string,
|
||||||
|
@ -23,8 +22,11 @@ export interface UserIE{
|
||||||
|
|
||||||
|
|
||||||
export interface EmployerIE extends UserIE{
|
export interface EmployerIE extends UserIE{
|
||||||
jobTittle:Specialities,
|
jobTittle:string,
|
||||||
respect: number,
|
respect: number,
|
||||||
|
telegramID: string,
|
||||||
|
command: string,
|
||||||
|
clan:string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -42,7 +44,7 @@ export interface ProductIE{
|
||||||
description:string,
|
description:string,
|
||||||
image: string,
|
image: string,
|
||||||
cost: number,
|
cost: number,
|
||||||
id: number,
|
id: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum SortTypes{
|
export enum SortTypes{
|
||||||
|
@ -55,3 +57,8 @@ export interface Market{
|
||||||
sortType: SortTypes,
|
sortType: SortTypes,
|
||||||
products: ProductIE[],
|
products: ProductIE[],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ClanIE{
|
||||||
|
name:string,
|
||||||
|
users:UserIE[]
|
||||||
|
}
|
|
@ -15,6 +15,7 @@
|
||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
color: #FFFFFF;
|
color: #FFFFFF;
|
||||||
|
z-index: 1000;
|
||||||
}
|
}
|
||||||
.links{
|
.links{
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
|
|
|
@ -12,29 +12,25 @@ export const FileUploader:React.FC<FileUploaderIE> = (data) =>{
|
||||||
|
|
||||||
const props = {
|
const props = {
|
||||||
name: 'file',
|
name: 'file',
|
||||||
|
action: "",
|
||||||
headers: {
|
headers: {
|
||||||
"content-type": 'multipart/form-data; boundary=----WebKitFormBoundaryqTqJIxvkWFYqvP5s'
|
"content-type": 'multipart/form-data; boundary=----WebKitFormBoundaryqTqJIxvkWFYqvP5s'
|
||||||
},
|
},
|
||||||
onChange(info:any) {
|
beforeUpload: (file:File) => {
|
||||||
if (info.file.status !== 'uploading') {
|
const isPNG = file.type === 'image/png';
|
||||||
}
|
if (!isPNG) {
|
||||||
|
message.error(`${file.name} is not a png file`);
|
||||||
if (info.file.status === 'done') {
|
|
||||||
data.onResponse(info.file.response)
|
|
||||||
message.success(`${info.file.name} file uploaded successfully`);
|
|
||||||
} else if (info.file.status === 'error') {
|
|
||||||
message.error(`${info.file.name} file upload failed.`);
|
|
||||||
}
|
}
|
||||||
|
data.onResponse(file)
|
||||||
|
return isPNG || Upload.LIST_IGNORE;
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Upload {...props} customRequest={(file) => {
|
<Upload {...props}>
|
||||||
console.log(file);
|
<Button icon={<UploadOutlined></UploadOutlined>}>Загрузите картинку</Button>
|
||||||
}} onDownload={(file) => {
|
|
||||||
console.log(file)
|
|
||||||
}} multiple>
|
|
||||||
<Button icon={<UploadOutlined></UploadOutlined>}>Загрузите файлы для проверки</Button>
|
|
||||||
</Upload>
|
</Upload>
|
||||||
);
|
);
|
||||||
}
|
}
|
|
@ -17,9 +17,14 @@ import {
|
||||||
Link,
|
Link,
|
||||||
Routes,
|
Routes,
|
||||||
} from "react-router-dom";
|
} from "react-router-dom";
|
||||||
|
import { UserTable } from './admin/userTable';
|
||||||
|
import { AdminUserCard } from './admin/adminUserCard';
|
||||||
|
import { AdminClans } from './admin/adminClans';
|
||||||
|
import { AddUser } from './admin/addUser';
|
||||||
import { UserLk } from './user/lk';
|
import { UserLk } from './user/lk';
|
||||||
import { Clan } from './user/clan';
|
import { Clan } from './user/clan';
|
||||||
|
|
||||||
|
|
||||||
const container = document.getElementById('root')!;
|
const container = document.getElementById('root')!;
|
||||||
const root = createRoot(container);
|
const root = createRoot(container);
|
||||||
const router = createBrowserRouter(
|
const router = createBrowserRouter(
|
||||||
|
@ -29,6 +34,11 @@ const router = createBrowserRouter(
|
||||||
<Route path='admin/market' element={<Provider store={adminStore}><AdminMarket></AdminMarket></Provider>}></Route>
|
<Route path='admin/market' element={<Provider store={adminStore}><AdminMarket></AdminMarket></Provider>}></Route>
|
||||||
<Route path='admin/market/:id' element={<Provider store={adminStore}><AdminMarketPopUp></AdminMarketPopUp></Provider>}></Route>
|
<Route path='admin/market/:id' element={<Provider store={adminStore}><AdminMarketPopUp></AdminMarketPopUp></Provider>}></Route>
|
||||||
<Route path='admin/market/add' element={<Provider store={adminStore}><AddAdminMarketProduct></AddAdminMarketProduct></Provider>}></Route>
|
<Route path='admin/market/add' element={<Provider store={adminStore}><AddAdminMarketProduct></AddAdminMarketProduct></Provider>}></Route>
|
||||||
|
<Route path='admin/users' element={<Provider store={adminStore}><UserTable></UserTable></Provider>}></Route>
|
||||||
|
<Route path='admin/users/:tg' element={<Provider store={adminStore}><AdminUserCard></AdminUserCard></Provider>}></Route>
|
||||||
|
<Route path='admin/clans/:id' element={<Provider store={adminStore}><AdminClans></AdminClans></Provider>}></Route>
|
||||||
|
<Route path='admin/users/add' element={<Provider store={adminStore}><AddUser></AddUser></Provider>}></Route>
|
||||||
|
|
||||||
<Route path="hr"></Route>
|
<Route path="hr"></Route>
|
||||||
<Route path="user/lk/" element={<UserLk />} />
|
<Route path="user/lk/" element={<UserLk />} />
|
||||||
<Route path='user/clan' element={<Clan />} />
|
<Route path='user/clan' element={<Clan />} />
|
||||||
|
|
Loading…
Reference in New Issue
Block a user