feat(router): delete project route and method

This commit is contained in:
Guz
2025-03-19 11:28:46 -03:00
parent b93ff0512f
commit 845d4b40c3
5 changed files with 116 additions and 17 deletions

View File

@@ -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
}
}

View File

@@ -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)
}

View File

@@ -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}}

View 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}}

View File

@@ -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...))