From 50588da322d3193bd03d1fc349f5690252626a25 Mon Sep 17 00:00:00 2001
From: ditatompel <ditatompel@gmail.com>
Date: Tue, 7 May 2024 23:23:31 +0700
Subject: [PATCH] Edit prober name action

---
 .../routes/(loggedin)/app/prober/+page.svelte | 42 ++++++++++++++++++-
 .../(loggedin)/app/prober/api-handler.js      | 13 ++++++
 handler/response.go                           | 25 +++++++++++
 handler/routes.go                             |  1 +
 internal/repo/prober.go                       | 19 ++++++---
 5 files changed, 92 insertions(+), 8 deletions(-)

diff --git a/frontend/src/routes/(loggedin)/app/prober/+page.svelte b/frontend/src/routes/(loggedin)/app/prober/+page.svelte
index 791b8df..51567cb 100644
--- a/frontend/src/routes/(loggedin)/app/prober/+page.svelte
+++ b/frontend/src/routes/(loggedin)/app/prober/+page.svelte
@@ -1,7 +1,7 @@
 <script>
 	import { DataHandler } from '@vincjo/datatables/remote';
 	import { format, formatDistance } from 'date-fns';
-	import { loadData, deleteData } from './api-handler';
+	import { loadData, deleteData, editProber } from './api-handler';
 	import { onMount, onDestroy } from 'svelte';
 	import { getModalStore, getToastStore } from '@skeletonlabs/skeleton';
 	import {
@@ -14,6 +14,40 @@
 	const modalStore = getModalStore();
 	const toastStore = getToastStore();
 
+	/**
+	 * @param {string} proberId
+	 * @param {string} proberName
+	 */
+	function showEditModal(proberId, proberName) {
+		/** @type {import('@skeletonlabs/skeleton').ModalSettings} */
+		const modal = {
+			type: 'prompt',
+			// Data
+			title: 'Enter Name',
+			body: 'Enter a new name for the prober',
+			value: proberName,
+			valueAttr: { type: 'text', minlength: 3, maxlength: 50, required: true },
+			response: (r) => {
+				editProber(proberId, r)
+					.then((res) => {
+						if (res.status !== 'ok') {
+							toastStore.trigger({ message: 'Failed to edit prober' });
+						} else {
+							toastStore.trigger({
+								message: 'Prober edited',
+								background: 'variant-filled-success'
+							});
+							handler.invalidate();
+						}
+					})
+					.catch(() => {
+						toastStore.trigger({ message: 'Failed to edit prober' });
+					});
+			}
+		};
+		modalStore.trigger(modal);
+	}
+
 	/** @param {number} id */
 	const handleDelete = (id) => {
 		modalStore.trigger({
@@ -143,7 +177,11 @@
 						<td>
 							{row.id}
 							<button
-								class="variant-filled-error btn btn-sm mr-1"
+								class="variant-filled-secondary btn btn-sm mr-1"
+								on:click={() => showEditModal(row.id, row.name)}>Edit</button
+							>
+							<button
+								class="variant-filled-error btn btn-sm"
 								name="delete_{row.id}"
 								on:click={() => {
 									handleDelete(row.id);
diff --git a/frontend/src/routes/(loggedin)/app/prober/api-handler.js b/frontend/src/routes/(loggedin)/app/prober/api-handler.js
index 4b9b683..bd40b38 100644
--- a/frontend/src/routes/(loggedin)/app/prober/api-handler.js
+++ b/frontend/src/routes/(loggedin)/app/prober/api-handler.js
@@ -17,6 +17,19 @@ export const deleteData = async (id) => {
 	return json;
 };
 
+export const editProber = async (id, name) => {
+	const response = await fetch(apiUri(`/api/v1/prober/${id}`), {
+		method: 'PATCH',
+		credentials: 'include',
+		headers: {
+			'Content-Type': 'application/json'
+		},
+		body: JSON.stringify({ name })
+	});
+	const json = await response.json();
+	return json;
+};
+
 const getParams = ({ pageNumber, rowsPerPage, sort, filters }) => {
 	let params = `page=${pageNumber}&limit=${rowsPerPage}`;
 
diff --git a/handler/response.go b/handler/response.go
index 3d4a8df..4b572c2 100644
--- a/handler/response.go
+++ b/handler/response.go
@@ -104,6 +104,31 @@ func Prober(c *fiber.Ctx) error {
 			"message": "Success",
 			"data":    nil,
 		})
+	} else if c.Method() == "PATCH" {
+		payload := repo.Prober{}
+		if err := c.BodyParser(&payload); err != nil {
+			return c.Status(fiber.StatusUnprocessableEntity).JSON(fiber.Map{
+				"status":  "error",
+				"message": err.Error(),
+				"data":    nil,
+			})
+		}
+		if payload.Name == "" {
+			return c.Status(fiber.StatusUnprocessableEntity).JSON(fiber.Map{
+				"status":  "error",
+				"message": "Please fill prober name",
+				"data":    nil,
+			})
+		}
+		id, _ := strconv.Atoi(c.Params("id"))
+		err := proberRepo.Update(id, payload.Name)
+		if err != nil {
+			return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
+				"status":  "error",
+				"message": err.Error(),
+				"data":    nil,
+			})
+		}
 	}
 
 	query := repo.ProbersQueryParams{
diff --git a/handler/routes.go b/handler/routes.go
index 3e00302..cfa493f 100644
--- a/handler/routes.go
+++ b/handler/routes.go
@@ -14,6 +14,7 @@ func V1Api(app *fiber.App) {
 
 	v1.Get("/prober", Prober)
 	v1.Post("/prober", Prober)
+	v1.Patch("/prober/:id", CookieProtected, Prober)
 	v1.Delete("/prober/:id", CookieProtected, Prober)
 	v1.Get("/nodes", MoneroNodes)
 	v1.Post("/nodes", AddNode)
diff --git a/internal/repo/prober.go b/internal/repo/prober.go
index ed2089c..2f07e12 100644
--- a/internal/repo/prober.go
+++ b/internal/repo/prober.go
@@ -12,6 +12,7 @@ import (
 
 type ProberRepository interface {
 	AddProber(name string) error
+	Update(id int, name string) error
 	Probers(q ProbersQueryParams) (Probers, error)
 	CheckApi(key string) (Prober, error)
 	Delete(id int) error
@@ -59,6 +60,18 @@ func (repo *ProberRepo) AddProber(name string) error {
 	return nil
 }
 
+func (repo *ProberRepo) Update(id int, name string) error {
+	query := `UPDATE tbl_prober SET name = ? WHERE id = ?`
+	_, err := repo.db.Exec(query, name, id)
+	return err
+}
+
+func (repo *ProberRepo) Delete(id int) error {
+	query := `DELETE FROM tbl_prober WHERE id = ?`
+	_, err := repo.db.Exec(query, id)
+	return err
+}
+
 func (repo *ProberRepo) Probers(q ProbersQueryParams) (Probers, error) {
 	queryParams := []interface{}{}
 	whereQueries := []string{}
@@ -125,9 +138,3 @@ func (repo *ProberRepo) CheckApi(key string) (Prober, error) {
 	err := repo.db.QueryRow(query, key).Scan(&prober.Id, &prober.Name, &prober.ApiKey, &prober.LastSubmitTs)
 	return prober, err
 }
-
-func (repo *ProberRepo) Delete(id int) error {
-	query := `DELETE FROM tbl_prober WHERE id = ?`
-	_, err := repo.db.Exec(query, id)
-	return err
-}