diff --git a/pkg/api/api.go b/pkg/api/api.go index 0a41ce9..28eb51c 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -95,6 +95,8 @@ func NewAPI(config Config) (*API, error) { // review apiMux.HandleFunc("/insert_review", api.HandleInsertReview) apiMux.HandleFunc("/get_reviews_on_file", api.HandleGetReviewsOnFile) + apiMux.HandleFunc("/get_review", api.HandleGetReview) + apiMux.HandleFunc("/update_review", api.HandleUpdateReview) // below needs token apiMux.HandleFunc("/walk", api.HandleWalk) apiMux.HandleFunc("/reset", api.HandleReset) diff --git a/pkg/api/handle_review.go b/pkg/api/handle_review.go index 8760e72..0d25682 100644 --- a/pkg/api/handle_review.go +++ b/pkg/api/handle_review.go @@ -67,3 +67,57 @@ func (api *API) HandleGetReviewsOnFile(w http.ResponseWriter, r *http.Request) { return } } + +type GetReviewRequest struct { + ID int64 `json:"id"` +} + +type GetReviewResponse struct { + Review *database.Review `json:"review"` +} + +func (api *API) HandleGetReview(w http.ResponseWriter, r *http.Request) { + req := &GetReviewRequest{} + + err := json.NewDecoder(r.Body).Decode(req) + if err != nil { + api.HandleError(w, r, err) + return + } + + review, err := api.Db.GetReview(req.ID) + if err != nil { + api.HandleError(w, r, err) + return + } + + ret := &GetReviewResponse{ + Review: review, + } + + err = json.NewEncoder(w).Encode(ret) + if err != nil { + api.HandleError(w, r, err) + return + } +} + +func (api *API) HandleUpdateReview(w http.ResponseWriter, r *http.Request) { + req := &database.Review{} + + err := json.NewDecoder(r.Body).Decode(req) + if err != nil { + api.HandleError(w, r, err) + return + } + + req.UpdatedAt = time.Now().Unix() + + err = api.Db.UpdateReview(req) + if err != nil { + api.HandleError(w, r, err) + return + } + + api.HandleOK(w, r) +} diff --git a/pkg/database/method_review.go b/pkg/database/method_review.go index 571684c..915456f 100644 --- a/pkg/database/method_review.go +++ b/pkg/database/method_review.go @@ -40,3 +40,29 @@ func (database *Database) GetReviewsOnFile(fileId int64) ([]*Review, error) { } return reviews, nil } + +func (database *Database) GetReview(reviewId int64) (*Review, error) { + row := database.stmt.getReview.QueryRow(reviewId) + + review := &Review{} + err := row.Scan( + &review.ID, + &review.FileId, + &review.UserId, + &review.CreatedAt, + &review.UpdatedAt, + &review.Content) + if err != nil { + return nil, err + } + + return review, nil +} + +func (database *Database) UpdateReview(review *Review) error { + _, err := database.stmt.updateReview.Exec( + review.Content, + review.UpdatedAt, + review.ID) + return err +} diff --git a/pkg/database/sql_stmt.go b/pkg/database/sql_stmt.go index 6beae4c..4dce78e 100644 --- a/pkg/database/sql_stmt.go +++ b/pkg/database/sql_stmt.go @@ -218,6 +218,10 @@ WHERE reviews.file_id = ? ORDER BY reviews.created_at ;` +var getReviewQuery = `SELECT id, file_id, user_id, created_at, updated_at, content FROM reviews WHERE id = ? LIMIT 1;` + +var updateReviewQuery = `UPDATE reviews SET content = ?, updated_at = ? WHERE id = ?;` + type Stmt struct { initFilesTable *sql.Stmt initFoldersTable *sql.Stmt @@ -260,6 +264,8 @@ type Stmt struct { updateFoldername *sql.Stmt insertReview *sql.Stmt getReviewsOnFile *sql.Stmt + getReview *sql.Stmt + updateReview *sql.Stmt } func NewPreparedStatement(sqlConn *sql.DB) (*Stmt, error) { @@ -576,5 +582,17 @@ func NewPreparedStatement(sqlConn *sql.DB) (*Stmt, error) { return nil, err } + // init getReview + stmt.getReview, err = sqlConn.Prepare(getReviewQuery) + if err != nil { + return nil, err + } + + // init updateReview + stmt.updateReview, err = sqlConn.Prepare(updateReviewQuery) + if err != nil { + return nil, err + } + return stmt, err } diff --git a/web/src/App.js b/web/src/App.js index cfd12b9..0129e5f 100644 --- a/web/src/App.js +++ b/web/src/App.js @@ -12,6 +12,7 @@ import Login from "./component/Login"; import Register from "./component/Register"; import Tags from "./component/Tags"; import EditTag from "./component/EditTag"; +import EditReview from "./component/EditReview"; import AudioPlayer from "./component/AudioPlayer"; import UserStatus from "./component/UserStatus"; import ReviewPage from "./component/ReviewPage"; @@ -83,6 +84,10 @@ function App() { path="/manage/tags/:id" element={} /> + } + /> } @@ -93,7 +98,7 @@ function App() { /> } + element={} /> diff --git a/web/src/component/EditReview.js b/web/src/component/EditReview.js new file mode 100644 index 0000000..e335a7d --- /dev/null +++ b/web/src/component/EditReview.js @@ -0,0 +1,75 @@ +import { useEffect, useState } from "react"; +import { useParams, useNavigate } from "react-router"; + +function SingleReview() { + let params = useParams(); + let navigate = useNavigate(); + + const [review, setReview] = useState({ + id: "", + user_id: "", + file_id: "", + content: "", + created_at: "", + updated_at: "", + }); + + function refresh() { + fetch("/api/v1/get_review", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + id: parseInt(params.id), + }), + }) + .then((response) => response.json()) + .then((data) => { + if (data.error) { + alert(data.error); + } else { + setReview(data.review); + } + }); + } + + function save() { + fetch("/api/v1/update_review", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + id: parseInt(params.id), + content: review.content, + }), + }) + .then((response) => response.json()) + .then((data) => { + if (data.error) { + alert(data.error); + } else { + alert("Review updated!"); + navigate(-1); + } + }); + } + + useEffect(() => { + refresh(); + }, []); + + return ( +
+

Edit Review

+ + +
+ ); +} + +export default SingleReview; diff --git a/web/src/component/ReviewPage.js b/web/src/component/ReviewPage.js index 2e3d74c..817bf05 100644 --- a/web/src/component/ReviewPage.js +++ b/web/src/component/ReviewPage.js @@ -1,10 +1,11 @@ import { useState, useEffect } from "react"; -import { useParams } from "react-router"; +import { useParams, useNavigate } from "react-router"; import { Link } from "react-router-dom"; import { convertIntToDateTime } from "./Common"; -function ReviewPage() { +function ReviewPage(props) { let params = useParams(); + let navigate = useNavigate(); const [newReview, setNewReview] = useState(""); const [reviews, setReviews] = useState([]); @@ -61,10 +62,22 @@ function ReviewPage() { {reviews.map((review) => (

- @{review.user.username} wrote on{" "} - {convertIntToDateTime(review.created_at)}{" "} + + @{review.user.username} + {" "} + wrote on {convertIntToDateTime(review.created_at)}{" "}

{review.content}

+ {(props.user.role === 1 || review.user.id === props.user.id) && + props.user.role != 0 && ( + + )}
))}