clients page dialog improvements: state in url params

This commit is contained in:
Yuri Tatishchev 2024-12-25 15:35:28 -08:00
parent 62daabcd4c
commit 015bb7b05b
Signed by: CaZzzer
GPG Key ID: E0EBF441EA424369
3 changed files with 39 additions and 22 deletions

View File

@ -1,6 +1,6 @@
import type { User } from '$lib/server/db/schema'; import type { User } from '$lib/server/db/schema';
import { ipAllocations, wgClients } from '$lib/server/db/schema';
import { db } from '$lib/server/db'; import { db } from '$lib/server/db';
import { wgClients, ipAllocations } from '$lib/server/db/schema';
import { opnsenseAuth, opnsenseUrl, serverPublicKey, serverUuid } from '$lib/server/opnsense'; import { opnsenseAuth, opnsenseUrl, serverPublicKey, serverUuid } from '$lib/server/opnsense';
import { Address4, Address6 } from 'ip-address'; import { Address4, Address6 } from 'ip-address';
import { env } from '$env/dynamic/private'; import { env } from '$env/dynamic/private';
@ -60,7 +60,7 @@ export function mapClientToDetails(
export async function createClient(params: { export async function createClient(params: {
name: string; name: string;
user: User; user: User;
}): Promise<Result<null, [400 | 500, string]>> { }): Promise<Result<number, [400 | 500, string]>> {
// check if user exceeds the limit of clients // check if user exceeds the limit of clients
const [{ clientCount }] = await db const [{ clientCount }] = await db
.select({ clientCount: count() }) .select({ clientCount: count() })
@ -76,7 +76,7 @@ export async function createClient(params: {
// 2.3. update the allocation with the client id // 2.3. update the allocation with the client id
// 3. create the client in opnsense // 3. create the client in opnsense
// 4. reconfigure opnsense to enable the new client // 4. reconfigure opnsense to enable the new client
const error = await db.transaction(async (tx) => { return await db.transaction(async (tx) => {
const [keys, availableAllocation, lastAllocation] = await Promise.all([ const [keys, availableAllocation, lastAllocation] = await Promise.all([
// fetch params for new client from opnsense api // fetch params for new client from opnsense api
getKeys(), getKeys(),
@ -143,10 +143,9 @@ export async function createClient(params: {
// reconfigure opnsense // reconfigure opnsense
await opnsenseReconfigure(); await opnsenseReconfigure();
return ok(newClient.id);
}); });
}); });
if (error) return error;
return ok(null);
} }
async function getKeys() { async function getKeys() {

View File

@ -1,6 +1,6 @@
import type { Actions } from './$types'; import type { Actions } from './$types';
import { createClient } from '$lib/server/clients'; import { createClient } from '$lib/server/clients';
import { error } from '@sveltejs/kit'; import { error, redirect } from '@sveltejs/kit';
export const actions = { export const actions = {
create: async (event) => { create: async (event) => {
@ -15,9 +15,7 @@ export const actions = {
switch (res._tag) { switch (res._tag) {
case 'ok': { case 'ok': {
return { return redirect(303, `/clients/${res.value}`);
status: 201,
};
} }
case 'err': { case 'err': {
const [status, message] = res.error; const [status, message] = res.error;

View File

@ -1,14 +1,25 @@
<script lang="ts"> <script lang="ts">
import * as Table from '$lib/components/ui/table'; import * as Table from '$lib/components/ui/table';
import * as Dialog from '$lib/components/ui/dialog';
import { Badge } from '$lib/components/ui/badge'; import { Badge } from '$lib/components/ui/badge';
import { Button } from '$lib/components/ui/button'; import { Button } from '$lib/components/ui/button';
import { Input } from '$lib/components/ui/input'; import { Input } from '$lib/components/ui/input';
import { LucidePlus } from 'lucide-svelte'; import { LucidePlus } from 'lucide-svelte';
import * as Dialog from "$lib/components/ui/dialog";
import type { PageData } from './$types'; import type { PageData } from './$types';
import { Label } from '$lib/components/ui/label'; import { Label } from '$lib/components/ui/label';
import { page } from '$app/state';
const { data }: { data: PageData } = $props(); const { data }: { data: PageData } = $props();
let dialogOpen = $state(page.url.searchParams.has('add'));
let dialogVal = $state(page.url.searchParams.get('add') ?? '');
$effect(() => {
if (dialogOpen) page.url.searchParams.set('add', dialogVal);
else page.url.searchParams.delete('add');
window.history.replaceState(history.state, '', page.url);
});
</script> </script>
<svelte:head> <svelte:head>
@ -52,24 +63,33 @@
<!--Floating action button for adding a new client--> <!--Floating action button for adding a new client-->
<!--Not sure if this is the best place for the input field, will think about it later--> <!--Not sure if this is the best place for the input field, will think about it later-->
<div class="mt-auto flex self-end pt-4"> <div class="mt-auto flex self-end pt-4">
<Dialog.Root> <Dialog.Root bind:open={dialogOpen}>
<Dialog.Trigger> <Dialog.Trigger asChild let:builder>
<Button> <Button builders={[builder]}>
<LucidePlus class="mr-2 h-4 w-4" /> <LucidePlus class="mr-2 h-4 w-4" />
Add Client Add Client
</Button> </Button>
</Dialog.Trigger> </Dialog.Trigger>
<Dialog.Content class="max-w-xs"> <Dialog.Content class="max-w-xs">
<form class="contents" method="post" action="?/create"> <form class="contents" method="post" action="?/create">
<Dialog.Header class=""> <Dialog.Header class="">
<Dialog.Title>Create a new client</Dialog.Title> <Dialog.Title>Create a new client</Dialog.Title>
</Dialog.Header> </Dialog.Header>
<div class="flex flex-wrap items-center justify-between gap-4"> <div class="flex flex-wrap items-center justify-between gap-4">
<Label for="name">Name</Label> <Label for="name">Name</Label>
<Input required pattern=".*[^\s]+.*" type="text" name="name" placeholder="New Client" class="max-w-[20ch]" /></div> <Input
<Dialog.Footer> bind:value={dialogVal}
<Button type="submit">Create</Button> required
</Dialog.Footer> pattern=".*[^\s]+.*"
type="text"
name="name"
placeholder="New Client"
class="max-w-[20ch]"
/>
</div>
<Dialog.Footer>
<Button type="submit">Create</Button>
</Dialog.Footer>
</form> </form>
</Dialog.Content> </Dialog.Content>
</Dialog.Root> </Dialog.Root>