feat: improve ux and security

This commit is contained in:
2026-03-25 17:30:49 +01:00
parent fedb0ae8db
commit 9c61d7561a
8 changed files with 80 additions and 15 deletions

View File

@@ -0,0 +1,22 @@
<script setup lang="ts">
defineProps<{
title: string;
description?: string;
}>();
const emit = defineEmits<{ close: [confirmed: boolean] }>();
</script>
<template>
<UModal :title="title">
<template #body>
<p v-if="description" class="text-sm text-muted">{{ description }}</p>
</template>
<template #footer>
<div class="flex gap-2 justify-end w-full">
<UButton variant="ghost" color="neutral" @click="emit('close', false)">Cancel</UButton>
<UButton color="error" @click="emit('close', true)">Delete</UButton>
</div>
</template>
</UModal>
</template>

View File

@@ -8,7 +8,7 @@ const props = defineProps<{
const schema = z.object({
name: z.string({ error: "Required" }),
path: z.string({ error: "Required" }),
path: z.string({ error: "Required" }).startsWith("/", "Must start with /"),
url: z.url({ error: (err) => err.code === "invalid_type" ? "Required" : "Invalid format", }),
disabled: z.boolean().optional(),
});

View File

@@ -1,5 +1,5 @@
<script setup lang="ts">
import { LazyLinkModal } from "#components";
import { LazyLinkModal, LazyConfirmModal } from "#components";
definePageMeta({ middleware: "auth" });
@@ -37,14 +37,18 @@ const openModal = async (link: Link | null) => {
}
const deleteLink = async (link: Link) => {
const modal = overlay.create(LazyConfirmModal, { destroyOnClose: true });
const instance = modal.open({ title: "Delete link", description: `Are you sure you want to delete "${link.name}"?` });
if (!await instance.result) return;
try {
await $fetch(`/api/links/${link.id}`, { method: "DELETE" });
toast.add({ title: "Link deleted", color: "success" });
await refresh();
} catch {
toast.add({ title: "Failed to delete link", color: "error" });
} catch (error) {
toast.add({ title: getApiError(error), color: "error" });
}
}
};
</script>