diff --git a/bro.config.js b/bro.config.js index aa1862d..0990dc2 100644 --- a/bro.config.js +++ b/bro.config.js @@ -26,6 +26,6 @@ module.exports = { }, }, config: { - "dry-wash.api.url": "/api" + "nav2.api": "/api" }, } diff --git a/src/__data__/actions/users.ts b/src/__data__/actions/users.ts index 55a1b57..3e920eb 100644 --- a/src/__data__/actions/users.ts +++ b/src/__data__/actions/users.ts @@ -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('Что-то пошло не так')) } -} \ No newline at end of file +} diff --git a/src/__data__/model/index.ts b/src/__data__/model/index.ts new file mode 100644 index 0000000..ab035f5 --- /dev/null +++ b/src/__data__/model/index.ts @@ -0,0 +1,15 @@ +export interface BaseResponse { + success: boolean; + body: Body; +} + +export interface User { + id: string; + name: string; + age: number; + surname: null; + email: null; + rated: number; + avatar: string; + friends: User[]; +} diff --git a/src/__data__/reducers/users.ts b/src/__data__/reducers/users.ts index 0f2c223..b4ed9bb 100644 --- a/src/__data__/reducers/users.ts +++ b/src/__data__/reducers/users.ts @@ -47,4 +47,4 @@ export const reducer = createReducer(initialState, { [types.USER_FETCH_SUCCESS]: fetchSuccess, [types.USER_FETCH_ERROR]: fetchError, [types.USER_FETCH_RESET]: reset, -}) \ No newline at end of file +}) diff --git a/src/__data__/service/main.ts b/src/__data__/service/main.ts new file mode 100644 index 0000000..3134762 --- /dev/null +++ b/src/__data__/service/main.ts @@ -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, void>({ + query: () => ({ + url: '/users', + }), + providesTags: ['Users'], + transformResponse: (response: BaseResponse) => + response.body.reduce((acc, user) => ({ ...acc, [user.id]: user }), {}), + }), + updateRating: builder.mutation({ + query: ({ userId, rating }) => ({ + url: `/user/rate/${userId}`, + method: 'POST', + body: { rating }, + }), + invalidatesTags: ['Users'], + }), + }), +}) diff --git a/src/__data__/slices/stars.ts b/src/__data__/slices/stars.ts new file mode 100644 index 0000000..6e3357e --- /dev/null +++ b/src/__data__/slices/stars.ts @@ -0,0 +1,23 @@ +import { type PayloadAction, createSlice } from '@reduxjs/toolkit' + +const initialState = { + friends: {} as Record, + 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 + }, + }, +}) + diff --git a/src/__data__/store.ts b/src/__data__/store.ts index e1f93c0..a91da31 100644 --- a/src/__data__/store.ts +++ b/src/__data__/store.ts @@ -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 diff --git a/src/app.tsx b/src/app.tsx index e0dc98f..87e73b4 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -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 = () => { + {/* */} + {/* */} diff --git a/src/components/profile/profile.tsx b/src/components/profile/profile.tsx index dd90322..030ced3 100644 --- a/src/components/profile/profile.tsx +++ b/src/components/profile/profile.tsx @@ -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 & { - 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 (
- {rate} + {data?.[userId]?.rated} - - + +
) diff --git a/src/components/stars.tsx b/src/components/stars.tsx index 3f74eac..9c2299f 100644 --- a/src/components/stars.tsx +++ b/src/components/stars.tsx @@ -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', diff --git a/src/pages/friends.tsx b/src/pages/friends.tsx index 07fcfa5..89a713b 100644 --- a/src/pages/friends.tsx +++ b/src/pages/friends.tsx @@ -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

loading...

diff --git a/stubs/api/index.js b/stubs/api/index.js index f4ce897..6a5a439 100644 --- a/stubs/api/index.js +++ b/stubs/api/index.js @@ -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(`

Users

  • -
  • -
- -

Users

-
    -
  • +
`) diff --git a/stubs/json/users/success-incremented.json b/stubs/json/users/success-incremented.json new file mode 100644 index 0000000..814ccc6 --- /dev/null +++ b/stubs/json/users/success-incremented.json @@ -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" + } + ] + } + ] +} diff --git a/stubs/json/users/success.json b/stubs/json/users/success.json index e6898ba..3509ad5 100644 --- a/stubs/json/users/success.json +++ b/stubs/json/users/success.json @@ -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 @@ } ] } - } + ] }