Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions materials/simple_api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
},
"dependencies": {
"body-parser": "^1.19.0",
"cors": "^2.8.5",
"express": "^4.17.1",
"nanoid": "^3.3.6"
}
Expand Down
7 changes: 5 additions & 2 deletions materials/simple_api/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ const express = require("express");
const api = require("./api");
const bodyParser = require("body-parser");
const port = 3001;
const cors = require("cors");

const app = express();
app.use(cors({ origin: true, credentials: true }));

app.use(function (req, res, next) {
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Methods", "*");
res.header(
Expand All @@ -17,7 +19,8 @@ app.use(function (req, res, next) {
app.use(bodyParser.json());
app.use("/api", api);

app.listen(port, "localhost", function (err) {

app.listen(port, "localhost", function(err) {
if (err) {
console.log(err);
return;
Expand Down
15 changes: 14 additions & 1 deletion materials/simple_api/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,14 @@ cookie@0.4.1:
resolved "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz"
integrity sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==

cors@^2.8.5:
version "2.8.5"
resolved "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz"
integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==
dependencies:
object-assign "^4"
vary "^1"

debug@2.6.9:
version "2.6.9"
resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz"
Expand Down Expand Up @@ -234,6 +242,11 @@ negotiator@0.6.2:
resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz"
integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==

object-assign@^4:
version "4.1.1"
resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz"
integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==

on-finished@~2.3.0:
version "2.3.0"
resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz"
Expand Down Expand Up @@ -351,7 +364,7 @@ utils-merge@1.0.1:
resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz"
integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==

vary@~1.1.2:
vary@^1, vary@~1.1.2:
version "1.1.2"
resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz"
integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==
28 changes: 28 additions & 0 deletions src/components/restaurant-comment-item/hooks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { useState } from "react";
import { useUpdateReviewMutation } from "../../data/services/api/api.js";

export const useCommentEditor = () => {
const [editMode, setEditMode] = useState(false);
const [correctedText, setCorrectedText] = useState("");
const [correctedRating, setCorrectedRating] = useState("");

const [updateReview] = useUpdateReviewMutation();

const editCommentHandler = ({ reviewId, text, userId, rating }) => {
setEditMode(!editMode);
if (editMode) {
setCorrectedText(text);
setCorrectedRating(rating);
updateReview({
reviewId,
review: {
text,
userId,
rating
}
});
}
};

return { editMode, editCommentHandler, correctedText, correctedRating };
};
Original file line number Diff line number Diff line change
@@ -1,16 +1,28 @@
import RestaurantCommentItem from "./restaurant-comment-item.jsx";
import { useSelector } from "react-redux";
import { selectReviewById } from "../../data/entities/reviews/slice.js";
import { selectUserById } from "../../data/entities/users/slice.js";
import { useGetUsersQuery } from "../../data/services/api/api.js";
import { FULFILLED } from "../../data/entities/request/sliсe.js";
import { useCommentEditor } from "./hooks.js";

export const RestaurantCommentItemContainer = ({ commentId }) => {
const comment = useSelector((state) => selectReviewById(state, commentId));
const user = useSelector((state) => selectUserById(state, comment.userId));
return (
<RestaurantCommentItem
user={user?.name}
text={comment.text}
rating={comment.rating}
export const RestaurantCommentItemContainer = ({ text, rating, userId, reviewId, currentUserId }) => {
// const comment = useSelector((state) => selectReviewById(state, commentId));
// const user = useSelector((state) => selectUserById(state, userId));

const { correctedComment, correctedRating,editMode, editCommentHandler } = useCommentEditor();

const { data: users, status } = useGetUsersQuery();

const user = users?.find((u) => u.id === userId);

const showEditButton = currentUserId === userId;

return status === FULFILLED ? (
<RestaurantCommentItem user={user}
reviewId={reviewId}
text={correctedComment || text}
rating={correctedRating || rating}
showEditButton={showEditButton}
editMode={editMode}
editCommentHandler={editCommentHandler}
/>
);
) : null;
};
Original file line number Diff line number Diff line change
@@ -1,11 +1,41 @@
import styles from "./restaurant-comment-item.module.css";
import { StarsRating } from "../stars-rating/stars-rating.jsx";
import { Button } from "../button/button.jsx";
import { useRef } from "react";

function RestaurantCommentItem({
user,
text,
rating,
showEditButton,
editMode,
reviewId,
editCommentHandler
}) {

const refText = useRef(null);
const refRating = useRef(null);

function RestaurantCommentItem({ user, text, rating }) {
return (
<li className={styles.item}>
<div>
{user}: <span className={styles.comment}>{text}</span>
{user.name}:
{editMode ? (
<>
<input ref={refText} type="text" defaultValue={text} />
<input ref={refRating} type="text" defaultValue={rating} />
</>
) : (
<span className={styles.comment}>{text}</span>
)}
Comment on lines +23 to +30

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

для редактирования нужно переиспользовать компонент формы. Вся логика уже реализована, для редактирования просто нужно прокинуть onSubmit и начальные значения полей

{showEditButton ? (
<Button text="Править" onClick={() => editCommentHandler({
reviewId,
text: refText?.current?.value,
userId: user.id,
rating: refRating?.current?.value
})} />
) : null}
</div>
<StarsRating value={rating} />
</li>
Expand Down
15 changes: 11 additions & 4 deletions src/components/restaurant-comments/restaurant-comments.jsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
import { RestaurantCommentItemContainer } from "../restaurant-comment-item/restaurant-comment-item-container.jsx";

export default function RestaurantComments({ commentsIds }) {
if (!commentsIds) {
export default function RestaurantComments({ reviews, userId }) {
if (!reviews) {
return null;
}

return (
<ul>
{commentsIds.map((id) => (
<RestaurantCommentItemContainer key={id} commentId={id} />
{reviews.map((review) => (
<RestaurantCommentItemContainer
key={review.id}
reviewId={review.id}
userId={review.userId}
text={review.text}
rating={review.rating}
currentUserId={userId}
/>
))}
</ul>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,16 @@
import { selectRestaurantById } from "../../data/entities/restaurants/slice.js";
import { useSelector } from "react-redux";
import { Link } from "../link/link.jsx";
import styles from "../tab/tabs.module.css";
import { MENU_PAGE, RESTAURANT_PAGE } from "../../pages/links-paths.js";

export const RestaurantTabContainer = ({ restaurantId }) => {
const restaurant = useSelector((state) =>
selectRestaurantById(state, restaurantId),
);
export const RestaurantTabContainer = ({ restaurantId, restaurantName }) => {
return (
<Link
to={`${RESTAURANT_PAGE}/${restaurant.id}/${MENU_PAGE}`}
to={`${RESTAURANT_PAGE}/${restaurantId}/${MENU_PAGE}`}
className={styles.tab}
activeClass={styles.active}
activeUrlText={restaurant.id}
activeUrlText={restaurantId}
>
{restaurant.name}
{restaurantName}
</Link>
);
};
9 changes: 4 additions & 5 deletions src/components/review-form/review-form.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,14 @@ const isFormClear = (form) => {
return !Object.values(form).find((value) => !!value);
};

export const ReviewForm = memo(({ restaurantId }) => {
export const ReviewForm = memo(({ restaurantId, onSubmit }) => {
const {
form,
setName,
setComment,
incrementRating,
decrementRating,
clear,
submit,
} = useForm({ restaurantId });

const { name, comment, rating } = form;
Expand Down Expand Up @@ -61,17 +60,17 @@ export const ReviewForm = memo(({ restaurantId }) => {

<button
className={classNames(styles.button, {
[styles.buttonDisabled]: inputsEmpty,
[styles.buttonDisabled]: inputsEmpty
})}
onClick={clear}
>
очистить
</button>
<button
className={classNames(styles.button, {
[styles.buttonDisabled]: inputsEmpty,
[styles.buttonDisabled]: inputsEmpty
})}
onClick={!inputsEmpty && submit}
onClick={!inputsEmpty ? () => onSubmit(form) : null}
>
отправить
</button>
Expand Down
6 changes: 4 additions & 2 deletions src/data/redux/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { reviewsSlice } from "../entities/reviews/slice.js";
import { usersSlice } from "../entities/users/slice.js";
import { cartSlice } from "../entities/cart/slice.js";
import { requestSlice } from "../entities/request/sliсe.js";
import { api } from "../services/api/api.js";

const loggerMiddleware = (store) => (next) => (action) => {
console.log(action);
Expand All @@ -18,8 +19,9 @@ export const store = configureStore({
[reviewsSlice.name]: reviewsSlice.reducer,
[usersSlice.name]: usersSlice.reducer,
[cartSlice.name]: cartSlice.reducer,
[requestSlice.name]: requestSlice.reducer
[requestSlice.name]: requestSlice.reducer,
[api.reducerPath]: api.reducer
},
middleware: (getDefaultMiddlewares) =>
getDefaultMiddlewares().concat(loggerMiddleware)
getDefaultMiddlewares().concat( api.middleware)
});
65 changes: 65 additions & 0 deletions src/data/services/api/api.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";

export const api = createApi({
reducerPath: "api",
// pollingInterval: 5000,
baseQuery: fetchBaseQuery({
baseUrl: "http://localhost:3001/api"
}),
tagTypes: ["review"],
endpoints: (builder) => ({
getRestaurants: builder.query({
query: () => "/restaurants",
keepUnusedDataFor: 20
}),
getRestaurantById: builder.query({
query: (id) => `/restaurant/${id}`,
keepUnusedDataFor: 20
}),
getDishes: builder.query({
query: (restaurantId) => `/dishes?restaurantId=${restaurantId}`,
keepUnusedDataFor: 20
}),
getDishById: builder.query({
query: (id) => `/dish/${id}`,
keepUnusedDataFor: 20
}),
getReviews: builder.query({
query: (restaurantId) => `/reviews?restaurantId=${restaurantId}`,
providesTags: (_, __, id) => [{ type: "review", id }],
keepUnusedDataFor: 20
}),
addReview: builder.mutation({
query: ({ restaurantId, review }) => ({
url: `/review/${restaurantId}`,
method: "POST",
body: review
}),
invalidatesTags: (_, __, { id }) => [{ type: "review", id }]
}),
updateReview: builder.mutation({
query: ({ reviewId, review }) => ({
url: `/review/${reviewId}`,
method: "PATCH",
body: review
}),
invalidatesTags: (_, __, { id }) => [{ type: "review", id }]
}),
getUsers: builder.query({
query: () => `/users`,
keepUnusedDataFor: 20
})
})
});

export const {
useGetRestaurantsQuery,
useLazyGetRestaurantsQuery,
useGetRestaurantByIdQuery,
useGetDishesQuery,
useGetDishByIdQuery,
useGetReviewsQuery,
useGetUsersQuery,
useAddReviewMutation,
useUpdateReviewMutation,
} = api;
22 changes: 4 additions & 18 deletions src/layouts/restaurant-page-layout-container.jsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,9 @@
import { useSelector } from "react-redux";
import { selectRestaurantById, selectRestaurantsIds } from "../data/entities/restaurants/slice.js";
import { RestaurantPageLayout } from "./restaurant-page-layout.jsx";
import { useParams } from "react-router";
import { getRestaurants } from "../data/entities/restaurants/get-restaurants.js";
import { useRequest } from "../data/hooks/use-request.js";
import { getRestaurant } from "../data/entities/restaurants/get-restaurant.js";
import { useGetRestaurantsQuery } from "../data/services/api/api.js";

export const RestaurantPageLayoutContainer = () => {
const requestStatus = useRequest(getRestaurants);
const { restaurantId } = useParams();
const restaurantRequestStatus = useRequest(getRestaurant, restaurantId);
const restaurant = useSelector((state) =>
selectRestaurantById(state, restaurantId)
);
const { data: restaurants, status: requestStatus } = useGetRestaurantsQuery();

const restaurantsIds = useSelector(selectRestaurantsIds);

return <RestaurantPageLayout restaurantsIds={restaurantsIds}
requestStatus={requestStatus}
restaurantRequestStatus={restaurantRequestStatus}
restaurant={restaurant}></RestaurantPageLayout>;
return <RestaurantPageLayout restaurants={restaurants}
requestStatus={requestStatus}></RestaurantPageLayout>;
};
Loading