Use modal window for add prober form

The previous add prober is using new page to show add prober form.
In this commit, the add prober form is using modal so it can be more
simpler.
This commit is contained in:
ditatompel 2024-05-07 23:42:35 +07:00
parent 50588da322
commit cda024ca6f
No known key found for this signature in database
GPG key ID: 31D3D06D77950979
4 changed files with 52 additions and 102 deletions

View file

@ -1,7 +1,7 @@
<script>
import { DataHandler } from '@vincjo/datatables/remote';
import { format, formatDistance } from 'date-fns';
import { loadData, deleteData, editProber } from './api-handler';
import { loadData, createProber, editProber, deleteProber } from './api-handler';
import { onMount, onDestroy } from 'svelte';
import { getModalStore, getToastStore } from '@skeletonlabs/skeleton';
import {
@ -14,6 +14,36 @@
const modalStore = getModalStore();
const toastStore = getToastStore();
function showAddModal() {
/** @type {import('@skeletonlabs/skeleton').ModalSettings} */
const modal = {
type: 'prompt',
// Data
title: 'Enter Name',
body: 'Enter a name for the prober',
valueAttr: { type: 'text', minlength: 3, maxlength: 50, required: true },
response: (r) => {
if (!r) return;
createProber(r)
.then((res) => {
if (res.status !== 'ok') {
toastStore.trigger({ message: 'Failed to create prober' });
} else {
toastStore.trigger({
message: 'Prober created',
background: 'variant-filled-success'
});
handler.invalidate();
}
})
.catch(() => {
toastStore.trigger({ message: 'Failed to create prober' });
});
}
};
modalStore.trigger(modal);
}
/**
* @param {string} proberId
* @param {string} proberName
@ -28,6 +58,7 @@
value: proberName,
valueAttr: { type: 'text', minlength: 3, maxlength: 50, required: true },
response: (r) => {
if (!r) return;
editProber(proberId, r)
.then((res) => {
if (res.status !== 'ok') {
@ -57,7 +88,7 @@
/** @param {boolean} r */
response: async (r) => {
if (r) {
deleteData(id)
deleteProber(id)
.then((res) => {
if (res.status !== 'ok') {
toastStore.trigger({ message: 'Failed to delete prober' });
@ -134,7 +165,7 @@
</div>
<div class="dashboard-card">
<a class="variant-filled-success btn btn-sm mb-4" href="/app/prober/add">Add Prober</a>
<button class="variant-filled-success btn btn-sm mb-4" on:click={showAddModal}>Add Prober</button>
<div class="flex justify-between">
<DtSrRowsPerPage {handler} />
<div class="invisible flex place-items-center md:visible">

View file

@ -1,94 +0,0 @@
<script>
import { invalidateAll, goto } from '$app/navigation';
import { apiUri } from '$lib/utils/common';
import { ProgressBar } from '@skeletonlabs/skeleton';
/**
* @typedef formResult
* @type {object}
* @property {string} status
* @property {string} message
* @property {null | object} data
*/
/** @type {formResult} */
export let formResult;
let isProcessing = false;
/** @param {{ currentTarget: EventTarget & HTMLFormElement}} event */
async function handleSubmit(event) {
isProcessing = true;
const data = new FormData(event.currentTarget);
const response = await fetch(event.currentTarget.action, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json'
},
body: JSON.stringify(Object.fromEntries(data))
});
formResult = await response.json();
isProcessing = false;
if (formResult.status === 'ok') {
// rerun all `load` functions, following the successful update
await invalidateAll();
goto('/app/prober/');
}
}
</script>
<div class="mb-4">
<h1 class="h2 font-extrabold dark:text-white">Add Prober</h1>
</div>
{#if !isProcessing}
{#if formResult?.status === 'error'}
<div class="p-4 mb-4 text-sm rounded-lg bg-gray-700 text-red-400" role="alert">
<span class="font-medium">Error:</span>
{formResult.message}!
</div>
{/if}
{#if formResult?.status === 'ok'}
<div class="p-4 mb-4 text-sm rounded-lg bg-gray-700 text-green-400" role="alert">
<span class="font-medium">Success:</span>
{formResult.message}!
</div>
{/if}
{:else}
<ProgressBar meter="bg-secondary-500" track="bg-secondary-500/30" value={undefined} />
<div class="mx-4 p-4 mb-4 text-sm rounded-lg bg-gray-700 text-blue-400" role="alert">
<span class="font-medium">Processing...</span>
</div>
{/if}
<div class="dashboard-card">
<form
class="space-y-4 md:space-y-6"
action={apiUri('/api/v1/prober')}
method="POST"
on:submit|preventDefault={handleSubmit}
>
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
<div>
<label for="name" class="label">
<span>Name</span>
<input
type="text"
name="name"
id="name"
placeholder="Prober name"
autocomplete="off"
class="input variant-form-material"
/>
</label>
</div>
</div>
<button
type="submit"
class="w-full rounded-lg bg-primary-600 px-5 py-2.5 text-center text-sm font-medium hover:bg-primary-700"
>Submit</button
>
</form>
</div>

View file

@ -8,10 +8,14 @@ export const loadData = async (state) => {
return json.data.items ?? [];
};
export const deleteData = async (id) => {
const response = await fetch(apiUri(`/api/v1/prober/${id}`), {
method: 'DELETE',
credentials: 'include'
export const createProber = async (name) => {
const response = await fetch(apiUri('/api/v1/prober'), {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ name })
});
const json = await response.json();
return json;
@ -30,6 +34,15 @@ export const editProber = async (id, name) => {
return json;
};
export const deleteProber = async (id) => {
const response = await fetch(apiUri(`/api/v1/prober/${id}`), {
method: 'DELETE',
credentials: 'include'
});
const json = await response.json();
return json;
};
const getParams = ({ pageNumber, rowsPerPage, sort, filters }) => {
let params = `page=${pageNumber}&limit=${rowsPerPage}`;

View file

@ -13,7 +13,7 @@ func V1Api(app *fiber.App) {
v1 := app.Group("/api/v1")
v1.Get("/prober", Prober)
v1.Post("/prober", Prober)
v1.Post("/prober", CookieProtected, Prober)
v1.Patch("/prober/:id", CookieProtected, Prober)
v1.Delete("/prober/:id", CookieProtected, Prober)
v1.Get("/nodes", MoneroNodes)