Move toaster component to AppLayout #33

This commit is contained in:
2025-10-29 15:42:43 +01:00
parent b943c17a0f
commit 9cf5db37bc
4 changed files with 89 additions and 81 deletions
+39
View File
@@ -3,6 +3,9 @@ import AppLayout from '@/layouts/app/AppSidebarLayout.vue';
// import AppLayout from '@/layouts/app/AppHeaderLayout.vue';
import type { BreadcrumbItemType } from '@/types';
import { computed, onMounted } from 'vue';
import 'vue-sonner/style.css'
import { Toaster } from 'vue-sonner'
import { Info, CircleAlert, CircleCheck, LoaderCircle, Ban } from "lucide-vue-next"
interface Props {
breadcrumbs?: BreadcrumbItemType[];
@@ -21,7 +24,43 @@ onMounted(() => {
</script>
<template>
<!-- :duration="120000" -->
<Toaster position="top-right" :expand="true" closeButton :visible-toasts="6" :offset="'1rem'" :toastOptions="{
unstyled: true,
classes: {
toast: 'bg-muted rounded-lg w-80 text-sm shadow-lg/20 p-3 pl-5 flex gap-3',
title: 'font-bold',
description: 'text-',
actionButton: 'bg-background hover:bg-accent border-1 rounded shadow text-foreground px-2 h-8 ml-auto whitespace-nowrap',
cancelButton: '',
closeButton: 'bg-background hover:bg-accent text-foreground stroke-2 rounded-full w-5 h-5 absolute -left-2 -top-2 flex justify-center items-center shadow-md',
info: 'text-blue-500 dark:text-blue-400',
error: 'text-white bg-red-500 dark:bg-red-800',
success: 'text-green-600 dark:text-green-500',
warning: 'text-white bg-amber-500! dark:bg-amber-600!',
}
}">
<template #loading-icon>
<div class="animate-spin">
<LoaderCircle />
</div>
</template>
<template #success-icon>
<CircleCheck />
</template>
<template #error-icon>
<Ban />
</template>
<template #info-icon>
<Info />
</template>
<template #warning-icon>
<CircleAlert />
</template>
</Toaster>
<AppLayout :breadcrumbs="breadcrumbs">
<slot />
</AppLayout>
</template>
+36 -3
View File
@@ -1,6 +1,7 @@
import { InertiaLinkProps } from '@inertiajs/vue3';
import { clsx, type ClassValue } from 'clsx';
import { twMerge } from 'tailwind-merge';
import { InertiaLinkProps } from '@inertiajs/vue3'
import { clsx, type ClassValue } from 'clsx'
import { twMerge } from 'tailwind-merge'
import { toast } from 'vue-sonner'
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
@@ -68,4 +69,36 @@ export function bgColorForString(input: string): string {
charCodeSum += input.charCodeAt(i)
}
return randomColors[charCodeSum % randomColors.length];
}
const promise = () => new Promise((resolve) => setTimeout(resolve, 2000))
export function testToast() {
let delay = 150
toast('Toast', {
description: "Description bjksad fkjlhsd fkjhsdf jkshdf jkashdf adskljhf ", action: {
label: 'Undo',
onClick: () => console.log('Undo')
}
})
setTimeout(() => {
toast.info('Info', {
action: {
label: 'Undo',
onClick: () => console.log('Undo')
}
})
}, delay * 1)
setTimeout(() => { toast.success('Success', { description: "description" }) }, delay * 2)
setTimeout(() => { toast.warning('Warning', { description: "description" }) }, delay * 3)
setTimeout(() => { toast.error('Error', { description: "description" }) }, delay * 4)
setTimeout(() => {
toast.promise(promise, {
loading: 'Loading...',
success: (data) => {
return `${data.name} toast has been added`;
},
error: (data: any) => 'Error',
})
}, delay * 5)
}
+10 -7
View File
@@ -17,6 +17,8 @@ import { getInitials } from '@/composables/useInitials';
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, } from '@/components/ui/card'
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'
import CustomerDialog from '@/components/CustomerDialog.vue'
import { toast } from 'vue-sonner'
import { AxiosError } from 'axios'
const breadcrumbs: BreadcrumbItem[] = [
{
@@ -38,8 +40,8 @@ onMounted(async () => {
searchField.value = document.getElementById('search')
searchField.value.focus()
} catch (error) {
// TODO: toast, depends on #33
} catch (error: AxiosError) {
toast.error(error.name, { description: error.message })
}
})
@@ -68,12 +70,13 @@ const addressToClipbard = async function (address: Address) {
address.lineOne + '\n' +
(address.lineTwo ? address.lineTwo + '\n' : '') +
address.postalCode + ' ' + address.city
// TODO: toast, depends on #33
)
} catch (error) {
if (error instanceof Error)
// TODO: toast, depends on #33
console.error(error.message)
toast('Adresse kopiert', { duration: 2000 })
} catch (notAllowedError: DOMException) {
if (notAllowedError instanceof Error) {
toast.error(notAllowedError.name, { description: notAllowedError.message })
}
}
}
+4 -71
View File
@@ -10,13 +10,12 @@ import axios, { Axios, AxiosError, AxiosResponse } from 'axios'
import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue, } from '@/components/ui/select'
import { Button } from '@/components/ui/button'
import DocumentTable from '@/components/documents/DocumentTable.vue'
import { ChevronLeft, ChevronRight, Plus, Search, Delete, LetterText, Info, Check, CircleAlert, Circle, CircleX, CircleCheck, LoaderCircle, Ban, CircleSlash, CircleMinus } from "lucide-vue-next"
import { ChevronLeft, ChevronRight, Plus, Search, Delete } from "lucide-vue-next"
import InvoiceDialog from '@/components/documents/InvoiceDialog.vue'
import Fuse from 'fuse.js';
import { Input } from '@/components/ui/input'
import 'vue-sonner/style.css'
import { Toaster, toast } from 'vue-sonner'
import { toast } from 'vue-sonner'
import { testToast } from '@/lib/utils'
import SelectSeparator from '@/components/ui/select/SelectSeparator.vue'
const breadcrumbs: BreadcrumbItem[] = [{
@@ -181,80 +180,14 @@ const deleteInvoice = async (id: number) => {
}
}
const promise = () => new Promise((resolve) => setTimeout(resolve, 2000))
const testToast = () => {
let delay = 150
toast('Toast', {
description: "Description bjksad fkjlhsd fkjhsdf jkshdf jkashdf adskljhf ", action: {
label: 'Undo',
onClick: () => console.log('Undo')
}
})
setTimeout(() => {
toast.info('Info', {
action: {
label: 'Undo',
onClick: () => console.log('Undo')
}
})
}, delay * 1)
setTimeout(() => { toast.success('Success', { description: "description" }) }, delay * 2)
setTimeout(() => { toast.warning('Warning', { description: "description" }) }, delay * 3)
setTimeout(() => { toast.error('Error', { description: "description" }) }, delay * 4)
setTimeout(() => {
toast.promise(promise, {
loading: 'Loading...',
success: (data) => {
return `${data.name} toast has been added`;
},
error: (data: any) => 'Error',
})
}, delay * 5)
}
</script>
<template>
<!-- TODO: Toaster -> App Vue -->
<!-- :duration="120000" -->
<Toaster position="top-right" :expand="true" closeButton :visible-toasts="6" :offset="'1rem'" :toastOptions="{
unstyled: true,
classes: {
toast: 'bg-muted rounded-lg w-80 text-sm shadow-lg/20 p-3 pl-5 flex gap-3',
title: 'font-bold',
description: 'text-',
actionButton: 'bg-background hover:bg-accent border-1 rounded shadow text-foreground px-2 h-8 ml-auto whitespace-nowrap',
cancelButton: '',
closeButton: 'bg-background hover:bg-accent text-foreground stroke-2 rounded-full w-5 h-5 absolute -left-2 -top-2 flex justify-center items-center shadow-md',
info: 'text-blue-500 dark:text-blue-400',
error: 'text-white bg-red-500 dark:bg-red-800',
success: 'text-green-600 dark:text-green-500',
warning: 'text-white bg-amber-500! dark:bg-amber-600!',
}
}">
<template #loading-icon>
<div class="animate-spin">
<LoaderCircle />
</div>
</template>
<template #success-icon>
<CircleCheck />
</template>
<template #error-icon>
<Ban />
</template>
<template #info-icon>
<Info />
</template>
<template #warning-icon>
<CircleAlert />
</template>
</Toaster>
<Head title="Rechnungen" />
<AppLayout :breadcrumbs="breadcrumbs">
<div
class="flex h-full flex-1 flex-col gap-4 overflow-x-auto p-4 lg:p-8 print:bg-transparent print:p-0 print:m-0">