Backend (prs.go):
Update — PATCH /{prID} edits title and/or body, validates title non-empty, returns prWithReviewers
Reopen — POST /{prID}/reopen transitions closed → open, fires webhook
Close now returns prWithReviewers and fires a webhook
Merge already existed; no changes needed
Frontend — PRDetailPage.tsx full rewrite:
Inline title editing — pencil icon (visible to author/admin when open), Enter to save, Esc to cancel
Inline body editing — same pattern in the description panel
Merge sidebar — radio buttons for allowed strategies (fetched from repo's merge strategy settings), "Merge pull request" button in Bitbucket purple, "Close without merging" below it
Status banner — merged (purple) or closed (grey) with the date, shown below the description
File list — scrollable +N −N table above the diff viewer showing all changed files with addition/deletion counts
Reopen button — appears in the sidebar when the PR is closed
Reviewers panel — lists assigned reviewers with avatars/initials
Details panel — from/into branches, opened date, last updated
Quick links — back to all PRs, open new PR
PRsPage.tsx — now shows real data:
Two tabs: "My pull requests" and "Awaiting my review" (with live counts from dashboard)
Per-repo quick links at the bottom showing open PR count badges
This commit is contained in:
@@ -174,7 +174,70 @@ func (h *PRHandler) Close(w http.ResponseWriter, r *http.Request) {
|
||||
jsonError(w, "could not close pull request", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
jsonOK(w, pr)
|
||||
go FireWebhooks(h.db, pr.RepoID, "pull_request", map[string]interface{}{
|
||||
"action": "closed",
|
||||
"pullRequest": map[string]interface{}{"id": pr.ID, "title": pr.Title},
|
||||
})
|
||||
jsonOK(w, prWithReviewers(h.db, pr))
|
||||
}
|
||||
|
||||
func (h *PRHandler) Reopen(w http.ResponseWriter, r *http.Request) {
|
||||
pr, ok := h.lookupPR(w, r)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if pr.Status != models.PRStatusClosed {
|
||||
jsonError(w, "pull request is not closed", http.StatusConflict)
|
||||
return
|
||||
}
|
||||
|
||||
pr.Status = models.PRStatusOpen
|
||||
if _, err := h.db.ID(pr.ID).Cols("status").Update(pr); err != nil {
|
||||
jsonError(w, "could not reopen pull request", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
go FireWebhooks(h.db, pr.RepoID, "pull_request", map[string]interface{}{
|
||||
"action": "reopened",
|
||||
"pullRequest": map[string]interface{}{"id": pr.ID, "title": pr.Title},
|
||||
})
|
||||
jsonOK(w, prWithReviewers(h.db, pr))
|
||||
}
|
||||
|
||||
func (h *PRHandler) Update(w http.ResponseWriter, r *http.Request) {
|
||||
pr, ok := h.lookupPR(w, r)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
var body struct {
|
||||
Title *string `json:"title"`
|
||||
Body *string `json:"body"`
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
|
||||
jsonError(w, "invalid request body", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var cols []string
|
||||
if body.Title != nil {
|
||||
if *body.Title == "" {
|
||||
jsonError(w, "title cannot be empty", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
pr.Title = *body.Title
|
||||
cols = append(cols, "title")
|
||||
}
|
||||
if body.Body != nil {
|
||||
pr.Body = *body.Body
|
||||
cols = append(cols, "body")
|
||||
}
|
||||
if len(cols) > 0 {
|
||||
if _, err := h.db.ID(pr.ID).Cols(cols...).Update(pr); err != nil {
|
||||
jsonError(w, "could not update pull request", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
jsonOK(w, prWithReviewers(h.db, pr))
|
||||
}
|
||||
|
||||
func (h *PRHandler) repoIDFromURL(w http.ResponseWriter, r *http.Request) (int64, bool) {
|
||||
|
||||
@@ -133,8 +133,10 @@ func New(cfg *config.Config, engine *xorm.Engine, store sessions.Store, staticFi
|
||||
r.Get("/", prH.List)
|
||||
r.With(csrf).Post("/", prH.Create)
|
||||
r.Get("/{prID}", prH.Get)
|
||||
r.With(csrf).Patch("/{prID}", prH.Update)
|
||||
r.With(csrf).Post("/{prID}/merge", prH.Merge)
|
||||
r.With(csrf).Post("/{prID}/close", prH.Close)
|
||||
r.With(csrf).Post("/{prID}/reopen", prH.Reopen)
|
||||
})
|
||||
r.Route("/issues", func(r chi.Router) {
|
||||
r.Get("/", issueH.List)
|
||||
|
||||
Reference in New Issue
Block a user