rtk
This commit is contained in:
parent
4b869ffe7a
commit
66af185358
@ -26,6 +26,6 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
config: {
|
config: {
|
||||||
"dry-wash.api.url": "/api"
|
"nav2.api": "/api"
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import * as types from '../const'
|
import * as types from '../const'
|
||||||
|
import { starsSlice } from '../slices/stars'
|
||||||
|
|
||||||
import { setStars } from './stars'
|
import { setStars } from './stars'
|
||||||
|
|
||||||
@ -20,7 +21,7 @@ export const getUsers = () => async (dispatch, getState) => {
|
|||||||
dispatch(success(data))
|
dispatch(success(data))
|
||||||
|
|
||||||
Object.keys(data).forEach(userId => {
|
Object.keys(data).forEach(userId => {
|
||||||
dispatch(setStars({ id: userId, value: data[userId].rated }))
|
dispatch(starsSlice.actions.setStars({ id: userId, value: data[userId].rated }))
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
throw 'Что-то пошло не так'
|
throw 'Что-то пошло не так'
|
||||||
@ -29,4 +30,4 @@ export const getUsers = () => async (dispatch, getState) => {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
dispatch(error('Что-то пошло не так'))
|
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_SUCCESS]: fetchSuccess,
|
||||||
[types.USER_FETCH_ERROR]: fetchError,
|
[types.USER_FETCH_ERROR]: fetchError,
|
||||||
[types.USER_FETCH_RESET]: reset,
|
[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 { configureStore } from '@reduxjs/toolkit'
|
||||||
import { useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
|
|
||||||
import { starsReducer } from './reducers/stars'
|
|
||||||
import { reducer as usersFetchReducer } from './reducers/users'
|
import { reducer as usersFetchReducer } from './reducers/users'
|
||||||
|
import { starsSlice } from './slices/stars'
|
||||||
|
import { api } from './service/main'
|
||||||
|
|
||||||
export const store = configureStore({
|
export const store = configureStore({
|
||||||
reducer: {
|
reducer: {
|
||||||
stars: starsReducer,
|
|
||||||
user: usersFetchReducer,
|
user: usersFetchReducer,
|
||||||
|
[starsSlice.reducerPath]: starsSlice.reducer,
|
||||||
|
[api.reducerPath]: api.reducer,
|
||||||
},
|
},
|
||||||
devTools: {
|
devTools: {
|
||||||
name: 'stars',
|
name: 'stars',
|
||||||
},
|
},
|
||||||
middleware: (getDefaultMiddleware) => getDefaultMiddleware(),
|
middleware: (getDefaultMiddleware) => getDefaultMiddleware()
|
||||||
|
.concat(api.middleware),
|
||||||
})
|
})
|
||||||
|
|
||||||
export type StoreType = ReturnType<typeof store.getState>
|
export type StoreType = ReturnType<typeof store.getState>
|
||||||
|
@ -2,11 +2,13 @@ import React, { useCallback, useState } from 'react'
|
|||||||
import { BrowserRouter } from 'react-router-dom'
|
import { BrowserRouter } from 'react-router-dom'
|
||||||
import { ChakraProvider } from '@chakra-ui/react'
|
import { ChakraProvider } from '@chakra-ui/react'
|
||||||
import { Provider } from 'react-redux'
|
import { Provider } from 'react-redux'
|
||||||
|
import { ApiProvider } from '@reduxjs/toolkit/query/react'
|
||||||
|
|
||||||
import { Dashboard } from './dashboard'
|
import { Dashboard } from './dashboard'
|
||||||
import { stars as starsContext } from './__data__/context'
|
import { stars as starsContext } from './__data__/context'
|
||||||
import { users } from './__data__/users'
|
import { users } from './__data__/users'
|
||||||
import { store } from './__data__/store'
|
import { store } from './__data__/store'
|
||||||
|
import { api } from './__data__/service/main'
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const [stars, setStar] = useState(
|
const [stars, setStar] = useState(
|
||||||
@ -31,7 +33,9 @@ const App = () => {
|
|||||||
<ChakraProvider>
|
<ChakraProvider>
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
|
{/* <ApiProvider api={api}> */}
|
||||||
<Dashboard />
|
<Dashboard />
|
||||||
|
{/* </ApiProvider> */}
|
||||||
</Provider>
|
</Provider>
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
</ChakraProvider>
|
</ChakraProvider>
|
||||||
|
@ -22,15 +22,10 @@ import { stars as starsContext } from '../../__data__/context'
|
|||||||
import { useUsers } from '../../hooks'
|
import { useUsers } from '../../hooks'
|
||||||
import { useDispatch, useSelector } from 'react-redux'
|
import { useDispatch, useSelector } from 'react-redux'
|
||||||
import { decrementStars, incrementStars } from '../../__data__/actions/stars'
|
import { decrementStars, incrementStars } from '../../__data__/actions/stars'
|
||||||
|
import { useAppSelector } from '../../__data__/store'
|
||||||
type User = Record<string, unknown> & {
|
import { starsSlice } from '../../__data__/slices/stars'
|
||||||
id: string
|
import { User } from '../../__data__/model'
|
||||||
name: string
|
import { api } from '../../__data__/service/main'
|
||||||
age: number
|
|
||||||
avatar?: string
|
|
||||||
rated: number
|
|
||||||
friends?: User[]
|
|
||||||
}
|
|
||||||
|
|
||||||
const features = getFeatures('nav2')
|
const features = getFeatures('nav2')
|
||||||
|
|
||||||
@ -172,16 +167,22 @@ const Counter = ({
|
|||||||
userId: string
|
userId: string
|
||||||
}) => {
|
}) => {
|
||||||
// const { rate, setUserRate } = useUsers((state) => state[userId].rated)
|
// 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 dispatch = useDispatch()
|
||||||
|
|
||||||
|
const handleIncrement = () => {
|
||||||
|
dispatch(starsSlice.actions.increment({ id: userId }))
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Center>
|
<Center>
|
||||||
<VStack>
|
<VStack>
|
||||||
<Heading>{rate}</Heading>
|
<Heading>{data?.[userId]?.rated}</Heading>
|
||||||
|
|
||||||
<Button onClick={() => dispatch(incrementStars({ id: userId }))}>+</Button>
|
<Button onClick={handleIncrement}>+</Button>
|
||||||
<Button onClick={() => dispatch(decrementStars({ id: userId }))}>-</Button>
|
<Button onClick={() => dispatch(starsSlice.actions.decrement({ id: userId }))}>-</Button>
|
||||||
</VStack>
|
</VStack>
|
||||||
</Center>
|
</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 { FaRegStar, FaStar } from 'react-icons/fa6'
|
||||||
import React, { useMemo, useState } from 'react'
|
import React, { useMemo, useState } from 'react'
|
||||||
import { getConfigValue } from '@brojs/cli'
|
import { getConfigValue } from '@brojs/cli'
|
||||||
@ -7,6 +7,7 @@ import { useDispatch, useSelector } from 'react-redux'
|
|||||||
import { stars } from '../__data__/context'
|
import { stars } from '../__data__/context'
|
||||||
import { useUsers } from '../hooks'
|
import { useUsers } from '../hooks'
|
||||||
import { setStars } from '../__data__/actions/stars'
|
import { setStars } from '../__data__/actions/stars'
|
||||||
|
import { api } from '../__data__/service/main'
|
||||||
|
|
||||||
|
|
||||||
const useStars = (currentRated, starsCount) => {
|
const useStars = (currentRated, starsCount) => {
|
||||||
@ -39,11 +40,16 @@ export const Stars = ({
|
|||||||
// setRated: starsSetRated
|
// setRated: starsSetRated
|
||||||
// } = useStars(rated, count)
|
// } = useStars(rated, count)
|
||||||
// const { setUserRate } = useUsers(state => state[userId].rated)
|
// 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 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) => {
|
const handleStarsClick = (value: number) => {
|
||||||
dispatch(setStars({ id: userId, value }))
|
// dispatch(setStars({ id: userId, value }))
|
||||||
|
updateRating({ userId, rating: value })
|
||||||
// setUserRate(userId, stars)
|
// setUserRate(userId, stars)
|
||||||
// fetch(getConfigValue('dry-wash.api.url') + '/user-rate', {
|
// fetch(getConfigValue('dry-wash.api.url') + '/user-rate', {
|
||||||
// method: 'POST',
|
// method: 'POST',
|
||||||
|
@ -11,21 +11,23 @@ import { UnknownAction } from 'redux'
|
|||||||
import { useAppSelector } from '../__data__/store'
|
import { useAppSelector } from '../__data__/store'
|
||||||
import { Statuses } from '../__data__/reducers/users'
|
import { Statuses } from '../__data__/reducers/users'
|
||||||
import { userSelectors } from '../__data__/selectors'
|
import { userSelectors } from '../__data__/selectors'
|
||||||
|
import { api } from '../__data__/service/main'
|
||||||
|
|
||||||
export const Friends = memo(() => {
|
export const Friends = memo(() => {
|
||||||
// const [isLoading, setIsLoading] = useState(false)
|
// const [isLoading, setIsLoading] = useState(false)
|
||||||
// const [data, setData] = useState(null)
|
// const [data, setData] = useState(null)
|
||||||
// const [error, setError] = useState(null)
|
// const [error, setError] = useState(null)
|
||||||
const dispatch = useDispatch()
|
// const dispatch = useDispatch()
|
||||||
const [showModal, setShowModal] = useState(false)
|
const [showModal, setShowModal] = useState(false)
|
||||||
|
const { data, error, isLoading } = api.useUsersQuery({})
|
||||||
|
|
||||||
const isLoading = useAppSelector(userSelectors.isLoading)
|
// const isLoading = useAppSelector(userSelectors.isLoading)
|
||||||
const data = useAppSelector(userSelectors.data)
|
// const data = useAppSelector(userSelectors.data)
|
||||||
const error = useAppSelector(userSelectors.error)
|
// const error = useAppSelector(userSelectors.error)
|
||||||
|
|
||||||
useEffect(() => {
|
// useEffect(() => {
|
||||||
dispatch(getUsers() as unknown as UnknownAction)
|
// dispatch(getUsers() as unknown as UnknownAction)
|
||||||
}, [])
|
// }, [])
|
||||||
|
|
||||||
if(!data || isLoading) return <h1>loading...</h1>
|
if(!data || isLoading) return <h1>loading...</h1>
|
||||||
|
|
||||||
|
@ -19,7 +19,6 @@ timer.fast = timer(300)
|
|||||||
router.post('/user-rate', (req, res) => {
|
router.post('/user-rate', (req, res) => {
|
||||||
res.status(500).send({ ok: false })
|
res.status(500).send({ ok: false })
|
||||||
})
|
})
|
||||||
|
|
||||||
router.use('/admin', router2)
|
router.use('/admin', router2)
|
||||||
|
|
||||||
router.get('/users',
|
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`))
|
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) => {
|
router2.get('/', (req, res) => {
|
||||||
res.send(`
|
res.send(`
|
||||||
<h2>Users</h2>
|
<h2>Users</h2>
|
||||||
<ul>
|
<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')" 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>
|
<li><button onclick="fetch('/api/admin/users/success-incremented')" style="background-color: ${stubs.users === 'success-incremented' ? 'green' : '#ccc'}">success-incremented</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/error')" style="background-color: ${stubs.users === 'error' ? 'green' : '#ccc'}">error</button></li>
|
<li><button onclick="fetch('/api/admin/users/error')" style="background-color: ${stubs.users === 'error' ? 'green' : '#ccc'}">error</button></li>
|
||||||
</ul>
|
</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,
|
"success": false,
|
||||||
"body": {
|
"body": [
|
||||||
"some-user-id": {
|
{
|
||||||
"id": "some-user-id",
|
"id": "some-user-id",
|
||||||
"name": "alexandr",
|
"name": "alexandr",
|
||||||
"age": 38,
|
"age": 38,
|
||||||
@ -20,7 +20,7 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"2": {
|
{
|
||||||
"id": "2",
|
"id": "2",
|
||||||
"name": "not alexandr",
|
"name": "not alexandr",
|
||||||
"surname": null,
|
"surname": null,
|
||||||
@ -39,5 +39,5 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
]
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user