Right now is I need to pass a function in the index to trigger the flash message like this
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Requests\Admin\SampleRequest;
use App\Models\Sample;
use Illuminate\Http\Request;
use App\Repositories\Contracts\SampleRepositoryInterface;
use Inertia\Inertia;
class SampleController extends Controller
{
public function __construct(protected SampleRepositoryInterface $sampleRepository){}
public function index (Request $request){
$samples = $this->sampleRepository->getSamples($request);
return Inertia::render('Sample/Index', [
'samples' => $samples,
'flash' => $this->flash()
]);
}
public function store(SampleRequest $request){
$this->sampleRepository->create($request->all());
return redirect()->route('sample.index')->with('success', 'Sample successfully added');
}
public function flash(){
return [
'info' => session('info'),
'success' => session('success'),
'danger' => session('danger'),
'warning' => session('warning'),
'light' => session('light'),
'dark' => session('dark'),
];
}
}
The case is I put the Banner component in the LayoutAuthenticated.vue
<script setup>
import { mdiForwardburger, mdiBackburger, mdiMenu } from '@mdi/js'
import { ref, watch } from 'vue'
import { router, usePage } from '@inertiajs/vue3';
import menuAside from '@/menuAside.js'
import menuNavBar from '@/menuNavBar.js'
import { useDarkModeStore } from '@/Stores/darkMode.js'
import BaseIcon from '@/Components/BaseIcon.vue'
import FormControl from '@/Components/FormControl.vue'
import NavBar from '@/Components/NavBar.vue'
import NavBarItemPlain from '@/Components/NavBarItemPlain.vue'
import AsideMenu from '@/Components/AsideMenu.vue'
import FooterBar from '@/Components/FooterBar.vue'
import Banner from '@/Components/Banner.vue'
const layoutAsidePadding = 'xl:pl-60'
const darkModeStore = useDarkModeStore()
const isAsideMobileExpanded = ref(false)
const isAsideLgActive = ref(false)
const page = usePage();
const flashMessage = page.props.flash
console.log(flashMessage);
router.on('navigate', () => {
isAsideMobileExpanded.value = false
isAsideLgActive.value = false
})
const menuClick = (event, item) => {
if (item.isToggleLightDark) {
darkModeStore.set()
}
if (item.isLogout) {
router.post(route('logout'))
}
}
</script>
<template>
<div :class="{
'overflow-hidden lg:overflow-visible': isAsideMobileExpanded
}">
<div :class="[layoutAsidePadding, { 'ml-60 lg:ml-0': isAsideMobileExpanded }]"
class="pt-14 min-h-screen w-screen transition-position lg:w-auto bg-gray-50 dark:bg-slate-800 dark:text-slate-100">
<NavBar :menu="menuNavBar" :class="[layoutAsidePadding, { 'ml-60 lg:ml-0': isAsideMobileExpanded }]"
@menu-click="menuClick">
<NavBarItemPlain display="flex lg:hidden"
@click.prevent="isAsideMobileExpanded = !isAsideMobileExpanded">
<BaseIcon :path="isAsideMobileExpanded ? mdiBackburger : mdiForwardburger" size="24" />
</NavBarItemPlain>
<NavBarItemPlain display="hidden lg:flex xl:hidden" @click.prevent="isAsideLgActive = true">
<BaseIcon :path="mdiMenu" size="24" />
</NavBarItemPlain>
<NavBarItemPlain use-margin>
<FormControl placeholder="Search (ctrl+k)" ctrl-k-focus transparent borderless />
</NavBarItemPlain>
</NavBar>
<AsideMenu :is-aside-mobile-expanded="isAsideMobileExpanded" :is-aside-lg-active="isAsideLgActive"
:menu="menuAside" @menu-click="menuClick" @aside-lg-close-click="isAsideLgActive = false" />
<Banner :flash-messages="flashMessage" />
<slot />
<FooterBar>
Get more with
<a href=" https://tailwind-vue.justboil.me/" target="_blank" class="text-blue-600">Premium version</a>
</FooterBar>
</div>
</div>
</template>
and this is how I set the Banner.vue
<script setup>
import { defineProps, onMounted } from 'vue';
const props = defineProps({
flashMessages: {
type: Object,
default: () => ({}),
},
});
const getBannerClass = (type) => {
const typeMap = {
success: 'flash-message__success',
info: 'flash-message__info',
danger: 'flash-message__danger',
warning: 'flash-message__warning',
light: 'flash-message__light',
secondary: 'flash-message__secondary',
dark: 'flash-message__dark',
};
return `flash-message ${typeMap[type] || 'flash-message__default'}`;
};
const handleRemoveMessage = (type) => {
props.flashMessages[type] = '';
};
onMounted(() => {
// Clear messages after 5 seconds
for (const type in props.flashMessages) {
if (props.flashMessages[type]) {
setTimeout(() => {
handleRemoveMessage(type);
}, 5000);
}
}
});
</script>
<template>
<div>
<div v-for="(message, type) in props.flashMessages" :key="type">
<div :class="getBannerClass(type)" v-if="message && type">
<button type="button" class="close">x</button>
<strong>{{ message }}</strong>
</div>
</div>
</div>
</template>
<style scoped>
.flash-message {
padding: 10px;
margin-bottom: 10px;
display: flex;
justify-content: space-between;
align-items: center;
border-radius: 4px;
}
.flash-message__success {
background-color: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.flash-message__info {
background-color: #d1ecf1;
color: #0c5460;
border: 1px solid #bee5eb;
}
.flash-message__danger {
background-color: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.flash-message__warning {
background-color: #fff3cd;
color: #856404;
border: 1px solid #ffeeba;
}
/* Light Theme */
.flash-message__light {
background-color: #fff3cd;
color: #856404;
border: 1px solid #ffeeba;
}
/* Dark Theme */
.flash-message__dark {
background-color: #343a40;
color: #adb5bd;
border: 1px solid #212529;
}
/* Secondary Theme */
.flash-message__secondary {
background-color: #d1ecf1;
color: #0c5460;
border: 1px solid #bee5eb;
}
.flash-message__default {
background-color: #f0f0f0;
color: #333;
border: 1px solid #ccc;
}
.flash-message__text {
margin: 0;
}
.flash-message__close {
cursor: pointer;
}
</style>