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