feat(router): delete project route and method
This commit is contained in:
@@ -15,18 +15,34 @@ func (router *router) projects(w http.ResponseWriter, r *http.Request) {
|
||||
router.assert.NotNil(w)
|
||||
router.assert.NotNil(r)
|
||||
|
||||
id := r.PathValue("id")
|
||||
if id != "" {
|
||||
router.getProject(w, r)
|
||||
return
|
||||
}
|
||||
switch getMethod(r) {
|
||||
case http.MethodGet, http.MethodHead:
|
||||
if id := r.PathValue("id"); id != "" {
|
||||
router.getProject(w, r)
|
||||
} else {
|
||||
router.listProjects(w, r)
|
||||
}
|
||||
|
||||
if r.Method == http.MethodGet {
|
||||
router.listProjects(w, r)
|
||||
return
|
||||
}
|
||||
case http.MethodPost:
|
||||
router.createProject(w, r)
|
||||
|
||||
router.createProject(w, r)
|
||||
case http.MethodDelete:
|
||||
if id := r.PathValue("id"); id != "" {
|
||||
router.deleteProject(w, r)
|
||||
} else {
|
||||
exception.
|
||||
BadRequest(errors.New(`missing "id" path value`)).
|
||||
ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
default:
|
||||
exception.MethodNotAllowed([]string{
|
||||
http.MethodHead,
|
||||
http.MethodGet,
|
||||
http.MethodPost,
|
||||
http.MethodDelete,
|
||||
}).ServeHTTP(w, r)
|
||||
}
|
||||
}
|
||||
|
||||
func (router *router) createProject(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -34,7 +50,7 @@ func (router *router) createProject(w http.ResponseWriter, r *http.Request) {
|
||||
router.assert.NotNil(r)
|
||||
router.assert.NotNil(router.service)
|
||||
|
||||
if r.Method != http.MethodPost {
|
||||
if getMethod(r) != http.MethodPost {
|
||||
exception.
|
||||
MethodNotAllowed([]string{http.MethodPost}).
|
||||
ServeHTTP(w, r)
|
||||
@@ -58,7 +74,7 @@ func (router *router) getProject(w http.ResponseWriter, r *http.Request) {
|
||||
router.assert.NotNil(router.service)
|
||||
router.assert.NotNil(router.templates)
|
||||
|
||||
if r.Method != http.MethodGet && r.Method != http.MethodHead {
|
||||
if getMethod(r) != http.MethodGet && getMethod(r) != http.MethodHead {
|
||||
exception.
|
||||
MethodNotAllowed([]string{http.MethodGet, http.MethodHead}).
|
||||
ServeHTTP(w, r)
|
||||
@@ -97,6 +113,13 @@ func (router *router) listProjects(w http.ResponseWriter, r *http.Request) {
|
||||
router.assert.NotNil(router.service)
|
||||
router.assert.NotNil(router.templates)
|
||||
|
||||
if getMethod(r) != http.MethodGet && getMethod(r) != http.MethodHead {
|
||||
exception.
|
||||
MethodNotAllowed([]string{http.MethodGet, http.MethodHead}).
|
||||
ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
ps, err := router.service.ListProjects()
|
||||
if err != nil {
|
||||
exception.InternalServerError(err).ServeHTTP(w, r)
|
||||
@@ -117,3 +140,42 @@ func (router *router) listProjects(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (router *router) deleteProject(w http.ResponseWriter, r *http.Request) {
|
||||
router.assert.NotNil(w)
|
||||
router.assert.NotNil(r)
|
||||
router.assert.NotNil(router.service)
|
||||
router.assert.NotNil(router.templates)
|
||||
|
||||
if getMethod(r) != http.MethodDelete {
|
||||
exception.
|
||||
MethodNotAllowed([]string{http.MethodDelete}).
|
||||
ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
id := r.PathValue("id")
|
||||
if id == "" {
|
||||
exception.
|
||||
BadRequest(fmt.Errorf(`a valid path value of "id" must be provided`)).
|
||||
ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
err := router.service.DeleteProject(id)
|
||||
if err != nil {
|
||||
exception.InternalServerError(err).ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
err = router.templates.ExecuteTemplate(w, "partials-status", map[string]any{
|
||||
"StatusCode": http.StatusOK,
|
||||
"Message": fmt.Sprintf("Project %q successfully deleted", id),
|
||||
"Redirect": "/dashboard/",
|
||||
"RedirectMessage": "Go back to dashboard",
|
||||
})
|
||||
if err != nil {
|
||||
exception.InternalServerError(err).ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"io/fs"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"forge.capytal.company/capytalcode/project-comicverse/service"
|
||||
"forge.capytal.company/capytalcode/project-comicverse/templates"
|
||||
@@ -117,3 +118,16 @@ func (router *router) dashboard(w http.ResponseWriter, r *http.Request) {
|
||||
exception.InternalServerError(err).ServeHTTP(w, r)
|
||||
}
|
||||
}
|
||||
|
||||
func getMethod(r *http.Request) string {
|
||||
if r.Method == http.MethodGet || r.Method == http.MethodHead {
|
||||
return r.Method
|
||||
}
|
||||
|
||||
m := r.FormValue("x-method")
|
||||
if m == "" {
|
||||
return r.Method
|
||||
}
|
||||
|
||||
return strings.ToUpper(m)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
{{template "layout-page-start" (args "Title" "Dashboard")}}
|
||||
<main class="h-full w-full justify-center px-5 py-10 align-middle">
|
||||
{{if and (ne . nil) (ne (len .) 0)}}
|
||||
<section class="flex h-64 flex-col gap-5 bg-red-500">
|
||||
<section class="flex h-64 flex-col gap-5">
|
||||
<div class="flex justify-between">
|
||||
<h2 class="text-2xl">Projects</h2>
|
||||
<form action="/projects/" method="post">
|
||||
@@ -11,13 +11,19 @@
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="grid h-full grid-flow-col grid-rows-1 overflow-scroll gap-5">
|
||||
<div class="grid h-full grid-flow-col grid-rows-1 justify-start gap-5 overflow-scroll">
|
||||
{{range .}}
|
||||
<a href="/projects/{{.ID}}" class="w-38 grid h-full grid-rows-2 bg-green-500">
|
||||
<a href="/projects/{{.ID}}" class="w-38 grid h-full grid-rows-2 bg-slate-500">
|
||||
<div class="bg-blue-500 p-2">Image</div>
|
||||
<div class="p-2">
|
||||
<h3>{{.Title}}</h3>
|
||||
<p>{{.ID}}</p>
|
||||
<form action="/projects/{{.ID}}" method="post">
|
||||
<input type="hidden" name="x-method" value="delete">
|
||||
<button class="rounded-full bg-red-700 p-1 px-3 text-sm text-slate-100">
|
||||
Delete
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</a>
|
||||
{{end}}
|
||||
|
||||
17
templates/partials/status.html
Normal file
17
templates/partials/status.html
Normal file
@@ -0,0 +1,17 @@
|
||||
{{define "partials-status"}}
|
||||
{{template "layout-page-start" (args "Title" .Title)}}
|
||||
<main class="justify-center align-middle w-full h-full">
|
||||
<div class="text-center">
|
||||
<h1>{{.StatusCode}}</h1>
|
||||
<p>{{.Message}}</p>
|
||||
<a href="{{.Redirect}}">
|
||||
{{if .RedirectMessage}}
|
||||
{{.RedirectMessage}}
|
||||
{{else}}
|
||||
Go back
|
||||
{{end}}
|
||||
</a>
|
||||
</div>
|
||||
</main>
|
||||
{{template "layout-page-end"}}
|
||||
{{end}}
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
patterns = []string{"*.html", "layouts/*.html"}
|
||||
patterns = []string{"*.html", "layouts/*.html", "partials/*.html"}
|
||||
functions = template.FuncMap{
|
||||
"args": func(pairs ...any) (map[string]any, error) {
|
||||
if len(pairs)%2 != 0 {
|
||||
@@ -35,7 +35,7 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
//go:embed *.html layouts/*.html
|
||||
//go:embed *.html layouts/*.html partials/*.html
|
||||
var embedded embed.FS
|
||||
|
||||
var temps = template.Must(template.New("templates").Funcs(functions).ParseFS(embedded, patterns...))
|
||||
|
||||
Reference in New Issue
Block a user