feat(SPA): screen to manage API keys (PART 2/3)

This commit is contained in:
fsylvain committed 2026-10-05 00:05:12 +02:00
1 parent 844842fdae
commit 514a082c96
30 files changed
+3844 -83

No files matched your search

+117
View File
@@ -0,0 +1,117 @@
<script setup lang="ts">
import { t } from '@/i18n'
import { computed, nextTick, ref, watch } from 'vue'
import { useCopyToClipboard } from '@/composables/useCopyToClipboard'
const props = defineProps<{
name: string
apiKey: string
}>()
const open = defineModel<boolean>('open', { default: false })
const emit = defineEmits<{ done: [] }>()
const { copied, copy } = useCopyToClipboard()
const copyButton = ref<{ $el: HTMLElement } | null>(null)
const description = computed(() =>
t('"{0}" is ready. Copy the key and store it somewhere safe.', [props.name]),
)
const copyLabel = computed(() => (copied.value ? t('Copied') : t('Copy')))
const copyIcon = computed(() => (copied.value ? 'i-lucide-check' : 'i-lucide-copy'))
watch(
open,
async (isOpen) => {
if (!isOpen) {
return
}
await nextTick()
await nextTick()
copyButton.value?.$el?.focus()
},
{ immediate: true },
)
function onOpenChange(isOpen: boolean) {
if (!isOpen) {
emit('done')
}
}
function keepOpenOnOutsideInteraction(event: Event) {
event.preventDefault()
}
</script>
<template>
<UModal
:open="open"
:title="t('Your new API key')"
:description="description"
:close="false"
:content="{
onPointerDownOutside: keepOpenOnOutsideInteraction,
onInteractOutside: keepOpenOnOutsideInteraction,
}"
:ui="{
content:
'divide-y-0 border border-default shadow-(--shadow-modal) ring-0 sm:top-30 sm:max-w-120 sm:translate-y-0 max-sm:!inset-x-4 max-sm:!top-auto max-sm:!bottom-4 max-sm:!left-4 max-sm:!w-auto max-sm:!max-w-none max-sm:!translate-x-0 max-sm:!translate-y-0',
header: 'min-h-0 px-5 pt-5 pb-0 sm:px-5',
title: 'text-[1.125rem]/6.5 font-semibold',
description: 'mt-1 text-base/5.5 text-toned text-pretty',
body: 'px-5 pt-3.5 pb-0 sm:px-5 sm:pt-3.5 sm:pb-0',
footer: 'px-5 pt-3.5 pb-5 sm:px-5',
}"
@update:open="onOpenChange"
>
<template #body>
<div class="flex flex-col gap-3.5">
<UAlert
role="alert"
color="warning"
variant="soft"
:close="false"
icon="i-lucide-triangle-alert"
:title="t('You won\'t be able to see this key again.')"
:ui="{ root: 'gap-2.5 px-3 py-2.5', title: 'text-sm font-medium' }"
/>
<div class="flex flex-col border border-accented bg-elevated md:flex-row">
<span
class="min-w-0 flex-1 px-3 py-2.5 font-mono text-code/5 break-all text-highlighted"
>{{ apiKey }}</span
>
<UButton
ref="copyButton"
:icon="copyIcon"
color="neutral"
variant="ghost"
aria-live="polite"
class="h-11 justify-center gap-1.5 rounded-none border-t border-accented px-3.5 font-medium md:border-t-0 md:border-l"
:class="copied ? 'text-success' : 'text-highlighted'"
:ui="{ leadingIcon: 'size-3.5' }"
@click="copy(apiKey)"
>
{{ copyLabel }}
</UButton>
</div>
<span class="text-muted">{{
t('Closing this window discards the key from the screen. Copy it first.')
}}</span>
</div>
</template>
<template #footer>
<div class="flex w-full flex-col sm:flex-row sm:justify-end">
<UButton
block
class="h-11 justify-center sm:w-auto sm:px-3.5"
@click="emit('done')"
>
{{ t('Done') }}
</UButton>
</div>
</template>
</UModal>
</template>