From 2a2ea206ffb04e87bfdd08b1b3268b38977fb2b2 Mon Sep 17 00:00:00 2001
From: Pierre Jarriges <pierre.jarriges@tutanota.com>
Date: Sun, 7 Nov 2021 17:57:47 +0100
Subject: [PATCH] admin panel create update delete articles

---
 .../src/components/create-article-form.js     |  31 ++--
 admin-frontend/src/components/root.js         |   3 +-
 .../src/components/update-article-form.js     | 145 ++++++++++++++++++
 admin-frontend/src/xhr.js                     |   3 +-
 src/main.rs                                   |   1 +
 src/service/articles.rs                       |   8 +-
 6 files changed, 176 insertions(+), 15 deletions(-)

diff --git a/admin-frontend/src/components/create-article-form.js b/admin-frontend/src/components/create-article-form.js
index 792877b..c5ed71d 100644
--- a/admin-frontend/src/components/create-article-form.js
+++ b/admin-frontend/src/components/create-article-form.js
@@ -1,12 +1,13 @@
 "use strict";
 
 const { images_url } = require("../constants");
-const { fetch_post_article, fetch_article } = require("../xhr");
+const { fetch_post_article, fetch_article, fetch_update_article } = require("../xhr");
 
 class CreateArticleForm {
-    constructor() {
+    constructor(params) {
+        this.params = params || {};
         this.state = {
-            output: {
+            output: this.params.data || {
                 title: "",
                 subtitle: "",
                 category: "",
@@ -259,13 +260,24 @@ class CreateArticleForm {
             },
             onsubmit: e => {
                 e.preventDefault();
-                fetch_post_article(this.state.output)
+
+                const __fetch = this.params.data
+                    ? fetch_update_article
+                    : fetch_post_article;
+
+                __fetch(this.state.output)
                     .then(res => {
-                        const id = res.insertedId.$oid;
-                        fetch_article(id).then(article => {
-                            this.state.article_sent = article;
-                            this.refresh();
-                        }).catch(er => console.log(er));
+                        const id = res.insertedId ? res.insertedId.$oid : res._id ? res._id.$oid : undefined;
+                        if (!id) {
+                            alert("error")
+                        } else {
+                            fetch_article(id)
+                                .then(article => {
+                                    this.state.article_sent = article;
+                                    this.refresh();
+                                })
+                                .catch(er => console.log(er));
+                        }
                     })
                     .catch(err => console.log(err))
             },
@@ -296,6 +308,7 @@ class CreateArticleForm {
                         gridColumn: "1 / span 2",
                         height: "300px",
                     },
+                    value: this.state.output.body,
                     placeholder: "Article body",
                     oninput: this.handle_text_input.bind(this, "body")
                 },
diff --git a/admin-frontend/src/components/root.js b/admin-frontend/src/components/root.js
index eb81cfd..e4cf916 100644
--- a/admin-frontend/src/components/root.js
+++ b/admin-frontend/src/components/root.js
@@ -1,5 +1,6 @@
 
 const CreateArticleForm = require("./create-article-form");
+const UpdateArticleForm = require("./update-article-form");
 
 class RootComponent {
     constructor() {
@@ -18,7 +19,7 @@ class RootComponent {
             case "create":
                 return new CreateArticleForm().render();
             case "update":
-                return undefined;
+                return new UpdateArticleForm().render();
             default:
                 return undefined;
         }
diff --git a/admin-frontend/src/components/update-article-form.js b/admin-frontend/src/components/update-article-form.js
index e69de29..0acb6cc 100644
--- a/admin-frontend/src/components/update-article-form.js
+++ b/admin-frontend/src/components/update-article-form.js
@@ -0,0 +1,145 @@
+"use strict";
+
+const { fetch_article_by_title, fetch_delete_article } = require("../xhr");
+const CreateArticleForm = require("./create-article-form");
+
+class UpdateArticleForm {
+    constructor() {
+        this.state = {
+            search_article_title: "",
+            search_result: {},
+            article_to_update: {},
+        }
+    }
+
+    reset() {
+        this.state = {
+            search_article_title: "",
+            search_result: {},
+            article_to_update: {},
+        };
+
+        this.refresh();
+    }
+
+    handle_search_article() {
+        fetch_article_by_title(this.state.search_article_title)
+            .then(res => {
+                this.state.search_result = res;
+                this.state.article_to_update = {};
+                this.refresh_search_result();
+                this.refresh_update_form();
+            })
+            .catch(err => alert(err));
+    }
+
+    handle_select_result() {
+        this.state.article_to_update = { ...this.state.search_result };
+        this.refresh_update_form();
+    }
+
+    handle_delete_article() {
+        fetch_delete_article(this.state.search_result._id.$oid)
+            .then(res => {
+                alert(res);
+                this.reset();
+            })
+            .catch(err => alert(err))
+    }
+
+    render_search() {
+        return {
+            tag: "form",
+            onsubmit: e => {
+                e.preventDefault();
+                this.handle_search_article();
+            },
+            style_rules: { display: "flex", gap: "10px", width: "100%" },
+            contents: [
+                {
+                    tag: "input", type: "text", value: this.state.search_article_title,
+                    style_rules: { flex: 1 },
+                    placeholder: "Search article by title",
+                    oninput: e => this.state.search_article_title = e.target.value,
+                },
+                {
+                    tag: "input", type: "submit", value: "SEARCH"
+                }
+            ]
+        }
+    }
+
+    refresh_search_result() {
+        obj2htm.subRender(
+            this.render_search_result(),
+            document.getElementById("update-article-form-search-result"),
+            { mode: "replace" },
+        );
+    }
+
+    render_search_result() {
+        const { search_result } = this.state;
+        return {
+            tag: "div",
+            id: "update-article-form-search-result",
+            style_rules: {
+                display: "flex",
+                gap: "10px",
+                alignItems: "center"
+            },
+            contents: search_result.title ? [
+                { tag: "strong", contents: search_result.title },
+                {
+                    tag: "button", contents: "SELECT",
+                    onclick: this.handle_select_result.bind(this)
+                },
+                {
+                    tag: "button", contents: "DELETE",
+                    onclick: this.handle_delete_article.bind(this)
+                }
+            ] : []
+        }
+    }
+
+    refresh_update_form() {
+        obj2htm.subRender(
+            this.render_update_form(),
+            document.getElementById("update-article-form-container"),
+            { mode: "replace" },
+        );
+    }
+
+    render_update_form() {
+        return {
+            tag: "div",
+            id: "update-article-form-container",
+            contents: this.state.article_to_update._id
+                ? [new CreateArticleForm({ data: this.state.article_to_update }).render()]
+                : []
+        }
+    }
+
+    refresh() {
+        obj2htm.subRender(this.render(), document.getElementById("update-article-multiform"), { mode: "replace" })
+    }
+
+    render() {
+        return {
+            tag: "div",
+            id: "update-article-multiform",
+            style_rules: {
+                display: "flex",
+                flexDirection: "column",
+                gap: "20px",
+                maxWidth: "800px",
+            },
+            contents: [
+                this.render_search(),
+                this.render_search_result(),
+                this.render_update_form(),
+            ]
+        }
+    }
+}
+
+module.exports = UpdateArticleForm;
\ No newline at end of file
diff --git a/admin-frontend/src/xhr.js b/admin-frontend/src/xhr.js
index 1b77ca7..6d1e210 100644
--- a/admin-frontend/src/xhr.js
+++ b/admin-frontend/src/xhr.js
@@ -17,6 +17,7 @@ async function fetch_article_by_title(article_title) {
 
     return new Promise((resolve, reject) => {
         fetch(`/article-by-title/`, {
+            method: "POST",
             body: new URLSearchParams(form_data),
         }).then(async res => {
             if (res.status >= 400 && res.status < 600) {
@@ -90,7 +91,7 @@ async function fetch_update_article(article_data) {
 
 async function fetch_delete_article(article_id) {
     return new Promise((resolve, reject) => {
-        fetch(`/delete_article/${article_id}`, {
+        fetch(`/delete-article/${article_id}`, {
             credentials: 'include',
             method: "DELETE"
         })
diff --git a/src/main.rs b/src/main.rs
index 205600b..3b83cc9 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -88,6 +88,7 @@ async fn main() -> std::io::Result<()> {
             .service(delete_article)
             .service(get_articles_by_category)
             .service(get_article)
+            .service(get_article_by_title)
             /////////////////////////////////////////////////////////////////////////////////////////////////////////////
             // STANDARD FILES ///////////////////////////////////////////////////////////////////////////////////////////
             .service(resource("/favicon.ico").route(get().to(favicon)))
diff --git a/src/service/articles.rs b/src/service/articles.rs
index 38f7849..3d5abc2 100644
--- a/src/service/articles.rs
+++ b/src/service/articles.rs
@@ -102,8 +102,8 @@ pub async fn delete_article(
         .find_one_and_delete(doc! {"_id": &article_id}, None)
         .await
     {
-        Ok(_) => HttpResponse::Accepted().finish(),
-        Err(_) => HttpResponse::InternalServerError().finish(),
+        Ok(_) => HttpResponse::Accepted().body("Article was deleted"),
+        Err(e) => HttpResponse::InternalServerError().body(&format!("{:?}", e)),
     }
 }
 
@@ -157,7 +157,7 @@ pub async fn get_article(app_state: Data<AppState>, article_id: Path<String>) ->
     }
 }
 
-#[get("/article-by-title")]
+#[post("/article-by-title")]
 pub async fn get_article_by_title(
     app_state: Data<AppState>,
     form_data: Form<ArticleTitleFormData>,
@@ -555,7 +555,7 @@ mod test_articles {
 
         let req = test::TestRequest::with_uri("/article-by-title")
             .header("Accept", "application/json")
-            .method(Method::GET)
+            .method(Method::POST)
             .set_form(&ArticleTitleFormData {
                 title: article_title.to_owned(),
             })
-- 
GitLab