The api client now catches network failures and non-JSON bodies robustly, and routes unexpected errors (offline / 5xx) to a global toast — 4xx stay with the caller so forms keep their inline messages. Toast actions are now optional (undo toasts keep their button; error toasts are message-only), and ToastHost moved from AppShell to the app root so toasts show everywhere, including the login screen. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FRgehjoz7Yv8LkUfADxACm
40 lines
1.2 KiB
Vue
40 lines
1.2 KiB
Vue
<script setup lang="ts">
|
||
import { useUiStore } from "../stores/ui";
|
||
|
||
const ui = useUiStore();
|
||
</script>
|
||
|
||
<template>
|
||
<Transition
|
||
enter-active-class="transition duration-150"
|
||
enter-from-class="translate-y-2 opacity-0"
|
||
leave-active-class="transition duration-150"
|
||
leave-to-class="translate-y-2 opacity-0"
|
||
>
|
||
<div
|
||
v-if="ui.toast"
|
||
class="fixed bottom-4 left-1/2 z-50 flex -translate-x-1/2 items-center gap-3 rounded-lg bg-neutral-900 px-4 py-2.5 text-sm text-white shadow-lg dark:bg-neutral-100 dark:text-neutral-900"
|
||
role="status"
|
||
aria-live="polite"
|
||
>
|
||
<span>{{ ui.toast.message }}</span>
|
||
<button
|
||
v-if="ui.toast.action"
|
||
type="button"
|
||
class="font-semibold text-brand hover:underline focus:outline-none focus-visible:ring-2 focus-visible:ring-brand"
|
||
@click="ui.runToastAction()"
|
||
>
|
||
{{ ui.toast.action.label }}
|
||
</button>
|
||
<button
|
||
type="button"
|
||
class="text-white/60 hover:text-white dark:text-neutral-900/60 dark:hover:text-neutral-900"
|
||
aria-label="Dismiss"
|
||
@click="ui.dismissToast()"
|
||
>
|
||
×
|
||
</button>
|
||
</div>
|
||
</Transition>
|
||
</template>
|