اذهب إلى المحتوى
  • 0

إضافة تقييم ثم جلبه من قاعدة البيانات.

محمود سعداوي2

السؤال

السلام عليكم.

في المثال التالي، أواجه الأخطاء التالية:

Capture.JPG.008cec6363b60783e44bddcc5049386a.JPG

  • عندما أضيف Review معين (بين 1 و 5) فإنه يتم إرسال عدد أكبر من 5 إلى قاعدة البياناتCapture2.JPG.675f9d179e359c0b6ed50871b3c975aa.JPG

لايتم إضافة التقييم إلى واجهة الصفحة إلا بعد التحديث

الكود

bookController

// method   POST 
// route    api/books/:id/reviews
// desc     Add a book review
// access   Private | auth
const addReview = asyncHandler(async(req, res) => {
    const { id } = req.params
    const { comment, rate } = req.body
    const book = await Book.findById(id)
    const user = await User.findById(req.userId)

    if (!book) {
        return res.status(404).json({ message: "Book Not Found" })
    }
    const isRated = book.reviews.findIndex(m => m.user == req.userId)
    if (isRated > -1){
        return res.status(403).send({ message: "Review Is Already Added" });
    }
    const totalRate = book.reviews.reduce((sum, review) => sum + review.rate ,0)  
    const finalRate = (totalRate + rate) / (book.reviews.length + 1)

    await Book.updateOne(
        { _id: id } ,
        {
            $push: {
                reviews: {
                    user: req.userId,
                    username: user.name,
                    comment,
                    rate
                }
            },
            $set: {
                rate: finalRate
            }
        }
    )

    res.status(201).json({ message: "Review added successfully" })    
})

Frontend

apiCall

 

// Post Review
export function postReview(bookId, review) {
  return async (dispatch, getState) => {
    try {
      dispatch(bookActions.setLoading())
      const {data} = await axios.post(`${BOOK_URL}/${bookId}/reviews`, review, {
        headers: {
          "authorization": getState().auth.user.accessToken
        }
      });
      toast.success(data?.message)
      dispatch(bookActions.addReviews());
      dispatch(bookActions.clearLoading());
    } catch (error) {
      toast.error(error?.response?.data.message);
      dispatch(bookActions.clearLoading());
    }
  };
}

bookSlice

const bookSlice = createSlice({
    name: "book",
    initialState: {
        books: [],
        error: false,
        loading: false,
    },
    reducers: {
        getBooks(state, action) {
            state.books = action.payload;
        },
        findBook(state, action) {
            state.books = action.payload;
        },
        addReviews(state, action) {
            state.books = action.payload;
        },
        setLoading(state) {
            state.loading = true;
        },
        clearLoading(state) {
            state.loading = false;
        },
        setError(state) {
            state.error = true;
        },
        clearError(state) {
            state.error = false;
        },
    }
}

Component

const Modal = ({ showModal, handleClose, book }) => {
    const [rate, setRating] = useState(0);
    const [comment, setComment] = useState("");

    const dispatch = useDispatch();
    const navigate = useNavigate();
  
    const submitReview = (e) => {
      e.preventDefault();
    if (comment === "") {
        return toast.error("Comment is required")
    }
      dispatch(postReview(book, { rate, comment }))
      navigate(`/${book}`)
    };
  
    return (
      <div className={`modal ${showModal ? "show" : ""}`}>
        <div className="modal-content">
          <span className="close" onClick={handleClose}>&times;</span>
          <h2>Submit Your Review</h2>
          <form onSubmit={submitReview}>
            <div className="rating-input">
              <label>Rating:</label>
              <select value={rate} onChange={(e) => setRating(e.target.value)} required>
                <option value="" disabled>Select a rating</option>
                <option value="1">1</option>
                <option value="2">2</option>
                <option value="3">3</option>
                <option value="4">4</option>
                <option value="5">5</option>
              </select>
            </div>
            <div className="comment">
              <label>Comment:</label>
              <textarea value={comment} onChange={(e) => setComment(e.target.value)}/>
            </div>
            <button className='modal-btn' type="submit">Submit</button>
          </form>
        </div>
      </div>
    );
  };
  
  export default Modal;

شكرا على المساعدة

رابط هذا التعليق
شارك على الشبكات الإجتماعية

Recommended Posts

  • 0

أولاً لديك خطأ في حساب متوسط التقييمات في السطر الذي يحسب فيه المتوسط النهائي finalRate.

فالمتغير rate ربما يتم إرساله كقيمة غير صحيحة مثل قيمة نصية من الواجهة الأمامية، لذا لنتأكد من أن rate يتم تحويله إلى رقم صحيح قبل استخدامه في الحساب، وأيضًا التأكد من صحة قيم التقييم المرسلة من الواجهة الأمامية.

أيضًا بعد إضافة المراجعة إلى قاعدة البيانات، عليك إعادة بيانات الكتاب المحدثة إلى الواجهة الأمامية، بما في ذلك التقييم الجديد، وبعد تلقي بيانات الكتاب المحدثة من الخادم، يجب تحديث حالة الكتاب في الواجهة الأمامية لتعكس التقييم الجديد.

ثم استخدام البيانات المحدثة للكتاب في المكون لعرض التقييم الجديد.

إليك التعديلات مع تعليقات لتوضيح أماكن التعديل.

تعديل في bookController.js:

const addReview = asyncHandler(async(req, res) => {
    const { id } = req.params
    const { comment, rate } = req.body
    const book = await Book.findById(id)
    const user = await User.findById(req.userId)

    // التحقق من وجود الكتاب
    if (!book) {
        return res.status(404).json({ message: "Book Not Found" })
    }

    // التحقق من أن المستخدم لم يقيم الكتاب مسبقاً
    const isRated = book.reviews.findIndex(m => m.user == req.userId)
    if (isRated > -1){
        return res.status(403).send({ message: "Review Is Already Added" });
    }

    // تحويل تقييم المستخدم إلى رقم صحيح والتحقق من صحته
    const numericRate = parseInt(rate, 10);
    if (isNaN(numericRate) || numericRate < 1 || numericRate > 5) {
        return res.status(400).send({ message: "Invalid rating value" });
    }

    // حساب التقييم النهائي
    const totalRate = book.reviews.reduce((sum, review) => sum + review.rate ,0)  
    const finalRate = (totalRate + numericRate) / (book.reviews.length + 1)

    // تحديث بيانات الكتاب بإضافة المراجعة الجديدة وتحديث التقييم
    await Book.updateOne(
        { _id: id },
        {
            $push: {
                reviews: {
                    user: req.userId,
                    username: user.name,
                    comment,
                    rate: numericRate
                }
            },
            $set: {
                rate: finalRate
            }
        }
    )

    // إعادة بيانات الكتاب المحدثة بعد الإضافة
    const updatedBook = await Book.findById(id);
    res.status(201).json(updatedBook)
})

تحديث الدالة postReview في apiCall:

export function postReview(bookId, review) {
  return async (dispatch, getState) => {
    try {
      dispatch(bookActions.setLoading())
      const {data} = await axios.post(`${BOOK_URL}/${bookId}/reviews`, review, {
        headers: {
          "authorization": getState().auth.user.accessToken
        }
      });
      // عرض رسالة نجاح باستخدام toast
      toast.success(data?.message)
      // تحديث بيانات الكتاب المحدثة في الحالة
      dispatch(bookActions.updateBook(data));
      dispatch(bookActions.clearLoading());
    } catch (error) {
      // عرض رسالة خطأ باستخدام toast
      toast.error(error?.response?.data.message);
      dispatch(bookActions.clearLoading());
    }
  };
}

تعديل في الواجهة الأمامية Modal.js

const Modal = ({ showModal, handleClose, book }) => {
    const [rate, setRating] = useState(0);
    const [comment, setComment] = useState("");

    const dispatch = useDispatch();
    const navigate = useNavigate();
  
    const submitReview = (e) => {
      e.preventDefault();
      if (comment === "") {
        // التحقق من وجود تعليق وعرض رسالة خطأ في حال عدم وجوده
        return toast.error("Comment is required")
      }
      // تحويل تقييم المستخدم إلى رقم صحيح والتحقق من صحته
      const numericRate = parseInt(rate, 10);
      if (isNaN(numericRate) || numericRate < 1 || numericRate > 5) {
        return toast.error("Invalid rating value");
      }
      // إرسال المراجعة باستخدام الدالة postReview
      dispatch(postReview(book._id, { rate: numericRate, comment }))
    };
  
    return (
      <div className={`modal ${showModal ? "show" : ""}`}>
        <div className="modal-content">
          <span className="close" onClick={handleClose}>&times;</span>
          <h2>Submit Your Review</h2>
          <form onSubmit={submitReview}>
            <div className="rating-input">
              <label>Rating:</label>
              <select value={rate} onChange={(e) => setRating(e.target.value)} required>
                <option value="" disabled>Select a rating</option>
                <option value="1">1</option>
                <option value="2">2</option>
                <option value="3">3</option>
                <option value="4">4</option>
                <option value="5">5</option>
              </select>
            </div>
            <div className="comment">
              <label>Comment:</label>
              <textarea value={comment} onChange={(e) => setComment(e.target.value)} required />
            </div>
            <button className='modal-btn' type="submit">Submit</button>
          </form>
        </div>
      </div>
    );
};

export default Modal;

تحديث bookSlice لتضمين الدالة updateBook:

const bookSlice = createSlice({
    name: "book",
    initialState: {
        books: [],
        error: false,
        loading: false,
    },
    reducers: {
        getBooks(state, action) {
            state.books = action.payload;
        },
        findBook(state, action) {
            state.books = action.payload;
        },
        addReviews(state, action) {
            state.books = action.payload;
        },
        updateBook(state, action) {
            // البحث عن الكتاب المحدث وتحديثه
            const updatedBook = action.payload;
            const index = state.books.findIndex(book => book._id === updatedBook._id);
            if (index !== -1) {
                state.books[index] = updatedBook;
            }
        },
        setLoading(state) {
            state.loading = true;
        },
        clearLoading(state) {
            state.loading = false;
        },
        setError(state) {
            state.error = true;
        },
        clearError(state) {
            state.error = false;
        },
    }
});

export const bookActions = bookSlice.actions;
export default bookSlice.reducer;

 

رابط هذا التعليق
شارك على الشبكات الإجتماعية

انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أجب على هذا السؤال...

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.

  • إعلانات

  • تابعنا على



×
×
  • أضف...