support for saving updates with CTRL+s

Signed-off-by: Ronni Skansing <rskansing@gmail.com>
This commit is contained in:
Ronni Skansing
2025-10-09 12:35:06 +02:00
parent 52df491312
commit 6ee2663374
6 changed files with 97 additions and 41 deletions
+29 -1
View File
@@ -1,7 +1,35 @@
<script>
import { createEventDispatcher, onMount, onDestroy } from 'svelte';
export let bindTo = null;
$: if (formElement) {
bindTo = formElement;
}
export let isSubmitting = false;
export let novalidate = false;
export let modalMode = null; // 'create', 'update', 'copy'
const dispatch = createEventDispatcher();
let formElement = null;
function handleKeydown(event) {
if (event.ctrlKey && event.key === 's') {
// Only trigger if the form or its descendants have focus and we're in update mode
if (modalMode === 'update' && formElement && formElement.contains(document.activeElement)) {
event.preventDefault();
dispatch('submit', { ...event, saveOnly: true });
}
}
}
onMount(() => {
window.addEventListener('keydown', handleKeydown);
});
onDestroy(() => {
window.removeEventListener('keydown', handleKeydown);
});
</script>
<form
@@ -9,7 +37,7 @@
inert={isSubmitting}
class="grid grid-cols-3 grid-rows-1 w-full h-full flex-col bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 transition-colors duration-200"
class:opacity-70={isSubmitting}
bind:this={bindTo}
bind:this={formElement}
{novalidate}
>
<slot />
+9 -6
View File
@@ -126,14 +126,15 @@
return [];
};
const onSubmit = async () => {
const onSubmit = async (event) => {
try {
isSubmitting = true;
const saveOnly = event?.detail?.saveOnly || false;
if (modalMode === 'create' || modalMode === 'copy') {
await onClickCreate();
return;
} else {
await onClickUpdate();
await onClickUpdate(saveOnly);
return;
}
} finally {
@@ -157,15 +158,17 @@
}
};
const onClickUpdate = async () => {
const onClickUpdate = async (saveOnly = false) => {
try {
const res = await api.apiSender.update(formValues);
if (!res.success) {
modalError = res.error;
throw res.error;
}
addToast('Updated API sender', 'Success');
closeEditModal();
addToast(saveOnly ? 'API sender saved' : 'Updated API sender', 'Success');
if (!saveOnly) {
closeEditModal();
}
refreshConfigurations();
} catch (err) {
console.error('failed to update API sender:', err);
@@ -352,7 +355,7 @@
{/each}
</Table>
<Modal headerText={modalText} visible={isModalVisible} onClose={closeCreateModal} {isSubmitting}>
<FormGrid on:submit={onSubmit} bind:bindTo={form} {isSubmitting}>
<FormGrid on:submit={onSubmit} bind:bindTo={form} {isSubmitting} {modalMode}>
<FormColumns>
<FormColumn>
<!-- Basic Information Section -->
+29 -13
View File
@@ -172,7 +172,8 @@
return null;
};
const onSubmitPageUpdate = async () => {
const onSubmitPageUpdate = async (event) => {
const saveOnly = event?.detail?.saveOnly || false;
try {
isSubmitting = true;
updateContentError = '';
@@ -218,20 +219,23 @@
updateContentError = res.error;
return;
}
addToast('Domain updated', 'Success');
closeAllModals();
addToast(saveOnly ? 'Domain content saved' : 'Domain updated', 'Success');
if (!saveOnly) {
closeAllModals();
}
refreshDomains();
} catch (e) {
addToast('Failed to update domain', 'Error');
addToast(saveOnly ? 'Failed to save domain content' : 'Failed to update domain', 'Error');
console.error('failed to update domain', e);
} finally {
isSubmitting = false;
}
};
const onSubmit = async () => {
const onSubmit = async (event) => {
try {
// reset validate
const saveOnly = event?.detail?.saveOnly || false;
managedTLSInputElement?.setCustomValidity('');
ownManagedTLSInputElement?.setCustomValidity('');
ownManagedTLSKeyElement?.setCustomValidity('');
@@ -262,7 +266,7 @@
await onClickCreate();
return;
} else {
await onClickUpdate();
await onClickUpdate(saveOnly);
return;
}
} finally {
@@ -305,7 +309,7 @@
}
};
const onClickUpdate = async () => {
const onClickUpdate = async (saveOnly = false) => {
modalError = '';
// clear site contents if not hosting a website or if proxy domain
if (!formValues.hostWebsite || isProxyDomain) {
@@ -350,11 +354,13 @@
modalError = res.error;
return;
}
addToast('Domain updated', 'Success');
closeAllModals();
addToast(saveOnly ? 'Domain saved' : 'Domain updated', 'Success');
if (!saveOnly) {
closeAllModals();
}
refreshDomains();
} catch (e) {
addToast('Failed to update domain', 'Error');
addToast(saveOnly ? 'Failed to save domain' : 'Failed to update domain', 'Error');
console.error('failed to update domain', e);
}
};
@@ -716,7 +722,7 @@
onClose={closeAllModals}
{isSubmitting}
>
<FormGrid novalidate on:submit={onSubmit} bind:bindTo={form} {isSubmitting}>
<FormGrid novalidate on:submit={onSubmit} bind:bindTo={form} {isSubmitting} {modalMode}>
<FormColumns>
<FormColumn>
<!-- Domain Information Section -->
@@ -821,7 +827,12 @@
onClose={closeAllModals}
{isSubmitting}
>
<FormGrid on:submit={onSubmitPageUpdate} bind:bindTo={contentForm} {isSubmitting}>
<FormGrid
on:submit={onSubmitPageUpdate}
bind:bindTo={contentForm}
{isSubmitting}
modalMode="update"
>
<Editor
contentType="domain"
baseURL={formValues.name}
@@ -839,7 +850,12 @@
onClose={closeAllModals}
{isSubmitting}
>
<FormGrid on:submit={onSubmitPageUpdate} bind:bindTo={contentNotFoundForm} {isSubmitting}>
<FormGrid
on:submit={onSubmitPageUpdate}
bind:bindTo={contentNotFoundForm}
{isSubmitting}
modalMode="update"
>
<Editor
contentType="domain"
baseURL={formValues.name}
+10 -7
View File
@@ -137,14 +137,15 @@
}
};
const onSubmit = async () => {
const onSubmit = async (event) => {
try {
isSubmitting = true;
const saveOnly = event?.detail?.saveOnly || false;
if (modalMode === 'create' || modalMode === 'copy') {
await onClickCreate();
return;
} else {
await onClickUpdate();
await onClickUpdate(saveOnly);
return;
}
} finally {
@@ -176,7 +177,7 @@
}
};
const onClickUpdate = async () => {
const onClickUpdate = async (saveOnly = false) => {
try {
const res = await api.email.update({
id: formValues.id,
@@ -192,11 +193,13 @@
return;
}
modalError = '';
closeModal();
addToast('Email updated', 'Success');
addToast(saveOnly ? 'Email saved' : 'Email updated', 'Success');
if (!saveOnly) {
closeModal();
}
refreshEmails();
} catch (e) {
addToast('Failed to update email', 'Error');
addToast(saveOnly ? 'Failed to save email' : 'Failed to update email', 'Error');
console.error('failed to update email', e);
}
};
@@ -423,7 +426,7 @@
{/each}
</Table>
<Modal headerText={modalText} visible={isModalVisible} onClose={closeModal} {isSubmitting}>
<FormGrid on:submit={onSubmit} bind:bindTo={form} {isSubmitting}>
<FormGrid on:submit={onSubmit} bind:bindTo={form} {isSubmitting} {modalMode}>
<Editor contentType="email" {domainMap} bind:value={formValues.content}>
<div class="flex flex-col lg:flex-row w-full pl-4">
<div class="flex flex-col lg:flex-row justify-between w-1/3">
+10 -7
View File
@@ -140,14 +140,15 @@
}
};
const onSubmit = async () => {
const onSubmit = async (event) => {
try {
isSubmitting = true;
const saveOnly = event?.detail?.saveOnly || false;
if (modalMode === 'create' || modalMode === 'copy') {
await create();
return;
} else {
await update();
await update(saveOnly);
return;
}
} finally {
@@ -171,7 +172,7 @@
}
};
const update = async () => {
const update = async (saveOnly = false) => {
try {
const updateData = {
name: formValues.name,
@@ -183,11 +184,13 @@
formError = res.error;
return;
}
addToast('Page updated', 'Success');
closeModal();
addToast(saveOnly ? 'Page saved' : 'Page updated', 'Success');
if (!saveOnly) {
closeModal();
}
refreshPages();
} catch (e) {
addToast('Failed to update page', 'Error');
addToast(saveOnly ? 'Failed to save page' : 'Failed to update page', 'Error');
console.error('failed to update page', e);
}
};
@@ -359,7 +362,7 @@
{/each}
</Table>
<Modal headerText={modalText} visible={isModalVisible} onClose={closeModal} {isSubmitting}>
<FormGrid on:submit={onSubmit} bind:bindTo={form} {isSubmitting}>
<FormGrid on:submit={onSubmit} bind:bindTo={form} {isSubmitting} {modalMode}>
<Editor contentType="page" {domainMap} bind:value={formValues.content}>
<div class="pl-4">
<TextField
+10 -7
View File
@@ -144,14 +144,15 @@ portal.example.com:
}
};
const onSubmit = async () => {
const onSubmit = async (event) => {
try {
isSubmitting = true;
const saveOnly = event?.detail?.saveOnly || false;
if (modalMode === 'create' || modalMode === 'copy') {
await create();
return;
} else {
await update();
await update(saveOnly);
return;
}
} finally {
@@ -185,7 +186,7 @@ portal.example.com:
}
};
const update = async () => {
const update = async (saveOnly = false) => {
try {
const updateData = {
name: formValues.name,
@@ -199,11 +200,13 @@ portal.example.com:
formError = res.error;
return;
}
addToast('Proxy updated', 'Success');
closeModal();
addToast(saveOnly ? 'Proxy saved' : 'Proxy updated', 'Success');
if (!saveOnly) {
closeModal();
}
refreshProxies();
} catch (e) {
addToast('Failed to update Proxy', 'Error');
addToast(saveOnly ? 'Failed to save Proxy' : 'Failed to update Proxy', 'Error');
console.error('failed to update Proxy', e);
}
};
@@ -372,7 +375,7 @@ portal.example.com:
{/each}
</Table>
<Modal headerText={modalText} visible={isModalVisible} onClose={closeModal} {isSubmitting}>
<FormGrid on:submit={onSubmit} bind:bindTo={form} {isSubmitting}>
<FormGrid on:submit={onSubmit} bind:bindTo={form} {isSubmitting} {modalMode}>
<div class="col-span-3 w-full overflow-y-auto px-6 py-4 space-y-8">
<!-- Basic Information Section -->
<div class="w-full">