feat: draft/online post field #26

This commit is contained in:
Florian Sylvain
2024-06-14 16:40:01 +02:00
parent 0759ab6d70
commit c69876a704
11 changed files with 111 additions and 13 deletions
@@ -11,6 +11,7 @@ type Post struct {
Title string Title string
Body string Body string
Images []*Image `gorm:"many2many:post_images;"` Images []*Image `gorm:"many2many:post_images;"`
IsOnline bool `gorm:"not_null;default:false"`
CreatedAt time.Time `gorm:"autoCreateTime"` CreatedAt time.Time `gorm:"autoCreateTime"`
UpdatedAt time.Time `gorm:"autoUpdateTime"` UpdatedAt time.Time `gorm:"autoUpdateTime"`
} }
+18 -1
View File
@@ -16,7 +16,7 @@ func NewPostRepository(db *gorm.DB) *PostRepository {
} }
func mapPostToDomain(post entity.Post) domain.Post { func mapPostToDomain(post entity.Post) domain.Post {
return domain.FromDb(post.ID, post.Title, post.Body, post.Images, post.CreatedAt, post.UpdatedAt) return domain.FromDb(post.ID, post.Title, post.Body, post.Images, post.IsOnline, post.CreatedAt, post.UpdatedAt)
} }
func (a *PostRepository) Get(id uint32) (domain.Post, error) { func (a *PostRepository) Get(id uint32) (domain.Post, error) {
@@ -87,6 +87,7 @@ func (a *PostRepository) UpdateBody(id uint32, body string) (domain.Post, error)
localPost.Title, localPost.Title,
localPost.Body, localPost.Body,
localPost.Images, localPost.Images,
localPost.IsOnline,
localPost.CreatedAt, localPost.CreatedAt,
localPost.UpdatedAt, localPost.UpdatedAt,
) )
@@ -94,6 +95,22 @@ func (a *PostRepository) UpdateBody(id uint32, body string) (domain.Post, error)
return newPost, nil return newPost, nil
} }
func (a *PostRepository) UpdateIsOnline(id uint32, isOnline bool) (domain.Post, error) {
var localPost entity.Post
err := a.db.Model(&entity.Post{}).First(&localPost, id).Error
if err != nil {
return domain.Post{}, err
}
localPost.IsOnline = isOnline
err = a.db.Save(&localPost).Error
if err != nil {
return domain.Post{}, err
}
return mapPostToDomain(localPost), nil
}
func (a *PostRepository) Delete(id uint32) error { func (a *PostRepository) Delete(id uint32) error {
return a.db.Delete(&entity.Post{}, id).Error return a.db.Delete(&entity.Post{}, id).Error
} }
@@ -23,6 +23,9 @@
</li> </li>
</ul> </ul>
<ul class="navbar-nav"> <ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link text-body-tertiary text-decoration-underline" href="https://www.paypal.com/donate/?hosted_button_id=S6LUFUYK84CYY">support me!</a>
</li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link disconnect-link" href="/logout">Log out</a> <a class="nav-link disconnect-link" href="/logout">Log out</a>
</li> </li>
@@ -5,6 +5,7 @@
{{.Head}} {{.Head}}
<style> <style>
.btn-outline-info:hover > *,
.btn-outline-danger:hover > * { .btn-outline-danger:hover > * {
fill: #fff !important; fill: #fff !important;
transition: fill ease-in-out 50ms; transition: fill ease-in-out 50ms;
@@ -22,12 +23,13 @@
<th scope="col">Name</th> <th scope="col">Name</th>
<th scope="col">Creation Date</th> <th scope="col">Creation Date</th>
<th scope="col">Edition Date</th> <th scope="col">Edition Date</th>
<th scope="col">Status</th>
<th scope="col">Actions</th> <th scope="col">Actions</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr> <tr>
<td colspan="4" class="text-center"> <td colspan="5" class="text-center">
<form action="post/create" method="get"> <form action="post/create" method="get">
<button class="btn btn-sm btn-link w-100 h-100" type="submit">Create a new post...</button> <button class="btn btn-sm btn-link w-100 h-100" type="submit">Create a new post...</button>
</form> </form>
@@ -38,12 +40,26 @@
<td>{{ $post.Title }}</td> <td>{{ $post.Title }}</td>
<td class="date">{{ $post.CreatedAt }}</td> <td class="date">{{ $post.CreatedAt }}</td>
<td class="date">{{ $post.UpdatedAt }}</td> <td class="date">{{ $post.UpdatedAt }}</td>
<td>{{ if $post.IsOnline }}🟢 Online{{ else }}🟠 Offline{{ end }}</td>
<td> <td>
<a href="/post/{{ $post.ID }}/edit" class="btn btn-secondary btn-sm"> <a href="/post/{{ $post.ID }}/edit" class="btn btn-outline-primary btn-sm">
<svg width="12px" height="12px" fill="currentColor"> <svg width="12px" height="12px" fill="currentColor">
<use xlink:href="/static/bootstrap-icons.svg#pen"/> <use xlink:href="/static/bootstrap-icons.svg#pen"/>
</svg> </svg>
</a> </a>
{{ if $post.IsOnline }}
<a href="/post/{{ $post.ID }}/unpublish" class="btn btn-outline-info btn-sm">
<svg width="12px" height="12px" fill="currentColor">
<use xlink:href="/static/bootstrap-icons.svg#eye-slash"/>
</svg>
</a>
{{ else }}
<a href="/post/{{ $post.ID }}/publish" class="btn btn-outline-info btn-sm">
<svg width="12px" height="12px" fill="currentColor">
<use xlink:href="/static/bootstrap-icons.svg#eye"/>
</svg>
</a>
{{ end }}
<a href="/post/{{ $post.Title }}/delete" class="btn btn-outline-danger btn-sm" data-bs-toggle="modal" <a href="/post/{{ $post.Title }}/delete" class="btn btn-outline-danger btn-sm" data-bs-toggle="modal"
data-bs-target="#{{ $post.ID }}"> data-bs-target="#{{ $post.ID }}">
<svg width=" 12px" height="12px" fill="currentColor"> <svg width=" 12px" height="12px" fill="currentColor">
@@ -60,7 +76,8 @@
aria-label="Close"></button> aria-label="Close"></button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<p>You are about to delete the post <b>« {{ $post.Title }} »</b>, this is definitive, are you sure?</p> <p>You are about to delete the post <b>« {{ $post.Title }} »</b>, this is definitive,
are you sure?</p>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Cancel <button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Cancel
+3
View File
@@ -148,6 +148,9 @@ func NewPageRouter() http.Handler {
r.Post("/post/{id}/image/create", postImage) r.Post("/post/{id}/image/create", postImage)
r.Get("/post/{id}/publish", GetPostPublishPage)
r.Get("/post/{id}/unpublish", GetPostUnpublishPage)
r.Get("/integration", GetPageIntegration) r.Get("/integration", GetPageIntegration)
}) })
+42
View File
@@ -0,0 +1,42 @@
package api
import (
"errors"
"github.com/go-chi/chi/v5"
"net/http"
"strconv"
)
func updateIsOnline(postId string, isOnline bool) (error, int) {
postIdInt, err := strconv.Atoi(postId)
if err != nil {
return errors.New("the server expects the ID to be in the format of an unsigned 32-bit integer (uint32)"), http.StatusBadRequest
}
_, err = Container.UpdatePostUseCase.UpdateIsOnline(uint32(postIdInt), isOnline)
if err != nil {
return errors.New("the requested resource, identified by its unique ID, could not be found on the server"), http.StatusNotFound
}
return nil, http.StatusOK
}
func GetPostUnpublishPage(w http.ResponseWriter, r *http.Request) {
postId := chi.URLParam(r, "id")
err, statusCode := updateIsOnline(postId, false)
if err != nil {
http.Error(w, err.Error(), statusCode)
return
}
http.Redirect(w, r, "/post", http.StatusSeeOther)
}
func GetPostPublishPage(w http.ResponseWriter, r *http.Request) {
postId := chi.URLParam(r, "id")
err, statusCode := updateIsOnline(postId, true)
if err != nil {
http.Error(w, err.Error(), statusCode)
return
}
http.Redirect(w, r, "/post", http.StatusSeeOther)
}
+1
View File
@@ -13,6 +13,7 @@ func GetPostsPage(w http.ResponseWriter, _ *http.Request) {
formattedPosts = append(formattedPosts, map[string]interface{}{ formattedPosts = append(formattedPosts, map[string]interface{}{
"ID": post.ID, "ID": post.ID,
"Title": post.Title, "Title": post.Title,
"IsOnline": post.IsOnline,
"CreatedAt": post.CreatedAt.Format("2006-01-02 15:04:05"), "CreatedAt": post.CreatedAt.Format("2006-01-02 15:04:05"),
"UpdatedAt": post.UpdatedAt.Format("2006-01-02 15:04:05"), "UpdatedAt": post.UpdatedAt.Format("2006-01-02 15:04:05"),
}) })
+15 -9
View File
@@ -1,6 +1,7 @@
package api package api
import ( import (
"GoCMS/domain/post"
"GoCMS/useCases" "GoCMS/useCases"
"encoding/json" "encoding/json"
"github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5"
@@ -22,33 +23,33 @@ func getPost(w http.ResponseWriter, r *http.Request) {
return return
} }
post, err := Container.GetPostUseCase.GetPost(uint32(id)) localPost, err := Container.GetPostUseCase.GetPost(uint32(id))
if err != nil { if err != nil {
http.Error(w, "The requested resource, identified by its unique ID, could not be found on the server.", http.StatusNotFound) http.Error(w, "The requested resource, identified by its unique ID, could not be found on the server.", http.StatusNotFound)
return return
} }
postJson, _ := json.Marshal(post) postJson, _ := json.Marshal(localPost)
_, _ = w.Write(postJson) _, _ = w.Write(postJson)
} }
func postPost(w http.ResponseWriter, r *http.Request) { func postPost(w http.ResponseWriter, r *http.Request) {
var post PostPost var localPost PostPost
err := json.NewDecoder(r.Body).Decode(&post) err := json.NewDecoder(r.Body).Decode(&localPost)
if err != nil { if err != nil {
http.Error(w, bodyErrorMessage, http.StatusBadRequest) http.Error(w, bodyErrorMessage, http.StatusBadRequest)
return return
} }
err = validate.Struct(post) err = validate.Struct(localPost)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest) http.Error(w, err.Error(), http.StatusBadRequest)
return return
} }
createdPost, err := Container.CreatePostUseCase.CreatePost(useCases.CreatePostCommand{ createdPost, err := Container.CreatePostUseCase.CreatePost(useCases.CreatePostCommand{
Title: post.Title, Title: localPost.Title,
Body: post.Body, Body: localPost.Body,
}) })
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
@@ -61,8 +62,13 @@ func postPost(w http.ResponseWriter, r *http.Request) {
func listPosts(w http.ResponseWriter, _ *http.Request) { func listPosts(w http.ResponseWriter, _ *http.Request) {
posts := Container.ListPostsUseCase.ListPosts() posts := Container.ListPostsUseCase.ListPosts()
postsJson, _ := json.Marshal(posts) onlinePosts := make([]post.Post, 0)
for _, localPost := range posts {
if localPost.IsOnline {
onlinePosts = append(onlinePosts, localPost)
}
}
postsJson, _ := json.Marshal(onlinePosts)
_, _ = w.Write(postsJson) _, _ = w.Write(postsJson)
} }
+1
View File
@@ -10,6 +10,7 @@ type IPostRepository interface {
GetAll() []post.Post GetAll() []post.Post
Create(post post.Post) (post.Post, error) Create(post post.Post) (post.Post, error)
UpdateBody(id uint32, body string) (post.Post, error) UpdateBody(id uint32, body string) (post.Post, error)
UpdateIsOnline(id uint32, isOnline bool) (post.Post, error)
Delete(id uint32) error Delete(id uint32) error
AddImage(postId uint32, imageId uint32) error AddImage(postId uint32, imageId uint32) error
} }
+3
View File
@@ -11,6 +11,7 @@ type Post struct {
Title string `json:"title"` Title string `json:"title"`
Body string `json:"body"` Body string `json:"body"`
Images []*domain.Image `json:"images"` Images []*domain.Image `json:"images"`
IsOnline bool `json:"is_online"`
CreatedAt time.Time `json:"created_at"` CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"` UpdatedAt time.Time `json:"updated_at"`
} }
@@ -30,6 +31,7 @@ func FromDb(
title string, title string,
body string, body string,
images []*entity.Image, images []*entity.Image,
isOnline bool,
createdAt time.Time, createdAt time.Time,
updatedAt time.Time, updatedAt time.Time,
) Post { ) Post {
@@ -49,6 +51,7 @@ func FromDb(
Title: title, Title: title,
Body: body, Body: body,
Images: domainImages, Images: domainImages,
IsOnline: isOnline,
CreatedAt: createdAt, CreatedAt: createdAt,
UpdatedAt: updatedAt, UpdatedAt: updatedAt,
} }
+4
View File
@@ -23,3 +23,7 @@ func (g *UpdatePostUseCase) UpdateBody(id uint32, body string) (post.Post, error
func (g *UpdatePostUseCase) AddImage(postId uint32, imageId uint32) error { func (g *UpdatePostUseCase) AddImage(postId uint32, imageId uint32) error {
return g.postRepository.AddImage(postId, imageId) return g.postRepository.AddImage(postId, imageId)
} }
func (g *UpdatePostUseCase) UpdateIsOnline(id uint32, isOnline bool) (post.Post, error) {
return g.postRepository.UpdateIsOnline(id, isOnline)
}