rtk
This commit is contained in:
parent
4b869ffe7a
commit
66af185358
@ -26,6 +26,6 @@ module.exports = {
|
||||
},
|
||||
},
|
||||
config: {
|
||||
"dry-wash.api.url": "/api"
|
||||
"nav2.api": "/api"
|
||||
},
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import * as types from '../const'
|
||||
import { starsSlice } from '../slices/stars'
|
||||
|
||||
import { setStars } from './stars'
|
||||
|
||||
@ -20,7 +21,7 @@ export const getUsers = () => async (dispatch, getState) => {
|
||||
dispatch(success(data))
|
||||
|
||||
Object.keys(data).forEach(userId => {
|
||||
dispatch(setStars({ id: userId, value: data[userId].rated }))
|
||||
dispatch(starsSlice.actions.setStars({ id: userId, value: data[userId].rated }))
|
||||
})
|
||||
} else {
|
||||
throw 'Что-то пошло не так'
|
||||
@ -29,4 +30,4 @@ export const getUsers = () => async (dispatch, getState) => {
|
||||
} catch (e) {
|
||||
dispatch(error('Что-то пошло не так'))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
15
src/__data__/model/index.ts
Normal file
15
src/__data__/model/index.ts
Normal file
@ -0,0 +1,15 @@
|
||||
export interface BaseResponse<Body> {
|
||||
success: boolean;
|
||||
body: Body;
|
||||
}
|
||||
|
||||
export interface User {
|
||||
id: string;
|
||||
name: string;
|
||||
age: number;
|
||||
surname: null;
|
||||
email: null;
|
||||
rated: number;
|
||||
avatar: string;
|
||||
friends: User[];
|
||||
}
|
@ -47,4 +47,4 @@ export const reducer = createReducer(initialState, {
|
||||
[types.USER_FETCH_SUCCESS]: fetchSuccess,
|
||||
[types.USER_FETCH_ERROR]: fetchError,
|
||||
[types.USER_FETCH_RESET]: reset,
|
||||
})
|
||||
})
|
||||
|
32
src/__data__/service/main.ts
Normal file
32
src/__data__/service/main.ts
Normal file
@ -0,0 +1,32 @@
|
||||
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
|
||||
import { getConfigValue } from '@brojs/cli'
|
||||
import { BaseResponse, User } from '../model'
|
||||
|
||||
export const api = createApi({
|
||||
reducerPath: 'api',
|
||||
baseQuery: fetchBaseQuery({
|
||||
baseUrl: getConfigValue('nav2.api'),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
}),
|
||||
tagTypes: ['Users'],
|
||||
endpoints: (builder) => ({
|
||||
users: builder.query<Record<string, User>, void>({
|
||||
query: () => ({
|
||||
url: '/users',
|
||||
}),
|
||||
providesTags: ['Users'],
|
||||
transformResponse: (response: BaseResponse<User[]>) =>
|
||||
response.body.reduce((acc, user) => ({ ...acc, [user.id]: user }), {}),
|
||||
}),
|
||||
updateRating: builder.mutation<void, { userId: string, rating: number }>({
|
||||
query: ({ userId, rating }) => ({
|
||||
url: `/user/rate/${userId}`,
|
||||
method: 'POST',
|
||||
body: { rating },
|
||||
}),
|
||||
invalidatesTags: ['Users'],
|
||||
}),
|
||||
}),
|
||||
})
|
23
src/__data__/slices/stars.ts
Normal file
23
src/__data__/slices/stars.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { type PayloadAction, createSlice } from '@reduxjs/toolkit'
|
||||
|
||||
const initialState = {
|
||||
friends: {} as Record<string, number>,
|
||||
user: {},
|
||||
}
|
||||
|
||||
export const starsSlice = createSlice({
|
||||
name: 'stars',
|
||||
initialState,
|
||||
reducers: {
|
||||
setStars(state, action) {
|
||||
state.friends[action.payload.id] = action.payload.value
|
||||
},
|
||||
increment(state, action: PayloadAction<{ id: string }>) {
|
||||
state.friends[action.payload.id] += 1
|
||||
},
|
||||
decrement(state, action) {
|
||||
state.friends[action.payload.id] -= 1
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@ -1,18 +1,21 @@
|
||||
import { configureStore } from '@reduxjs/toolkit'
|
||||
import { useSelector } from 'react-redux'
|
||||
|
||||
import { starsReducer } from './reducers/stars'
|
||||
import { reducer as usersFetchReducer } from './reducers/users'
|
||||
import { starsSlice } from './slices/stars'
|
||||
import { api } from './service/main'
|
||||
|
||||
export const store = configureStore({
|
||||
reducer: {
|
||||
stars: starsReducer,
|
||||
user: usersFetchReducer,
|
||||
[starsSlice.reducerPath]: starsSlice.reducer,
|
||||
[api.reducerPath]: api.reducer,
|
||||
},
|
||||
devTools: {
|
||||
name: 'stars',
|
||||
},
|
||||
middleware: (getDefaultMiddleware) => getDefaultMiddleware(),
|
||||
middleware: (getDefaultMiddleware) => getDefaultMiddleware()
|
||||
.concat(api.middleware),
|
||||
})
|
||||
|
||||
export type StoreType = ReturnType<typeof store.getState>
|
||||
|
@ -2,11 +2,13 @@ import React, { useCallback, useState } from 'react'
|
||||
import { BrowserRouter } from 'react-router-dom'
|
||||
import { ChakraProvider } from '@chakra-ui/react'
|
||||
import { Provider } from 'react-redux'
|
||||
import { ApiProvider } from '@reduxjs/toolkit/query/react'
|
||||
|
||||
import { Dashboard } from './dashboard'
|
||||
import { stars as starsContext } from './__data__/context'
|
||||
import { users } from './__data__/users'
|
||||
import { store } from './__data__/store'
|
||||
import { api } from './__data__/service/main'
|
||||
|
||||
const App = () => {
|
||||
const [stars, setStar] = useState(
|
||||
@ -31,7 +33,9 @@ const App = () => {
|
||||
<ChakraProvider>
|
||||
<BrowserRouter>
|
||||
<Provider store={store}>
|
||||
{/* <ApiProvider api={api}> */}
|
||||
<Dashboard />
|
||||
{/* </ApiProvider> */}
|
||||
</Provider>
|
||||
</BrowserRouter>
|
||||
</ChakraProvider>
|
||||
|
@ -22,15 +22,10 @@ import { stars as starsContext } from '../../__data__/context'
|
||||
import { useUsers } from '../../hooks'
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
import { decrementStars, incrementStars } from '../../__data__/actions/stars'
|
||||
|
||||
type User = Record<string, unknown> & {
|
||||
id: string
|
||||
name: string
|
||||
age: number
|
||||
avatar?: string
|
||||
rated: number
|
||||
friends?: User[]
|
||||
}
|
||||
import { useAppSelector } from '../../__data__/store'
|
||||
import { starsSlice } from '../../__data__/slices/stars'
|
||||
import { User } from '../../__data__/model'
|
||||
import { api } from '../../__data__/service/main'
|
||||
|
||||
const features = getFeatures('nav2')
|
||||
|
||||
@ -172,16 +167,22 @@ const Counter = ({
|
||||
userId: string
|
||||
}) => {
|
||||
// const { rate, setUserRate } = useUsers((state) => state[userId].rated)
|
||||
const rate = useSelector((store) => store.stars.friends[userId]) ?? 0
|
||||
// const rate = useSelector((store) => store.stars.friends)
|
||||
const { data, error, isLoading } = api.useUsersQuery()
|
||||
|
||||
const dispatch = useDispatch()
|
||||
|
||||
const handleIncrement = () => {
|
||||
dispatch(starsSlice.actions.increment({ id: userId }))
|
||||
}
|
||||
|
||||
return (
|
||||
<Center>
|
||||
<VStack>
|
||||
<Heading>{rate}</Heading>
|
||||
<Heading>{data?.[userId]?.rated}</Heading>
|
||||
|
||||
<Button onClick={() => dispatch(incrementStars({ id: userId }))}>+</Button>
|
||||
<Button onClick={() => dispatch(decrementStars({ id: userId }))}>-</Button>
|
||||
<Button onClick={handleIncrement}>+</Button>
|
||||
<Button onClick={() => dispatch(starsSlice.actions.decrement({ id: userId }))}>-</Button>
|
||||
</VStack>
|
||||
</Center>
|
||||
)
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { HStack, Icon } from '@chakra-ui/react'
|
||||
import { HStack, Icon, transform } from '@chakra-ui/react'
|
||||
import { FaRegStar, FaStar } from 'react-icons/fa6'
|
||||
import React, { useMemo, useState } from 'react'
|
||||
import { getConfigValue } from '@brojs/cli'
|
||||
@ -7,6 +7,7 @@ import { useDispatch, useSelector } from 'react-redux'
|
||||
import { stars } from '../__data__/context'
|
||||
import { useUsers } from '../hooks'
|
||||
import { setStars } from '../__data__/actions/stars'
|
||||
import { api } from '../__data__/service/main'
|
||||
|
||||
|
||||
const useStars = (currentRated, starsCount) => {
|
||||
@ -39,11 +40,16 @@ export const Stars = ({
|
||||
// setRated: starsSetRated
|
||||
// } = useStars(rated, count)
|
||||
// const { setUserRate } = useUsers(state => state[userId].rated)
|
||||
const rate = useSelector((store) => store.stars.friends[userId]) ?? 0
|
||||
// const rate = useSelector((store) => store.stars.friends[userId as '2']) ?? 0
|
||||
const dispatch = useDispatch()
|
||||
const { data, error, isLoading } = api.useUsersQuery(void 0)
|
||||
const [updateRating, updateRatingRequest] = api.useUpdateRatingMutation()
|
||||
|
||||
const rate = data?.[userId]?.rated ?? 0
|
||||
|
||||
const handleStarsClick = (value: number) => {
|
||||
dispatch(setStars({ id: userId, value }))
|
||||
// dispatch(setStars({ id: userId, value }))
|
||||
updateRating({ userId, rating: value })
|
||||
// setUserRate(userId, stars)
|
||||
// fetch(getConfigValue('dry-wash.api.url') + '/user-rate', {
|
||||
// method: 'POST',
|
||||
|
@ -11,21 +11,23 @@ import { UnknownAction } from 'redux'
|
||||
import { useAppSelector } from '../__data__/store'
|
||||
import { Statuses } from '../__data__/reducers/users'
|
||||
import { userSelectors } from '../__data__/selectors'
|
||||
import { api } from '../__data__/service/main'
|
||||
|
||||
export const Friends = memo(() => {
|
||||
// const [isLoading, setIsLoading] = useState(false)
|
||||
// const [data, setData] = useState(null)
|
||||
// const [error, setError] = useState(null)
|
||||
const dispatch = useDispatch()
|
||||
// const dispatch = useDispatch()
|
||||
const [showModal, setShowModal] = useState(false)
|
||||
const { data, error, isLoading } = api.useUsersQuery({})
|
||||
|
||||
const isLoading = useAppSelector(userSelectors.isLoading)
|
||||
const data = useAppSelector(userSelectors.data)
|
||||
const error = useAppSelector(userSelectors.error)
|
||||
// const isLoading = useAppSelector(userSelectors.isLoading)
|
||||
// const data = useAppSelector(userSelectors.data)
|
||||
// const error = useAppSelector(userSelectors.error)
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(getUsers() as unknown as UnknownAction)
|
||||
}, [])
|
||||
// useEffect(() => {
|
||||
// dispatch(getUsers() as unknown as UnknownAction)
|
||||
// }, [])
|
||||
|
||||
if(!data || isLoading) return <h1>loading...</h1>
|
||||
|
||||
|
@ -19,7 +19,6 @@ timer.fast = timer(300)
|
||||
router.post('/user-rate', (req, res) => {
|
||||
res.status(500).send({ ok: false })
|
||||
})
|
||||
|
||||
router.use('/admin', router2)
|
||||
|
||||
router.get('/users',
|
||||
@ -27,17 +26,18 @@ router.get('/users',
|
||||
res.status(stubs.users.includes('error') ? 400 : 200).send(require(`../json/users/${stubs.users}.json`))
|
||||
})
|
||||
|
||||
router.post('/user/rate/:userId', (req, res) => {
|
||||
stubs.users = 'success-incremented'
|
||||
|
||||
res.send({ ok: true })
|
||||
})
|
||||
|
||||
router2.get('/', (req, res) => {
|
||||
res.send(`
|
||||
<h2>Users</h2>
|
||||
<ul>
|
||||
<li><button onclick="fetch('/api/admin/users/success')" style="background-color: ${stubs.users === 'success' ? 'green' : '#ccc'}">success</button></li>
|
||||
<li><button onclick="fetch('/api/admin/users/error')" style="background-color: ${stubs.users === 'error' ? 'green' : '#ccc'}">error</button></li>
|
||||
</ul>
|
||||
|
||||
<h2>Users</h2>
|
||||
<ul>
|
||||
<li><button onclick="fetch('/api/admin/users/success')" style="background-color: ${stubs.users === 'success' ? 'green' : '#ccc'}">success</button></li>
|
||||
<li><button onclick="fetch('/api/admin/users/success-incremented')" style="background-color: ${stubs.users === 'success-incremented' ? 'green' : '#ccc'}">success-incremented</button></li>
|
||||
<li><button onclick="fetch('/api/admin/users/error')" style="background-color: ${stubs.users === 'error' ? 'green' : '#ccc'}">error</button></li>
|
||||
</ul>
|
||||
`)
|
||||
|
43
stubs/json/users/success-incremented.json
Normal file
43
stubs/json/users/success-incremented.json
Normal file
@ -0,0 +1,43 @@
|
||||
{
|
||||
"success": false,
|
||||
"body": [
|
||||
{
|
||||
"id": "some-user-id",
|
||||
"name": "alexandr",
|
||||
"age": 38,
|
||||
"surname": null,
|
||||
"email": null,
|
||||
"rated": 5,
|
||||
"avatar": "https://www.gravatar.com/avatar/6529e885535ef67a3fad810ad71167c2c03f79480936e9b3a714731753cbb47e?d=robohash",
|
||||
"friends": [
|
||||
{
|
||||
"id": "2",
|
||||
"name": "not alexandr",
|
||||
"surname": null,
|
||||
"email": null,
|
||||
"rated": 2,
|
||||
"avatar": "https://www.gravatar.com/avatar/6e?d=robohash"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "2",
|
||||
"name": "not alexandr",
|
||||
"surname": null,
|
||||
"email": null,
|
||||
"age": 24,
|
||||
"rated": 2,
|
||||
"avatar": "https://www.gravatar.com/avatar/6e?d=robohash",
|
||||
"friends": [
|
||||
{
|
||||
"id": "some-user-id",
|
||||
"name": "alexandr",
|
||||
"surname": null,
|
||||
"email": null,
|
||||
"rated": 3,
|
||||
"avatar": "https://www.gravatar.com/avatar/6529e885535ef67a3fad810ad71167c2c03f79480936e9b3a714731753cbb47e?d=robohash"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"success": false,
|
||||
"body": {
|
||||
"some-user-id": {
|
||||
"body": [
|
||||
{
|
||||
"id": "some-user-id",
|
||||
"name": "alexandr",
|
||||
"age": 38,
|
||||
@ -20,7 +20,7 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"2": {
|
||||
{
|
||||
"id": "2",
|
||||
"name": "not alexandr",
|
||||
"surname": null,
|
||||
@ -39,5 +39,5 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user