mirror of
https://github.com/open-reception/appointment-booking-software.git
synced 2026-10-01 13:14:50 +02:00
Added edit tenant form
This commit is contained in:
@@ -46,7 +46,7 @@
|
||||
"noPassAtAll": "Either passphrase or passkey is required",
|
||||
"bothPassSet": "Nur Passphrase oder Passkey darf gesetzt sein",
|
||||
"name": "Muss mindestens 2 Zeichen lang sein",
|
||||
"shortname": "Muss zwischen 4 und 20 Zeichen lang sein",
|
||||
"shortname": "Muss zwischen 4 und 15 Zeichen lang sein",
|
||||
"deleteConfirmation": "Muss exakt »{expectedValue}« sein"
|
||||
},
|
||||
"passkey": "Passkey",
|
||||
@@ -127,7 +127,8 @@
|
||||
"name": {
|
||||
"description": "Muss einmalig auf diesem Server sein. Wird als URL verwendet {domain}",
|
||||
"errors": {
|
||||
"urlFormat": "Nur Kleinbuchstaben, Zahlen und Bindestriche sind erlaubt"
|
||||
"urlFormat": "Nur Kleinbuchstaben, Zahlen und Bindestriche sind erlaubt",
|
||||
"startEndDash": "Darf nicht mit einem Bindestrich beginnen oder enden"
|
||||
}
|
||||
},
|
||||
"invite": {
|
||||
@@ -167,6 +168,13 @@
|
||||
"title": "Mandanten kann nicht gelöscht werden",
|
||||
"description": "Du kannst den Mandanten, den Du gerade konfigurierst, nicht löschen. Bitte wechsle zu einem anderen Mandanten, bevor Du diesen löschst."
|
||||
}
|
||||
},
|
||||
"edit": {
|
||||
"title": "Mandant bearbeiten",
|
||||
"description": "Aktualisiere den Namen des Mandanten.",
|
||||
"success": "Mandant aktualisiert",
|
||||
"error": "Mandant konnte nicht aktualisiert werden.",
|
||||
"action": "Name ändern"
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
"noPassAtAll": "Either passphrase or passkey is required",
|
||||
"bothPassSet": "Only one of passphrase or passkey should be provided",
|
||||
"name": "Must be at least 2 characters",
|
||||
"shortname": "Must be between 4 and 20 characters",
|
||||
"shortname": "Must be between 4 and 15 characters",
|
||||
"deleteConfirmation": "Must be exactly »{expectedValue}«"
|
||||
},
|
||||
"noPassAtAll": "Either passphrase or passkey is required",
|
||||
@@ -136,7 +136,8 @@
|
||||
"name": {
|
||||
"description": "Must be unique on this server. Used as a subdomain {domain}",
|
||||
"errors": {
|
||||
"urlFormat": "Only letters, numbers, and hyphens are allowed"
|
||||
"urlFormat": "Only letters, numbers, and hyphens are allowed",
|
||||
"startEndDash": "Must not start or end with a dash"
|
||||
}
|
||||
},
|
||||
"invite": {
|
||||
@@ -175,7 +176,14 @@
|
||||
"loading": "Loading tenants",
|
||||
"allTenants": "All tenants",
|
||||
"unselectTenant": "Unselect tenant",
|
||||
"failedToSwitch": "Failed to switch tenant"
|
||||
"failedToSwitch": "Failed to switch tenant",
|
||||
"edit": {
|
||||
"title": "Edit Tenant",
|
||||
"description": "Update the tenant's short name.",
|
||||
"success": "Tenant updated",
|
||||
"error": "Could not update tenant.",
|
||||
"action": "Edit Tenant"
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"openMenu": "Open menu"
|
||||
|
||||
+4
-4
@@ -42,19 +42,19 @@
|
||||
</script>
|
||||
|
||||
<Form.Root {enhance} action="?/add">
|
||||
<Form.Field {form} name="shortname">
|
||||
<Form.Field {form} name="shortName">
|
||||
<Form.Control>
|
||||
{#snippet children({ props })}
|
||||
<Form.Label>{m["form.name"]()}</Form.Label>
|
||||
<Input {...props} bind:value={$formData.shortname} type="text" autocomplete="off" />
|
||||
<Input {...props} bind:value={$formData.shortName} type="text" autocomplete="off" />
|
||||
{/snippet}
|
||||
</Form.Control>
|
||||
<Form.Description>
|
||||
{m["tenants.add.name.description"]({
|
||||
domain:
|
||||
$formData.shortname.length < 2
|
||||
$formData.shortName.length < 2
|
||||
? ""
|
||||
: `${$formData.shortname}.${window.location.hostname}`,
|
||||
: `${$formData.shortName}.${window.location.hostname}`,
|
||||
})}
|
||||
</Form.Description>
|
||||
<Form.FieldErrors />
|
||||
|
||||
@@ -2,15 +2,15 @@ import { m } from "$i18n/messages";
|
||||
import { z } from "zod";
|
||||
|
||||
export const formSchema = z.object({
|
||||
shortname: z
|
||||
shortName: z
|
||||
.string()
|
||||
.min(4, m["form.errors.shortname"]())
|
||||
.max(20, m["form.errors.shortname"]())
|
||||
.max(15, m["form.errors.shortname"]())
|
||||
.regex(/^[a-z0-9]+(-[a-z0-9]+)*$/, {
|
||||
message: m["tenants.add.name.errors.urlFormat"](),
|
||||
})
|
||||
.refine((val) => !val.startsWith("-") && !val.endsWith("-"), {
|
||||
message: "Cannot start or end with a dash",
|
||||
message: m["tenants.add.name.errors.startEndDash"](),
|
||||
}),
|
||||
inviteAdmin: z.boolean(),
|
||||
email: z.string().email(m["form.errors.email"]()).optional().or(z.literal("")),
|
||||
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
<script lang="ts">
|
||||
import { m } from "$i18n/messages.js";
|
||||
import * as Form from "$lib/components/ui/form";
|
||||
import { Input } from "$lib/components/ui/input";
|
||||
import type { TTenant } from "$lib/types/tenant";
|
||||
import { toast } from "svelte-sonner";
|
||||
import { superForm } from "sveltekit-superforms";
|
||||
import { zodClient } from "sveltekit-superforms/adapters";
|
||||
import { formSchema } from ".";
|
||||
|
||||
let { entity, done }: { entity: TTenant; done: () => void } = $props();
|
||||
|
||||
const form = superForm(
|
||||
{ tenantId: entity.id, shortName: entity.shortName },
|
||||
{
|
||||
validators: zodClient(formSchema),
|
||||
onResult: async (event) => {
|
||||
if (event.result.type === "success") {
|
||||
toast.success(m["tenants.edit.success"]());
|
||||
done();
|
||||
} else if (event.result.type === "failure") {
|
||||
toast.error(m["tenants.edit.error"]());
|
||||
}
|
||||
isSubmitting = false;
|
||||
},
|
||||
onSubmit: () => (isSubmitting = true),
|
||||
},
|
||||
);
|
||||
|
||||
let isSubmitting = $state(false);
|
||||
|
||||
const { form: formData, enhance } = form;
|
||||
</script>
|
||||
|
||||
<Form.Root {enhance} action="?/edit">
|
||||
<Form.Field {form} name="shortName">
|
||||
<Form.Control>
|
||||
{#snippet children({ props })}
|
||||
<Form.Label>{m["form.name"]()}</Form.Label>
|
||||
<Input {...props} bind:value={$formData.shortName} type="text" autocomplete="off" />
|
||||
{/snippet}
|
||||
</Form.Control>
|
||||
<Form.Description>
|
||||
{m["tenants.add.name.description"]({
|
||||
domain:
|
||||
$formData.shortName.length < 2
|
||||
? ""
|
||||
: `${$formData.shortName}.${window.location.hostname}`,
|
||||
})}
|
||||
</Form.Description>
|
||||
<Form.FieldErrors />
|
||||
</Form.Field>
|
||||
<Form.Field {form} name="tenantId" class="hidden">
|
||||
<Form.Control>
|
||||
{#snippet children({ props })}
|
||||
<Form.Label>{m["form.name"]()}</Form.Label>
|
||||
<Input {...props} bind:value={$formData.tenantId} type="hidden" />
|
||||
{/snippet}
|
||||
</Form.Control>
|
||||
</Form.Field>
|
||||
|
||||
<div class="mt-6 flex flex-col gap-4">
|
||||
<Form.Button size="lg" type="submit" isLoading={isSubmitting} disabled={isSubmitting}>
|
||||
{m["tenants.edit.action"]()}
|
||||
</Form.Button>
|
||||
</div>
|
||||
</Form.Root>
|
||||
@@ -0,0 +1,5 @@
|
||||
import EditTenantForm from "./edit-tenant-form.svelte";
|
||||
|
||||
export { EditTenantForm };
|
||||
export { formSchema } from "./schema";
|
||||
export type { FormSchema } from "./schema";
|
||||
@@ -0,0 +1,18 @@
|
||||
import { m } from "$i18n/messages";
|
||||
import { z } from "zod";
|
||||
|
||||
export const formSchema = z.object({
|
||||
shortName: z
|
||||
.string()
|
||||
.min(4, m["form.errors.shortname"]())
|
||||
.max(15, m["form.errors.shortname"]())
|
||||
.regex(/^[a-z0-9]+(-[a-z0-9]+)*$/, {
|
||||
message: m["tenants.add.name.errors.urlFormat"](),
|
||||
})
|
||||
.refine((val) => !val.startsWith("-") && !val.endsWith("-"), {
|
||||
message: m["tenants.add.name.errors.startEndDash"](),
|
||||
}),
|
||||
tenantId: z.string(),
|
||||
});
|
||||
|
||||
export type FormSchema = typeof formSchema;
|
||||
@@ -4,6 +4,7 @@ import { fail, redirect, type Actions } from "@sveltejs/kit";
|
||||
import { superValidate } from "sveltekit-superforms";
|
||||
import { zod } from "sveltekit-superforms/adapters";
|
||||
import { formSchema as addFormSchema } from "./(components)/add-tenant-form";
|
||||
import { formSchema as editFormSchema } from "./(components)/edit-tenant-form";
|
||||
import { formSchema as deleteFormSchema } from "./(components)/delete-tenant-form";
|
||||
import type { TTenant } from "$lib/types/tenant";
|
||||
|
||||
@@ -59,7 +60,7 @@ export const actions: Actions = {
|
||||
},
|
||||
credentials: "same-origin",
|
||||
body: JSON.stringify({
|
||||
shortName: form.data.shortname,
|
||||
shortName: form.data.shortName,
|
||||
inviteAdmin: form.data.email,
|
||||
}),
|
||||
});
|
||||
@@ -80,6 +81,42 @@ export const actions: Actions = {
|
||||
});
|
||||
}
|
||||
},
|
||||
edit: async (event) => {
|
||||
const form = await superValidate(event, zod(editFormSchema));
|
||||
|
||||
if (!form.valid) {
|
||||
log.error("Edit tenant form is not valid", { errors: form.errors });
|
||||
return fail(400, {
|
||||
form: { ...form, data: { ...form.data } },
|
||||
error: "Form is not valid",
|
||||
});
|
||||
}
|
||||
|
||||
const resp = await event.fetch(`/api/tenants/${form.data.tenantId}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
credentials: "same-origin",
|
||||
body: JSON.stringify({ shortName: form.data.shortName }),
|
||||
});
|
||||
|
||||
if (resp.status < 400) {
|
||||
return { form };
|
||||
} else {
|
||||
let error = "Unknown error";
|
||||
try {
|
||||
const body = await resp.json();
|
||||
error = body.error;
|
||||
} catch (e) {
|
||||
log.error("Failed to parse edit tenant error response", { error: e });
|
||||
}
|
||||
return fail(400, {
|
||||
form: { ...form, data: { ...form.data } },
|
||||
error,
|
||||
});
|
||||
}
|
||||
},
|
||||
delete: async (event) => {
|
||||
const form = await superValidate(event, zod(deleteFormSchema));
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
import { AddTenantForm } from "./(components)/add-tenant-form";
|
||||
import { tenants as tenantsStore } from "$lib/stores/tenants";
|
||||
import DeleteTenantForm from "./(components)/delete-tenant-form/delete-tenant-form.svelte";
|
||||
import EditTenantForm from "./(components)/edit-tenant-form/edit-tenant-form.svelte";
|
||||
|
||||
const { data } = $props();
|
||||
let curTenant: TTenant | null = $state(null);
|
||||
@@ -75,7 +76,10 @@
|
||||
type: "action",
|
||||
icon: EditIcon,
|
||||
label: m["edit"](),
|
||||
onClick: () => {},
|
||||
onClick: () => {
|
||||
curTenant = tenant;
|
||||
openDialog("edit-tenant");
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "action",
|
||||
@@ -102,6 +106,23 @@
|
||||
</ListItem>
|
||||
{/each}
|
||||
</List>
|
||||
<ResponsiveDialog
|
||||
id="edit-tenant"
|
||||
title={m["tenants.edit.title"]()}
|
||||
description={m["tenants.edit.description"]()}
|
||||
triggerHidden={true}
|
||||
>
|
||||
{#if curTenant}
|
||||
<EditTenantForm
|
||||
entity={curTenant}
|
||||
done={() => {
|
||||
closeDialog("edit-tenant");
|
||||
curTenant = null;
|
||||
invalidate(ROUTES.DASHBOARD.TENANTS);
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
</ResponsiveDialog>
|
||||
<ResponsiveDialog
|
||||
id="delete-tenant"
|
||||
title={m["tenants.delete.title"]()}
|
||||
|
||||
Reference in New Issue
Block a user