mirror of
https://github.com/more-tech4-magnum-opus/frontend.git
synced 2024-11-22 00:26:35 +03:00
add HR
This commit is contained in:
parent
f9b70590c0
commit
6b84a11c2a
BIN
public/mock.png
Normal file
BIN
public/mock.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 48 KiB |
44
src/Transaction.tsx
Normal file
44
src/Transaction.tsx
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
import { Button, Input } from "antd";
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import { adminFetcher, hrFetcher } from "./app/hooks";
|
||||||
|
|
||||||
|
export const Transaction:React.FC = () =>{
|
||||||
|
const [user, setUser] = useState("")
|
||||||
|
const [amount, setAmount] = useState(0)
|
||||||
|
|
||||||
|
const reset = () =>{
|
||||||
|
setUser("")
|
||||||
|
setAmount(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
const sendCoins = () =>{
|
||||||
|
let formData = new FormData()
|
||||||
|
formData.append("amount", amount.toString())
|
||||||
|
formData.append("username", user)
|
||||||
|
|
||||||
|
hrFetcher.post("blockchain/transact/", formData).then(e=>{
|
||||||
|
alert("Коины переведены!")
|
||||||
|
reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return(
|
||||||
|
<div className="addProduct">
|
||||||
|
<div className="addProductCard">
|
||||||
|
<div className="addProductH1">Перевод/Выдача DigitalRubles</div>
|
||||||
|
<div className="fieldsWrapper" >
|
||||||
|
<div className="InpWrapper">
|
||||||
|
<div>Telegram username</div>
|
||||||
|
<Input value={user} onChange={(e)=>setUser(e.target.value)} placeholder="firesieht"></Input>
|
||||||
|
</div>
|
||||||
|
<div className="InpWrapper">
|
||||||
|
<div>Сумма в коинах</div>
|
||||||
|
<Input value={amount} type="number" onChange={(e)=>setAmount(Number(e.target.value))} placeholder="100"></Input>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Button onClick={()=>sendCoins()} className="btn1">Отправить</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -6,6 +6,7 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
gap:30px
|
||||||
}
|
}
|
||||||
|
|
||||||
.addProductCard{
|
.addProductCard{
|
||||||
|
@ -109,4 +110,29 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
gap:25px
|
gap:25px
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.userPreview{
|
||||||
|
background: #262626;
|
||||||
|
border-radius: 16px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding: 32px 64px;
|
||||||
|
gap: 36px;
|
||||||
|
width: 75%;
|
||||||
|
font-family: 'Roboto', sans-serif;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 22px;
|
||||||
|
/* identical to box height, or 157% */
|
||||||
|
|
||||||
|
text-align: right;
|
||||||
|
|
||||||
|
/* Character/Title .85 */
|
||||||
|
|
||||||
|
color:#fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,6 +47,10 @@ export const AddAdminMarketProduct:React.FC = () =>{
|
||||||
link:"/admin/market/add",
|
link:"/admin/market/add",
|
||||||
name:"Cоздать товар (NFT)"
|
name:"Cоздать товар (NFT)"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
link:"/admin/transaction",
|
||||||
|
name:"История транзакций"
|
||||||
|
},
|
||||||
]}
|
]}
|
||||||
name={user.name}></Header>
|
name={user.name}></Header>
|
||||||
<div className="addProductCard">
|
<div className="addProductCard">
|
||||||
|
|
|
@ -60,6 +60,10 @@ export const AdminMarket:React.FC = () =>{
|
||||||
link:"/admin/market/add",
|
link:"/admin/market/add",
|
||||||
name:"Cоздать товар (NFT)"
|
name:"Cоздать товар (NFT)"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
link:"/admin/transaction",
|
||||||
|
name:"История транзакций"
|
||||||
|
},
|
||||||
]}
|
]}
|
||||||
name={user.name}></Header>
|
name={user.name}></Header>
|
||||||
<div className="marketCard">
|
<div className="marketCard">
|
||||||
|
|
|
@ -43,7 +43,11 @@ export const AdminMarketPopUp:React.FC = () =>{
|
||||||
{
|
{
|
||||||
link:"/admin/users",
|
link:"/admin/users",
|
||||||
name:"Участники"
|
name:"Участники"
|
||||||
}
|
},
|
||||||
|
{
|
||||||
|
link:"/admin/transaction",
|
||||||
|
name:"История транзакций"
|
||||||
|
},
|
||||||
]}
|
]}
|
||||||
name={user.name}></Header>
|
name={user.name}></Header>
|
||||||
<div className="changeProductCard">
|
<div className="changeProductCard">
|
||||||
|
|
7
src/admin/adminTransactions/index.tsx
Normal file
7
src/admin/adminTransactions/index.tsx
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
export const AdminTransactions:React.FC = () =>{
|
||||||
|
return(<div>
|
||||||
|
|
||||||
|
</div>);
|
||||||
|
}
|
|
@ -140,7 +140,7 @@ export const UserTable:React.FC = () =>{
|
||||||
command: user.command,
|
command: user.command,
|
||||||
role: user.type,
|
role: user.type,
|
||||||
respect: user.respect,
|
respect: user.respect,
|
||||||
balance: user.salary,
|
balance: user.money,
|
||||||
jobTittle: user.department,
|
jobTittle: user.department,
|
||||||
clan: user.clan_name
|
clan: user.clan_name
|
||||||
}) as EmployerIE ))))
|
}) as EmployerIE ))))
|
||||||
|
@ -156,7 +156,7 @@ export const UserTable:React.FC = () =>{
|
||||||
command: user.command,
|
command: user.command,
|
||||||
role: user.type,
|
role: user.type,
|
||||||
respect: user.respect,
|
respect: user.respect,
|
||||||
balance: user.salary,
|
balance: user.money,
|
||||||
jobTittle: user.department,
|
jobTittle: user.department,
|
||||||
}) ))
|
}) ))
|
||||||
} as ClanIE))
|
} as ClanIE))
|
||||||
|
@ -202,7 +202,11 @@ export const UserTable:React.FC = () =>{
|
||||||
link:"/admin/market/add",
|
link:"/admin/market/add",
|
||||||
name:"Cоздать товар (NFT)"
|
name:"Cоздать товар (NFT)"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
link:"/admin/transaction",
|
||||||
|
name:"История транзакций"
|
||||||
|
},
|
||||||
|
|
||||||
]}
|
]}
|
||||||
name={user.name}></Header>
|
name={user.name}></Header>
|
||||||
<div className='changeProductCard'>
|
<div className='changeProductCard'>
|
||||||
|
|
|
@ -3,8 +3,6 @@ import { stat } from 'fs'
|
||||||
import { Market, Roles, SortTypes, UserIE, ProductIE, EmployerIE, ClanIE} 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 { triggerAsyncId } from 'async_hooks'
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -15,7 +13,7 @@ const initState = {
|
||||||
wallet:"123214",
|
wallet:"123214",
|
||||||
balance: 100,
|
balance: 100,
|
||||||
id:"1",
|
id:"1",
|
||||||
role:Roles.admin,
|
role:"ADMIN",
|
||||||
name:"Firesieht"
|
name:"Firesieht"
|
||||||
} as UserIE, //потом достается запросом
|
} as UserIE, //потом достается запросом
|
||||||
market: {
|
market: {
|
||||||
|
|
|
@ -1,2 +1,4 @@
|
||||||
export const host = "https://dev.akarpov.ru/api/"
|
export const host = "https://dev.akarpov.ru/api/"
|
||||||
export const token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjY1ODI0ODQxLCJpYXQiOjE2NjUyMjQ4NDEsImp0aSI6ImIyMjgzZjg0MmQ0NjQ4ZDU5MGY3N2Y1NzU2NDU3YjUyIiwidXNlcl9pZCI6NH0.-jAQjFLV9WCZEbthdpO-JDbRaL3N3eTCNr9Vo-PAUEk"
|
export const adminToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjY1ODcyMzQwLCJpYXQiOjE2NjUyNzIzNDAsImp0aSI6IjZkNGUxOTQ0ZTE3ZjQ4OTk4MjllYmU4Mzk2NDMwZTVlIiwidXNlcl9pZCI6Nn0.ivyEB13zMGS4liCn2N2fHUi0q9bbI8iaRWBXdiO30d4"
|
||||||
|
export const hrToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjY1ODcyMzYxLCJpYXQiOjE2NjUyNzIzNjEsImp0aSI6Ijc3ZDk1ZTI2NWJkNzQ4NGRhNWY1MjYyNDE4YWQ2NzAwIiwidXNlcl9pZCI6NX0.luP2N8g_Yp3TNEwIBMYhSTSdSpUj3-QzT4PGfoYnM0k"
|
||||||
|
export const workerToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjY1ODcyMzkxLCJpYXQiOjE2NjUyNzIzOTEsImp0aSI6IjBlYzQxOTUzMTA2NDRlOWFiNzE2NTgyZWJmZDY0MTBmIiwidXNlcl9pZCI6NH0.GULKUG-2iuEAVSPXdREC0wPbQbSsSaTLqQ3ZJblwtAk"
|
|
@ -1,17 +1,38 @@
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
|
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
|
||||||
import type { RootAdminState, AppAdminDispatch } from './adminStore';
|
import type { RootAdminState, AppAdminDispatch } from './adminStore';
|
||||||
import { host, token } from './consts';
|
import { host, adminToken, hrToken, workerToken } from './consts';
|
||||||
|
import { AppHRDispatch, RootHRState } from './hrStore';
|
||||||
|
|
||||||
// Use throughout your app instead of plain `useDispatch` and `useSelector`
|
// Use throughout your app instead of plain `useDispatch` and `useSelector`
|
||||||
export const useAppDispatch = () => useDispatch<AppAdminDispatch>();
|
export const useAppDispatch = () => useDispatch<AppAdminDispatch>();
|
||||||
|
export const useHRDispatch = () => useDispatch<AppHRDispatch>();
|
||||||
export const useAppSelector: TypedUseSelectorHook<RootAdminState> = useSelector;
|
export const useAppSelector: TypedUseSelectorHook<RootAdminState> = useSelector;
|
||||||
|
export const useHRSelector: TypedUseSelectorHook<RootHRState> = useSelector;
|
||||||
export const adminFetcher = axios.create(
|
export const adminFetcher = axios.create(
|
||||||
{
|
{
|
||||||
baseURL: host,
|
baseURL: host,
|
||||||
timeout: 5000,
|
timeout: 5000,
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: 'Bearer ' + token
|
Authorization: 'Bearer ' + adminToken
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
export const hrFetcher = axios.create(
|
||||||
|
{
|
||||||
|
baseURL: host,
|
||||||
|
timeout: 5000,
|
||||||
|
headers: {
|
||||||
|
Authorization: 'Bearer ' + hrToken
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
export const workerFetcher = axios.create(
|
||||||
|
{
|
||||||
|
baseURL: host,
|
||||||
|
timeout: 5000,
|
||||||
|
headers: {
|
||||||
|
Authorization: 'Bearer ' + workerToken
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
158
src/app/hr/hrSlice.ts
Normal file
158
src/app/hr/hrSlice.ts
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
import { createAsyncThunk, createSlice, createSelector, PayloadAction } from '@reduxjs/toolkit'
|
||||||
|
import { EventAttendence, EventIE, HRIE} from '../interfaces'
|
||||||
|
import { AppHRDispatch, RootHRState } from '../hrStore'
|
||||||
|
import { hrFetcher, useAppDispatch } from '../hooks'
|
||||||
|
|
||||||
|
|
||||||
|
const initState = {
|
||||||
|
me: {} as HRIE,
|
||||||
|
events: [] as EventIE[],
|
||||||
|
currentEvent: "",
|
||||||
|
attendences: [] as EventAttendence[],
|
||||||
|
}
|
||||||
|
|
||||||
|
const hrSlice = createSlice(
|
||||||
|
{
|
||||||
|
name: "hrSlice",
|
||||||
|
initialState: initState,
|
||||||
|
reducers:{
|
||||||
|
setMe(state, action:PayloadAction<HRIE>){
|
||||||
|
state.me = action.payload
|
||||||
|
},
|
||||||
|
setEvents(state, action:PayloadAction<EventIE[]>){
|
||||||
|
state.events = action.payload
|
||||||
|
},
|
||||||
|
setAttendences(state, action:PayloadAction<EventAttendence[]>){
|
||||||
|
state.attendences = action.payload
|
||||||
|
},
|
||||||
|
setCurrentEvent(state, aaction:PayloadAction<string>){
|
||||||
|
state.currentEvent = aaction.payload
|
||||||
|
},
|
||||||
|
addEvent(state, action:PayloadAction<EventIE>){
|
||||||
|
state.events = state.events.concat([action.payload])
|
||||||
|
},
|
||||||
|
delEvent(state, action:PayloadAction<string>){
|
||||||
|
let events = state.events
|
||||||
|
let ind = 0
|
||||||
|
|
||||||
|
events.forEach((event:EventIE, index:number) => {
|
||||||
|
if(event.slug == action.payload){
|
||||||
|
ind = index
|
||||||
|
}
|
||||||
|
});
|
||||||
|
events.splice(ind, 1)
|
||||||
|
state.events = events
|
||||||
|
},
|
||||||
|
updateEvent(state, action:PayloadAction<EventIE>){
|
||||||
|
let events = state.events
|
||||||
|
events.forEach((event:EventIE, index:number) => {
|
||||||
|
if(event.slug == action.payload.slug){
|
||||||
|
events[index] = action.payload
|
||||||
|
}
|
||||||
|
});
|
||||||
|
state.events = events
|
||||||
|
},
|
||||||
|
submitAttendance(state, action:PayloadAction<string>){
|
||||||
|
let attendences = state.attendences
|
||||||
|
|
||||||
|
attendences.forEach((attendance:EventAttendence, index:number) => {
|
||||||
|
if(attendance.worker_username == action.payload){
|
||||||
|
attendences[index].attended = true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
state.attendences = attendences
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export async function fetchAddEvent(dispatch:AppHRDispatch, params:{name: string, about: string, starts:string, image:File}) {
|
||||||
|
//тут идет фетч
|
||||||
|
const formData = new FormData()
|
||||||
|
|
||||||
|
formData.append("name",params.name)
|
||||||
|
formData.append("about",params.about)
|
||||||
|
formData.append("image", params.image)
|
||||||
|
formData.append("starts",params.starts)
|
||||||
|
|
||||||
|
hrFetcher.post("events/", formData).then((response)=>{
|
||||||
|
dispatch(
|
||||||
|
addEvent(
|
||||||
|
{
|
||||||
|
name: response.data.name,
|
||||||
|
about: response.data.about,
|
||||||
|
slug: response.data.slug,
|
||||||
|
creator: {
|
||||||
|
wallet: "",
|
||||||
|
role: response.data.creator.type,
|
||||||
|
telegram: response.data.creator.telegram,
|
||||||
|
command: response.data.creator.command,
|
||||||
|
respect: response.data.creator.respect,
|
||||||
|
balance: response.data.creator.money,
|
||||||
|
name: response.data.creator.name
|
||||||
|
} as HRIE,
|
||||||
|
starts: response.data.starts,
|
||||||
|
image: response.data.image,
|
||||||
|
planning: Number(response.data.planning),
|
||||||
|
attended: Number(response.data.attended)
|
||||||
|
} as EventIE
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchDelEvent(dispatch:AppHRDispatch, params:string) {
|
||||||
|
|
||||||
|
hrFetcher.delete("events/" + params).then(()=>{
|
||||||
|
dispatch(delEvent(params))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchSubmitAttendance(dispatch:AppHRDispatch, user:string, currentEvent:string){
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append("username", user)
|
||||||
|
|
||||||
|
hrFetcher.post("events/attendance/" + currentEvent + "/submit/", formData).then(response=>{
|
||||||
|
dispatch(submitAttendance(user))
|
||||||
|
})
|
||||||
|
|
||||||
|
const salaryData = new FormData()
|
||||||
|
salaryData.append("username", user)
|
||||||
|
salaryData.append("amount", "1")
|
||||||
|
hrFetcher.post("blockchain/transact/", salaryData)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const getCurrentEvent = createSelector(
|
||||||
|
(state:RootHRState) => state.hrSlice.events.filter(el=> el.slug == state.hrSlice.currentEvent)[0],
|
||||||
|
(field)=>field
|
||||||
|
)
|
||||||
|
export const getEvents = createSelector(
|
||||||
|
(state:RootHRState) => state.hrSlice.events,
|
||||||
|
(field)=>field
|
||||||
|
)
|
||||||
|
export const getMe = createSelector(
|
||||||
|
(state:RootHRState) => state.hrSlice.me,
|
||||||
|
(field)=>field
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export const {
|
||||||
|
setEvents,
|
||||||
|
setAttendences,
|
||||||
|
setCurrentEvent,
|
||||||
|
addEvent,
|
||||||
|
delEvent,
|
||||||
|
updateEvent,
|
||||||
|
submitAttendance,
|
||||||
|
setMe
|
||||||
|
} = hrSlice.actions
|
||||||
|
|
||||||
|
export default hrSlice.reducer
|
16
src/app/hrStore.ts
Normal file
16
src/app/hrStore.ts
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import { configureStore, ThunkAction, Action } from '@reduxjs/toolkit';
|
||||||
|
import hrSlice from './hr/hrSlice';
|
||||||
|
export const hrStore = configureStore({
|
||||||
|
reducer: {
|
||||||
|
hrSlice: hrSlice,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export type AppHRDispatch = typeof hrStore.dispatch;
|
||||||
|
export type RootHRState = ReturnType<typeof hrStore.getState>;
|
||||||
|
export type AppHRThunk<ReturnType = void> = ThunkAction<
|
||||||
|
ReturnType,
|
||||||
|
RootHRState,
|
||||||
|
unknown,
|
||||||
|
Action<string>
|
||||||
|
>;
|
|
@ -14,7 +14,7 @@ export enum Specialities{
|
||||||
|
|
||||||
export interface UserIE{
|
export interface UserIE{
|
||||||
wallet:string,
|
wallet:string,
|
||||||
role:Roles
|
role:string
|
||||||
balance: number,
|
balance: number,
|
||||||
name:string,
|
name:string,
|
||||||
}
|
}
|
||||||
|
@ -26,17 +26,32 @@ export interface EmployerIE extends UserIE{
|
||||||
respect: number,
|
respect: number,
|
||||||
telegramID: string,
|
telegramID: string,
|
||||||
command: string,
|
command: string,
|
||||||
clan:string
|
clan:string,
|
||||||
|
money:number,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface HRIE extends UserIE{
|
||||||
|
respect:number,
|
||||||
|
command:number,
|
||||||
|
telegram: string,
|
||||||
|
}
|
||||||
|
|
||||||
export interface EventIE{
|
export interface EventIE{
|
||||||
name:string,
|
name:string,
|
||||||
description: string,
|
about: string,
|
||||||
date: Date,
|
starts: string,
|
||||||
time:Date,
|
slug:string,
|
||||||
org: number;
|
creator:HRIE,
|
||||||
visitors: number[]
|
image:string,
|
||||||
|
planning:number,
|
||||||
|
attended:number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EventAttendence{
|
||||||
|
id:number,
|
||||||
|
event_slug:string,
|
||||||
|
worker_username:string,
|
||||||
|
attended:boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ProductIE{
|
export interface ProductIE{
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
import "./Header.css"
|
import "./Header.css"
|
||||||
import React from "react"
|
import React from "react"
|
||||||
import { Link, useLocation, useParams } from "react-router-dom"
|
import { Link, useLocation, useParams } from "react-router-dom"
|
||||||
import axios, { AxiosHeaders } from 'axios';
|
|
||||||
import { host, token } from "../../app/consts";
|
|
||||||
|
|
||||||
interface HeaderIE{
|
interface HeaderIE{
|
||||||
links: {
|
links: {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Tag } from 'antd'
|
import { Tag } from 'antd'
|
||||||
import react from 'react'
|
import react from 'react'
|
||||||
|
import "./style.css"
|
||||||
|
|
||||||
interface IUserPreview{
|
interface IUserPreview{
|
||||||
level: number;
|
level: number;
|
||||||
|
|
5
src/hr/EventCard/eventCard.css
Normal file
5
src/hr/EventCard/eventCard.css
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
.eventImg{
|
||||||
|
height: 50%;
|
||||||
|
max-height: 150px;
|
||||||
|
border-radius: 16px 16px 0px 0px;
|
||||||
|
}
|
37
src/hr/EventCard/index.tsx
Normal file
37
src/hr/EventCard/index.tsx
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import { Button } from "antd"
|
||||||
|
import React from "react"
|
||||||
|
import { useNavigate } from "react-router-dom"
|
||||||
|
import { useHRDispatch } from "../../app/hooks"
|
||||||
|
import { fetchDelEvent } from "../../app/hr/hrSlice"
|
||||||
|
import "../../components/prevMarketCard/prevMarketCard.css"
|
||||||
|
import "./eventCard.css"
|
||||||
|
|
||||||
|
interface EventCardIE{
|
||||||
|
image:string,
|
||||||
|
cost: number,
|
||||||
|
descr: string,
|
||||||
|
name:string,
|
||||||
|
members: number
|
||||||
|
slug?:string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const EventCard:React.FC<EventCardIE> = (props) =>{
|
||||||
|
let dispatch = useHRDispatch()
|
||||||
|
let navigator = useNavigate()
|
||||||
|
|
||||||
|
return(
|
||||||
|
<div className="prevCard">
|
||||||
|
<img className="eventImg" src={props.image}></img>
|
||||||
|
<div className="prevCardWrapper">
|
||||||
|
<div className="prevCost">
|
||||||
|
<img src="/rub.svg"></img>
|
||||||
|
<div>{props.cost}</div>
|
||||||
|
</div>
|
||||||
|
<div className="prevCardH2">{props.name}</div>
|
||||||
|
<div className="prevCardDescr">{props.descr.split(" ").slice(0,6).join(" ")}</div>
|
||||||
|
<Button style={{width:"100%"}} onClick={()=>navigator("/hr/events/reg/" + props.slug)} className="btn2">Зарегистрировать</Button>
|
||||||
|
<Button style={{width:"100%"}} onClick={()=>fetchDelEvent(dispatch, props.slug as string)} className="btn1">Удалить ивент</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
0
src/hr/addEvent/addEvent.css
Normal file
0
src/hr/addEvent/addEvent.css
Normal file
91
src/hr/addEvent/index.tsx
Normal file
91
src/hr/addEvent/index.tsx
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
import "./addEvent.css"
|
||||||
|
import { Button, DatePicker, Input } from "antd";
|
||||||
|
import React, { useState } from "react"
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import { useHRDispatch } from "../../app/hooks";
|
||||||
|
import { fetchAddEvent } from "../../app/hr/hrSlice";
|
||||||
|
import { FileUploader } from "../../components/fileUploader";
|
||||||
|
import { Header } from "../../components/Header";
|
||||||
|
import { EventCard } from "../EventCard";
|
||||||
|
|
||||||
|
|
||||||
|
export const AddEvent: React.FC = () =>{
|
||||||
|
|
||||||
|
const [name, setName] = useState("")
|
||||||
|
const [about, setAbout] = useState("")
|
||||||
|
const [starts, setStarts] = useState("")
|
||||||
|
const [image, setImage] = useState()
|
||||||
|
|
||||||
|
let navigate = useNavigate()
|
||||||
|
let dispatch = useHRDispatch()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const create = () =>{
|
||||||
|
fetchAddEvent(dispatch, {
|
||||||
|
name:name,
|
||||||
|
about:about,
|
||||||
|
starts:starts,
|
||||||
|
image:image as any
|
||||||
|
})
|
||||||
|
|
||||||
|
alert("Ивент успешно создан!")
|
||||||
|
|
||||||
|
navigate("/hr")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return(
|
||||||
|
<div className="addProduct">
|
||||||
|
<Header links={[
|
||||||
|
{
|
||||||
|
link:"/hr",
|
||||||
|
name:"События"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
link:"/hr/addEvent",
|
||||||
|
name:"Создать событие"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
link:"/hr/transaction",
|
||||||
|
name:"История транзакций"
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
name={"HR"}></Header>
|
||||||
|
<div className="addProductCard">
|
||||||
|
<div className="addProductH1">Cоздать новое событие</div>
|
||||||
|
<div className="addBodyWrapper">
|
||||||
|
<EventCard
|
||||||
|
image={"/mock.png"}
|
||||||
|
cost={5}
|
||||||
|
descr={about}
|
||||||
|
name={name}
|
||||||
|
members={2}
|
||||||
|
></EventCard>
|
||||||
|
<div className="fieldsWrapper">
|
||||||
|
<div className="InpWrapper">
|
||||||
|
<div>Название</div>
|
||||||
|
<Input value={name} onChange={(e)=>setName(e.target.value)}></Input>
|
||||||
|
</div>
|
||||||
|
<div className="InpWrapper">
|
||||||
|
<div>Описание</div>
|
||||||
|
<Input value={about} onChange={(e)=>setAbout(e.target.value)}></Input>
|
||||||
|
</div>
|
||||||
|
<div className="InpWrapper">
|
||||||
|
<div>Дата проведения</div>
|
||||||
|
<DatePicker onChange={(_, datestring)=>setStarts(datestring)} />
|
||||||
|
</div>
|
||||||
|
<div className="InpWrapper">
|
||||||
|
<div>Картинка</div>
|
||||||
|
<FileUploader onResponse={(file)=>setImage(file)}></FileUploader>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="addBtnWrapper">
|
||||||
|
<Button className="btn1" onClick={()=>navigate("/hr")}>Отмена</Button>
|
||||||
|
<Button className="btn2" onClick={()=>create()}>Создать</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
100
src/hr/index.tsx
Normal file
100
src/hr/index.tsx
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
import { Tag } from "antd";
|
||||||
|
import React from "react";
|
||||||
|
import { hrFetcher, useAppSelector, useHRDispatch, useHRSelector } from "../app/hooks";
|
||||||
|
import { getEvents, getMe, setEvents, setMe } from "../app/hr/hrSlice";
|
||||||
|
import { RootHRState } from "../app/hrStore";
|
||||||
|
import { EventIE, HRIE } from "../app/interfaces";
|
||||||
|
import { Header } from "../components/Header";
|
||||||
|
import { UserPreview } from "../components/userPreview";
|
||||||
|
import { EventCard } from "./EventCard";
|
||||||
|
|
||||||
|
|
||||||
|
export const HR:React.FC = () =>{
|
||||||
|
let me = useHRSelector((state:RootHRState)=>getMe(state))
|
||||||
|
let dispatch = useHRDispatch()
|
||||||
|
let events = useHRSelector((state:RootHRState)=>getEvents(state))
|
||||||
|
|
||||||
|
|
||||||
|
if (me.telegram == undefined){
|
||||||
|
hrFetcher.get("users/self/").then(response=>{
|
||||||
|
dispatch(setMe({
|
||||||
|
wallet: response.data.wallet_public_key,
|
||||||
|
role: response.data.type,
|
||||||
|
telegram: response.data.telegram,
|
||||||
|
command: response.data.command,
|
||||||
|
respect: response.data.respect,
|
||||||
|
balance: response.data.money,
|
||||||
|
name: response.data.name
|
||||||
|
} as HRIE))
|
||||||
|
})
|
||||||
|
hrFetcher.get("events/").then(resp=>{
|
||||||
|
dispatch(setEvents(resp.data.map((response:any)=>({
|
||||||
|
name: response.name,
|
||||||
|
about: response.about,
|
||||||
|
slug: response.slug,
|
||||||
|
creator: {
|
||||||
|
wallet: "",
|
||||||
|
role: response.creator.type,
|
||||||
|
telegram: response.creator.telegram,
|
||||||
|
command: response.creator.command,
|
||||||
|
respect: response.creator.respect,
|
||||||
|
balance: response.creator.money,
|
||||||
|
name: response.creator.name
|
||||||
|
} as HRIE,
|
||||||
|
starts: response.starts,
|
||||||
|
image: response.image,
|
||||||
|
planning: Number(response.planning),
|
||||||
|
attended: Number(response.attended)
|
||||||
|
} as EventIE))))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return(
|
||||||
|
<div className="addProduct">
|
||||||
|
<Header links={[
|
||||||
|
{
|
||||||
|
link:"/hr",
|
||||||
|
name:"События"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
link:"/hr/addEvent",
|
||||||
|
name:"Создать событие"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
link:"/hr/transaction",
|
||||||
|
name:"История транзакций"
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
name={"HR"}></Header>
|
||||||
|
<div className="userPreview">
|
||||||
|
<UserPreview
|
||||||
|
level={3}
|
||||||
|
logo_url='/logo_example.png'
|
||||||
|
username='BrainBreaker'
|
||||||
|
rubles={Number(me.balance).toString()}
|
||||||
|
fio={me.name}
|
||||||
|
id={me.command}
|
||||||
|
tg={me.telegram}
|
||||||
|
tags={[<Tag color={'blue'}>HR</Tag>]}
|
||||||
|
description={"Sample"}
|
||||||
|
score={Number(me.respect).toString()}
|
||||||
|
></UserPreview>
|
||||||
|
</div>
|
||||||
|
<div className="marketCard">
|
||||||
|
<div className="addProductH1">Доступные события</div>
|
||||||
|
<div className="productWrapper">
|
||||||
|
{
|
||||||
|
events.map(event=><EventCard
|
||||||
|
image={event.image}
|
||||||
|
cost={5}
|
||||||
|
descr={event.about}
|
||||||
|
name={event.name}
|
||||||
|
members={event.planning}
|
||||||
|
slug={event.slug}
|
||||||
|
></EventCard>)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
94
src/hr/regUsers/index.tsx
Normal file
94
src/hr/regUsers/index.tsx
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import { useNavigate, useParams } from "react-router-dom";
|
||||||
|
import "./regUsers.css"
|
||||||
|
import type { ColumnsType } from 'antd/es/table';
|
||||||
|
import { Button, Space, Table, Tag } from 'antd';
|
||||||
|
import { fetchSubmitAttendance, getCurrentEvent, setCurrentEvent } from "../../app/hr/hrSlice";
|
||||||
|
import { hrFetcher, useHRDispatch, useHRSelector } from "../../app/hooks";
|
||||||
|
import { RootHRState } from "../../app/hrStore";
|
||||||
|
|
||||||
|
interface DataTypeIE{
|
||||||
|
key:string,
|
||||||
|
id:number,
|
||||||
|
telegram:string,
|
||||||
|
attended: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const RegUsers:React.FC = () =>{
|
||||||
|
const [attendance, setAttendance] = useState([-1 as any])
|
||||||
|
let {name} = useParams()
|
||||||
|
|
||||||
|
let navigator = useNavigate()
|
||||||
|
const collator = new Intl.Collator('ru');
|
||||||
|
const data:DataTypeIE[] = []
|
||||||
|
let dispatch = useHRDispatch()
|
||||||
|
|
||||||
|
dispatch(setCurrentEvent(name as string))
|
||||||
|
if (attendance[0] == -1){
|
||||||
|
hrFetcher.get("events/attendance/"+name+"/list/").then(response=>setAttendance(response.data))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const columns: ColumnsType<DataTypeIE> = [
|
||||||
|
{
|
||||||
|
title: 'ID',
|
||||||
|
dataIndex: 'id',
|
||||||
|
key: 'id',
|
||||||
|
defaultSortOrder: 'descend',
|
||||||
|
sorter: (a, b) => a.id - b.id ,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Telegram',
|
||||||
|
dataIndex: 'telegram',
|
||||||
|
key: 'telegram',
|
||||||
|
sorter: (a, b) => collator.compare(a.telegram, b.telegram) ,
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
title: 'Респект',
|
||||||
|
dataIndex: 'telegram',
|
||||||
|
key: 'telegram',
|
||||||
|
render: text=><div>{ "5"} <img src="/respect.svg"></img></div>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Начислено',
|
||||||
|
dataIndex: 'attended',
|
||||||
|
key: 'attended',
|
||||||
|
render: text=><Tag color={text? "green":"red"}>
|
||||||
|
{text? "Да":"Нет"}
|
||||||
|
</Tag>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Награда",
|
||||||
|
dataIndex: 'speciality',
|
||||||
|
key: 'speciality',
|
||||||
|
render: (_, recorder) => <Button onClick={()=>fetchSubmitAttendance(dispatch, recorder.telegram,name as string)} className="btn2">Начислить</Button>
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
attendance.forEach((attend)=>{
|
||||||
|
data.push({
|
||||||
|
key:attend.id,
|
||||||
|
id: attend.id,
|
||||||
|
telegram: attend.worker_username,
|
||||||
|
attended: attend.attended
|
||||||
|
} as DataTypeIE)
|
||||||
|
})
|
||||||
|
|
||||||
|
let currentEvent = useHRSelector((state:RootHRState)=>getCurrentEvent(state))
|
||||||
|
|
||||||
|
|
||||||
|
return(
|
||||||
|
<div className="userTable">
|
||||||
|
<div className="changeProductCard">
|
||||||
|
<div className="addProductH1">Участники мероприятия {currentEvent.name}</div>
|
||||||
|
<Table columns={columns} dataSource={data} />
|
||||||
|
<Button onClick={()=>navigator("/hr")} className="btn2">Назад</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
0
src/hr/regUsers/regUsers.css
Normal file
0
src/hr/regUsers/regUsers.css
Normal file
126
src/hr/transactionHistory/index.tsx
Normal file
126
src/hr/transactionHistory/index.tsx
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
import { Button, Table, Tag } from "antd";
|
||||||
|
import { ColumnsType } from "antd/es/table";
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import { useLocation, useNavigate } from "react-router-dom";
|
||||||
|
import { adminFetcher, hrFetcher, useHRDispatch } from "../../app/hooks";
|
||||||
|
import { Header } from "../../components/Header";
|
||||||
|
|
||||||
|
interface DataTypeIE{
|
||||||
|
key:string,
|
||||||
|
type:string,
|
||||||
|
user_from:string,
|
||||||
|
user_to: string,
|
||||||
|
amount: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export const TransactionHistory: React.FC = () =>{
|
||||||
|
const [transaction, setTransaction] = useState([-1] as any)
|
||||||
|
let location = useLocation()
|
||||||
|
let hr;
|
||||||
|
if (location.pathname.split("/")[1] == "hr"){
|
||||||
|
hr = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (transaction[0] == -1){
|
||||||
|
if(hr){
|
||||||
|
console.log(1)
|
||||||
|
hrFetcher.get("blockchain/history/").then(response=>setTransaction(response.data))
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
console.log(2)
|
||||||
|
adminFetcher.get("blockchain/history/").then(response=>setTransaction(response.data))
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let navigator = useNavigate()
|
||||||
|
const collator = new Intl.Collator('ru');
|
||||||
|
const data:DataTypeIE[] = []
|
||||||
|
|
||||||
|
const columns: ColumnsType<DataTypeIE> = [
|
||||||
|
{
|
||||||
|
title: 'Тип операции',
|
||||||
|
dataIndex: 'type',
|
||||||
|
key: 'type',
|
||||||
|
sorter: (a, b) => collator.compare(a.type, b.type),
|
||||||
|
render: text=><Tag color="green">
|
||||||
|
{text}
|
||||||
|
</Tag>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Отправитель',
|
||||||
|
dataIndex: 'user_from',
|
||||||
|
key: 'user_from',
|
||||||
|
sorter: (a, b) => collator.compare(a.user_from, b.user_from) ,
|
||||||
|
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Получатель',
|
||||||
|
dataIndex: 'user_to',
|
||||||
|
key: 'user_to',
|
||||||
|
sorter: (a, b) => collator.compare(a.user_to, b.user_to) ,
|
||||||
|
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Сумма',
|
||||||
|
dataIndex: 'amount',
|
||||||
|
key: 'amount',
|
||||||
|
render: text=><div>{ text} <img src="/rub.svg"></img></div>
|
||||||
|
},
|
||||||
|
]
|
||||||
|
transaction.forEach((el:any)=>{
|
||||||
|
data.push({
|
||||||
|
key: el.type,
|
||||||
|
type: el.type,
|
||||||
|
user_from: el.user_from,
|
||||||
|
user_to: el.user_to,
|
||||||
|
amount: el.amount,
|
||||||
|
} as DataTypeIE)
|
||||||
|
})
|
||||||
|
|
||||||
|
return(
|
||||||
|
<div className="addProduct">
|
||||||
|
{hr? <Header links={[
|
||||||
|
{
|
||||||
|
link:"/hr",
|
||||||
|
name:"События"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
link:"/hr/addEvent",
|
||||||
|
name:"Создать событие"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
link:"/hr/transaction",
|
||||||
|
name:"История транзакций"
|
||||||
|
}
|
||||||
|
]} name={"HR"}></Header>: <Header links={[
|
||||||
|
{
|
||||||
|
link:"/admin/users",
|
||||||
|
name:"Участники"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
link:"/admin/market",
|
||||||
|
name:"Market place"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
link:"/admin/market/add",
|
||||||
|
name:"Cоздать товар (NFT)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
link:"/admin/transaction",
|
||||||
|
name:"История транзакций"
|
||||||
|
},
|
||||||
|
|
||||||
|
]}
|
||||||
|
name={"Firesieht"}></Header>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
<div className="addProductCard">
|
||||||
|
<div className="addProductH1">История транзакций</div>
|
||||||
|
<Button onClick={()=>navigator("/transaction")} className="btn1">Сделать перевод</Button>
|
||||||
|
<Table columns={columns} dataSource={data} />
|
||||||
|
<Button onClick={()=>navigator(-1)} className="btn2">Назад</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -23,6 +23,12 @@ import { AdminClans } from './admin/adminClans';
|
||||||
import { AddUser } from './admin/addUser';
|
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';
|
||||||
|
import { HR } from './hr';
|
||||||
|
import { hrStore } from './app/hrStore';
|
||||||
|
import { AddEvent } from './hr/addEvent';
|
||||||
|
import { RegUsers } from './hr/regUsers';
|
||||||
|
import { TransactionHistory } from './hr/transactionHistory';
|
||||||
|
import { Transaction } from './Transaction';
|
||||||
|
|
||||||
|
|
||||||
const container = document.getElementById('root')!;
|
const container = document.getElementById('root')!;
|
||||||
|
@ -38,10 +44,17 @@ const router = createBrowserRouter(
|
||||||
<Route path='admin/users/:tg' element={<Provider store={adminStore}><AdminUserCard></AdminUserCard></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/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='admin/users/add' element={<Provider store={adminStore}><AddUser></AddUser></Provider>}></Route>
|
||||||
|
<Route path="/admin/transaction" element={<Provider store={adminStore}><TransactionHistory></TransactionHistory></Provider>}></Route>
|
||||||
|
|
||||||
|
<Route path="hr" element={<Provider store={hrStore}><HR></HR></Provider>}></Route>
|
||||||
|
<Route path="hr/addEvent" element={<Provider store={hrStore}><AddEvent></AddEvent></Provider>}></Route>
|
||||||
|
<Route path="/hr/events/reg/:name" element={<Provider store={hrStore}><RegUsers></RegUsers></Provider>}></Route>
|
||||||
|
<Route path="/hr/transaction" element={<Provider store={hrStore}><TransactionHistory></TransactionHistory></Provider>}></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 />} />
|
||||||
|
|
||||||
|
<Route path="transaction" element={<Transaction></Transaction>}></Route>
|
||||||
</Route>
|
</Route>
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user