Dynamic et responsive comme je les aime

main
anulax1225 ago%!(EXTRA string=4 months)
parent 5f00d6188c
commit 6cd3411795
  1. 12
      app/Http/Controllers/ServerController.php
  2. 7
      public/icons/menu.svg
  3. 7
      resources/js/Components/NavLink.vue
  4. 8
      resources/js/Components/ResponsiveNavLink.vue
  5. 43
      resources/js/Layouts/Layout.vue
  6. 6
      resources/js/Pages/Home.vue
  7. 297
      resources/js/Pages/Server/Show.vue

@ -164,11 +164,13 @@ public function makePublic(Request $request)
public function destroy(Request $request)
{
$server = Server::where("uuid", $request->id)->first();
try {
$container = new Container($server->container);
$data = $container->inspect()->State;
if($data->Running || $data->Paused || $data->Restarting) $container->kill();
} catch(Exception) { return redirect()->back()->withErrors(["error" => "Problème pour éteindre le serveur. Réessayer!"]); }
if($server->container) {
try {
$container = new Container($server->container);
$data = $container->inspect()->State;
if($data->Running || $data->Paused || $data->Restarting) $container->kill();
} catch(Exception) { return redirect()->back()->withErrors(["error" => "Problème pour éteindre le serveur. Réessayer!"]); }
}
$server->exposedPorts()->update([ "usable" => true ]);
$server->exposedPorts()->detach();
$server->delete();

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Menu / Menu_Alt_01">
<path id="Vector" d="M12 17H19M5 12H19M5 7H19" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 397 B

@ -14,8 +14,11 @@ const props = defineProps({
const classes = computed(() =>
props.active
? 'inline-flex items-center px-1 pt-1 border-b-2 border-indigo-400 text-sm font-medium leading-5 text-textColor-900 focus:outline-none focus:border-indigo-700 transition duration-150 ease-in-out'
: 'inline-flex items-center px-1 pt-1 border-b-2 border-transparent text-sm font-medium leading-5 text-textColor-500 hover:text-textColor-700 hover:border-gray-300 focus:outline-none focus:text-textColor-700 focus:border-gray-300 transition duration-150 ease-in-out',
? 'flex items-center px-1 pt-1 border-b-2 border-blue-400 text-md font-bold leading-5' +
' text-textColor-50 focus:outline-none focus:border-blue-700 transition duration-150 ease-in-out'
: 'flex items-center px-1 pt-1 border-b-2 border-transparent text-md font-medium ' +
'leading-5 text-textColor-200 hover:text-textColor-100 hover:border-blue-500 focus:outline-none ' +
'focus:text-textColor-700 focus:border-gray-300 transition duration-150 ease-in-out',
);
</script>

@ -14,8 +14,12 @@ const props = defineProps({
const classes = computed(() =>
props.active
? 'block w-full ps-3 pe-4 py-2 border-l-4 border-indigo-400 text-start text-base font-medium text-indigo-700 bg-indigo-50 focus:outline-none focus:text-indigo-800 focus:bg-indigo-100 focus:border-indigo-700 transition duration-150 ease-in-out'
: 'block w-full ps-3 pe-4 py-2 border-l-4 border-transparent text-start text-base font-medium text-textColor-600 hover:text-textColor-800 hover:bg-gray-50 hover:border-gray-300 focus:outline-none focus:text-textColor-800 focus:bg-gray-50 focus:border-gray-300 transition duration-150 ease-in-out',
? 'block w-full ps-3 pe-4 py-2 border-l-4 border-blue-400 text-start '+
'text-base font-medium text-blue-700 bg-blue-50 focus:outline-none focus:text-blue-800 '+
'focus:bg-blue-100 focus:border-blue-700 transition duration-150 ease-in-out'
: 'block w-full ps-3 pe-4 py-2 border-l-4 border-transparent text-start text-base font-medium text-textColor-600 '+
'hover:text-textColor-800 hover:bg-gray-50 hover:border-gray-300 focus:outline-none focus:text-textColor-800 focus:bg-gray-50'+
' focus:border-gray-300 transition duration-150 ease-in-out',
);
</script>

@ -1,12 +1,16 @@
<script setup>
import { Link, usePage } from '@inertiajs/vue3';
import NavLink from '@/Components/NavLink.vue';
import { onMounted, onUpdated, ref, watch } from 'vue';
import ResponsiveNavLink from '@/Components/ResponsiveNavLink.vue';
const user = usePage().props.auth.user;
const banner = usePage().props.banner;
const message = ref(usePage().props.errors.message);
const error = ref(usePage().props.errors.error);
const isFocus = ref(false);
onUpdated(() => {
if(message.value != usePage().props.errors.message) {
message.value = usePage().props.errors.message;
@ -31,24 +35,39 @@ usePage().props.errors.error = null;
</script>
<template>
<nav class="fixed top-0 right-0 left-0 h-16 w-full z-40 bg-gray-950/45">
<div class="w-full h-full flex items-center justify-between px-[10%]">
<div class="flex text-lg text-textColor-100 items-center font-medium">
<Link :href="route('home')" class="mr-2"><img src="/img/logo.png" class="h-[6.8rem]"></Link>
<Link :href="route('home')" class="mr-5">Home</Link>
<Link :href="route('servers.create')" class="mr-5">Spawn server</Link>
<Link v-if="user" :href="'/servers'">Management</Link>
<nav class="fixed top-0 right-0 left-0 h-16 w-full z-50 bg-gray-950/45">
<div class="w-full h-full flex items-center justify-between lg:px-[10%] md:px-[5%]">
<div class="flex text-lg text-textColor-100 items-center font-medium h-full">
<Link :href="route('home')" class="md:mr-2"><img src="/img/logo.png" class="md:h-[6.8rem] h-[6.3rem]"></Link>
<NavLink :href="route('home')" :active="route().current('home')" class="mr-5 h-full md:flex items-center hidden">Home</NavLink>
<NavLink :href="route('servers.create')" :active="route().current('servers.create')" class="mr-5 h-full md:flex items-center hidden">Spawn server</NavLink>
<NavLink v-if="user"
:href="route('servers.index')" :active="route().current('servers.index') || route().current('servers.show')"
class="h-full md:flex items-center hidden">Management</NavLink>
</div>
<div v-if="!user" class="flex text-lg text-textColor-300 items-center font-medium">
<Link :href="route('login')" class=""><img src="/icons/user.svg" class="h-8 invert"></Link>
<div v-if="!user" class="md:flex hidden text-lg text-textColor-300 items-center font-medium md:mr-5 mr-1 h-full">
<NavLink :href="route('login')" :active="route().current('login')" class="mr-5 md:flex items-center hidden h-full">Log in</NavLink>
</div>
<div v-else class="flex text-lg text-textColor-300 items-center font-medium">
<Link :href="route('logout')"><img src="/icons/logout.svg" class="h-8 invert"></Link>
<div v-else class="md:flex hidden text-lg text-textColor-300 items-center font-medium md:mr-5 mr-1 h-full">
<NavLink :href="route('logout')" :active="route().current('logout')" class="mr-5 h-full md:flex items-center hidden">Log out</NavLink>
</div>
<button @click="isFocus = !isFocus" class="md:hidden">
<img src="/icons/menu.svg" class="h-12 invert mr-1">
</button>
</div>
</nav>
<div v-show="isFocus" class="md:hidden fixed top-0 right-0 left-0 bottom-0 pt-16 w-full z-40 bg-gray-800">
<div class="flex flex-col gap-1">
<ResponsiveNavLink :href="route('home')" :active="route().current('home')" class="mr-5">Home</ResponsiveNavLink>
<ResponsiveNavLink :href="route('servers.create')" :active="route().current('servers.create')" class="mr-5">Spawn server</ResponsiveNavLink>
<ResponsiveNavLink v-if="user" :href="route('servers.index')" :active="route().current('servers.index') || route().current('servers.show')">Management</ResponsiveNavLink>
<ResponsiveNavLink v-if="!user" :href="route('login')" :active="route().current('login')" class="items-center">Log in</ResponsiveNavLink>
<ResponsiveNavLink v-else :href="route('logout')" :active="route().current('logout')" class="items-center">Log out</ResponsiveNavLink>
</div>
</div>
<main class="w-full max-h-full overflow-y-auto text-white">
<div v-if="banner" class="w-full h-80 flex items-center justify-between overflow-hidden">
<div v-if="banner" class="w-full md:h-80 flex items-center justify-between overflow-hidden">
<img :src="banner" class="w-full pb-20">
</div>
<div v-else class="pt-20"></div>

@ -21,13 +21,13 @@ const props = defineProps({
<Layout>
<header class="text-center my-10">
<h1 class="text-4xl font-bold fade-in flex justify-center items-center gap-2">
<img src="/icons/server-icon.svg" alt="Serveur" class="w-12 h-12 invert"> Bienvenue sur Hosting
<img src="/icons/server-icon.svg" alt="Serveur" class="md:block hidden w-12 h-12 invert"> Bienvenue sur Hosting
</h1>
<p class="text-gray-300 max-w-3xl mx-auto mt-4 fade-in">Un service gratuit permettant d'héberger facilement vos serveurs dans des conteneurs Docker sécurisés et performants.</p>
</header>
<p class="text-center text-2xl font-bold mb-4 fade-in">Nos Services</p>
<section class="flex justify-center mx-auto mb-10 slide-up">
<div v-for="service in props.services" class="relative flex items-center justify-center h-72 max-w-52 overflow-x-hidden bg-black w-full rounded-lg mx-5">
<section class="flex md:flex-row flex-col items-center md:justify-center justify-between mx-auto mb-10 slide-up">
<div v-for="service in props.services" class="relative flex items-center justify-center h-72 max-w-52 overflow-x-hidden bg-black w-full rounded-lg my-2 mx-5">
<img :src="service.image" class="object-cover h-full">
<!-- <p class="absolute bottom-0 right-0 left-0 p-1 text-center bg-gray-950/60 font-extrabold text-gray-300 text-2xl">{{ service.name }}</p> -->
</div>

@ -17,170 +17,179 @@ const form = useForm();
const editForm = useForm({ name: props.server.name });
const start = (uuid) => {
form.post(route("servers.start", uuid));
}
form.post(route("servers.start", uuid));
}
const stop = (uuid) => {
form.post(route("servers.stop", uuid));
}
form.post(route("servers.stop", uuid));
}
const del = (uuid) => {
if(prompt("Entrer le nom du server en confirmation.") == props.server.name) form.delete(route("servers.destroy", uuid));
if (prompt("Entrer le nom du server en confirmation.") == props.server.name) form.delete(route("servers.destroy", uuid));
}
const pub = (uuid) => {
if(props.server.public) form.post(route("servers.public", uuid));
else if (prompt("Entrer le nom du server en confirmation.") == props.server.name) form.post(route("servers.public", uuid));
if (props.server.public) form.post(route("servers.public", uuid));
else if (prompt("Entrer le nom du server en confirmation.") == props.server.name) form.post(route("servers.public", uuid));
}
const edit = () => {
console.log(editForm.name);
editForm.post(route("servers.update", props.server.uuid));
console.log(editForm.name);
editForm.post(route("servers.update", props.server.uuid));
}
const copie = () => {
navigator.clipboard.writeText('hosting.anulax.ch:' + props.server.ports[0]).then(() => alert("lien de connection copié!"))
navigator.clipboard.writeText('hosting.anulax.ch:' + props.server.ports[0]).then(() => alert("lien de connection copié!"))
}
</script>
<template>
<Head title="Spawn" />
<Layout>
<!-- En-tête -->
<header class="my-10 text-center">
<h1 class="text-4xl font-bold flex items-center justify-center gap-2 fade-in">
Dashboard du Serveur
</h1>
<p class="text-gray-300 mt-4 fade-in">
Gérez et visualisez les informations clés de votre serveur en un coup d'œil.
</p>
</header>
<!-- Cartes d'information -->
<main class="max-w-6xl mx-auto grid grid-cols-1 md:grid-cols-3 gap-6 mb-6 slide-up">
<!-- Carte : Informations Générales -->
<div class="bg-gray-800 rounded-lg shadow-lg p-6">
<h2 class="text-2xl font-semibold border-b border-gray-700 pb-2 mb-4 flex items-center gap-2">
<img src="/icons/server-icon.svg" alt="Général" class="w-6 h-6 rotate-on-hover invert">
Informations Générales
</h2>
<div class="space-y-3 text-sm">
<div class="flex justify-between">
<span class="font-medium">Nom</span>
<span class="text-green-400 font-bold">{{ props.server.name }}</span>
</div>
<div class="flex justify-between">
<span class="font-medium">Type de jeu</span>
<span>{{ props.server.service.name }}</span>
</div>
<div class="flex justify-between">
<span class="font-medium">Public</span>
<span>{{ props.server.public ? "Oui" : "Non" }}</span>
</div>
<div class="flex flex-col gap-2">
<div class="flex justify-between items-center mb-1">
<div class="flex items-center gap-2">
<span class="font-medium">Lien de connection</span>
<Head title="Spawn" />
<Layout>
<!-- En-tête -->
<header class="my-10 text-center">
<h1 class="text-4xl font-bold flex items-center justify-center gap-2 fade-in">
Dashboard du Serveur
</h1>
<p class="text-gray-300 mt-4 fade-in">
Gérez et visualisez les informations clés de votre serveur en un coup d'œil.
</p>
</header>
<!-- Cartes d'information -->
<main class="max-w-6xl mx-auto grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3 xl:gap-6 mb-6 slide-up">
<!-- Carte : Informations Générales -->
<div class="bg-gray-800 rounded-lg shadow-lg p-6">
<h2 class="text-2xl font-semibold border-b border-gray-700 pb-2 mb-4 flex items-center gap-2">
<img src="/icons/server-icon.svg" alt="Général" class="w-6 h-6 rotate-on-hover invert">
Informations Générales
</h2>
<div class="space-y-3 text-sm">
<div class="flex justify-between">
<span class="font-medium">Nom</span>
<span class="text-green-400 font-bold">{{ props.server.name }}</span>
</div>
<div class="flex justify-between">
<span class="font-medium">Type de jeu</span>
<span>{{ props.server.service.name }}</span>
</div>
<div class="flex justify-between">
<span class="font-medium">Public</span>
<span>{{ props.server.public ? "Oui" : "Non" }}</span>
</div>
<div class="flex flex-col gap-2">
<div class="flex justify-between items-center mb-1">
<div class="flex items-center gap-2">
<span class="font-medium lg:hidden">Lien de connection</span>
<span class="font-medium xl:hidden lg:block hidden">Lien du server</span>
</div>
<div class="flex items-center gap-2">
<p class="text-green-400 hover:underline">
{{ props.server.ports.length && props.server.status.id < 3 ? "hosting.anulax.ch:" +
props.server.ports[0] : "Indisponible" }} </p>
<button v-show="props.server.ports.length && props.server.status.id < 3"
@click="copie"
class="bg-blue-500 hover:bg-blue-600 text-white font-bold p-1 rounded-lg shadow-lg glow-on-hover flex items-center">
<img src="/icons/copy-icon.svg" alt="Copier" class="w-5 h-5 invert">
</button>
</div>
</div>
</div>
</div>
</div>
<div class="flex items-center gap-2">
<p class="text-green-400 hover:underline">
{{ props.server.ports.length && props.server.status.id < 3 ? "hosting.anulax.ch:" + props.server.ports[0] : "Indisponible" }}
</p>
<button v-show="props.server.ports.length && props.server.status.id < 3" @click="copie" class="bg-blue-500 hover:bg-blue-600 text-white font-bold p-1 rounded-lg shadow-lg glow-on-hover flex items-center">
<img src="/icons/copy-icon.svg" alt="Copier" class="w-5 h-5 invert">
</button>
<!-- Carte : Activité -->
<div class="bg-gray-800 rounded-lg shadow-lg p-6">
<h2 class="text-2xl font-semibold border-b border-gray-700 pb-2 mb-4 flex items-center gap-2">
<img src="/icons/stats-icon.svg" alt="Activité" class="w-6 h-6 rotate-on-hover invert">Activité
</h2>
<div class="space-y-3 text-sm mb-4">
<div class="flex justify-between">
<span class="font-medium">Statut</span>
<span class="text-green-400 font-bold">{{ props.server.status.title }}</span>
</div>
<div class="flex justify-between">
<span class="font-medium">Dernier lancement</span>
<span class="text-green-400 font-bold">{{ props.server.start ? props.server.start : "Aucun"
}}</span>
</div>
<div class="flex justify-between">
<span class="font-medium">Dernier arrêt </span>
<span class="text-red-400 font-bold">{{ props.server.end ? props.server.end : "Aucun" }}</span>
</div>
</div>
<div class="flex gap-3">
<div @click="start(props.server.uuid)" v-if="props.server.status.id >= 3"
class="bg-green-500 hover:bg-green-600 text-white font-bold p-2 rounded-lg shadow-lg glow-on-hover flex items-center gap-2">
<img src="/icons/start-icon.svg" alt="Start" class="w-5 h-5 invert">
</div>
<div @click="stop(props.server.uuid)" v-else
class="bg-red-500 hover:bg-red-600 text-white font-bold p-2 rounded-lg shadow-lg glow-on-hover flex items-center gap-2">
<img src="/icons/stop-icon.svg" alt="Stop" class="w-5 h-5 invert">
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Carte : Activité -->
<div class="bg-gray-800 rounded-lg shadow-lg p-6">
<h2 class="text-2xl font-semibold border-b border-gray-700 pb-2 mb-4 flex items-center gap-2">
<img src="/icons/stats-icon.svg" alt="Activité" class="w-6 h-6 rotate-on-hover invert">Activité
</h2>
<div class="space-y-3 text-sm mb-4">
<div class="flex justify-between">
<span class="font-medium">Statut</span>
<span class="text-green-400 font-bold">{{ props.server.status.title }}</span>
</div>
<div class="flex justify-between">
<span class="font-medium">Dernier lancement</span>
<span class="text-green-400 font-bold">{{ props.server.start ? props.server.start : "Aucun" }}</span>
</div>
<div class="flex justify-between">
<span class="font-medium">Dernier arrêt </span>
<span class="text-red-400 font-bold">{{ props.server.end ? props.server.end : "Aucun" }}</span>
</div>
</div>
<div class="flex gap-3">
<div @click="start(props.server.uuid)" v-if="props.server.status.id >= 3" class="bg-green-500 hover:bg-green-600 text-white font-bold p-2 rounded-lg shadow-lg glow-on-hover flex items-center gap-2">
<img src="/icons/start-icon.svg" alt="Start" class="w-5 h-5 invert">
</div>
<div @click="stop(props.server.uuid)" v-else class="bg-red-500 hover:bg-red-600 text-white font-bold p-2 rounded-lg shadow-lg glow-on-hover flex items-center gap-2">
<img src="/icons/stop-icon.svg" alt="Stop" class="w-5 h-5 invert">
</div>
</div>
</div>
<!-- Carte : Hardware -->
<div class="bg-gray-800 rounded-lg shadow-lg p-6">
<h2 class="text-2xl font-semibold border-b border-gray-700 pb-2 mb-4 flex items-center gap-2">
<img src="/icons/hardware-icon.svg" alt="Hardware" class="w-6 h-6 rotate-on-hover invert">
Hardware(fake)
</h2>
<div class="space-y-3 text-sm">
<div class="flex justify-between">
<span class="font-medium">CPUs</span>
<span>1</span>
</div>
<div class="flex justify-between">
<span class="font-medium">Memory</span>
<span>4GB</span>
</div>
<div class="flex justify-between">
<span class="font-medium">Stockage</span>
<span>10GB</span>
</div>
</div>
</div>
</main>
<!-- Section Actions en haut -->
<section class="max-w-6xl mx-auto mb-10 fade-in">
<div class="flex justify-between items-center bg-gray-800 p-4 rounded-lg shadow-lg">
<div class="flex gap-3">
<button class="bg-blue-500 hover:bg-blue-600 text-white font-bold p-2 rounded-lg shadow-lg glow-on-hover flex items-center">
<img src="/icons/share-icon.svg" alt="Partager" class="w-5 h-5 invert">
</button>
</div>
<!-- Actions de gestion -->
<div class="flex gap-3 items-center">
<form v-show="isFocus" @submit.prevent="edit">
<TextInput
v-model="editForm.name"
type="text"
class="w-full px-3 py-1 rounded bg-gray-700 text-white"
/>
<button type="submit"></button>
<InputError :message="form.errors.name" class="text-left mt-1"></InputError>
</form>
<button @click="isFocus = !isFocus" class="bg-blue-500 hover:bg-blue-600 text-white font-bold p-2 rounded-lg shadow-lg glow-on-hover flex items-center gap-2">
<img src="/icons/edit-icon.svg" alt="Edit" class="w-5 h-5 invert">
</button>
<button v-if="props.server.public" @click="pub(props.server.uuid)" class="bg-blue-500 hover:bg-blue-600 text-white font-bold p-2 rounded-lg shadow-lg glow-on-hover flex items-center">
<img src="/icons/lock.svg" alt="Rendre Privé" class="w-5 h-5 invert">
</button>
<button v-else @click="pub(props.server.uuid)" class="bg-red-500 hover:bg-red-600 text-white font-bold p-2 rounded-lg shadow-lg glow-on-hover flex items-center">
<img src="/icons/public-icon.svg" alt="Rendre public" class="w-5 h-5 invert">
</button>
<button @click="del(props.server.uuid)" class="bg-red-500 hover:bg-red-600 text-white font-bold p-2 rounded-lg shadow-lg glow-on-hover flex items-center gap-2">
<img src="/icons/delete-icon.svg" alt="Delete" class="w-5 h-5 invert">
</button>
</div>
</div>
</section>
</Layout>
<!-- Carte : Hardware -->
<div class="bg-gray-800 rounded-lg shadow-lg p-6 lg:col-span-1 md:col-span-2 col-span-1">
<h2 class="text-2xl font-semibold border-b border-gray-700 pb-2 mb-4 flex items-center gap-2">
<img src="/icons/hardware-icon.svg" alt="Hardware" class="w-6 h-6 rotate-on-hover invert">
Hardware(fake)
</h2>
<div class="space-y-3 text-sm">
<div class="flex justify-between">
<span class="font-medium">CPUs</span>
<span>1</span>
</div>
<div class="flex justify-between">
<span class="font-medium">Memory</span>
<span>4GB</span>
</div>
<div class="flex justify-between">
<span class="font-medium">Stockage</span>
<span>10GB</span>
</div>
</div>
</div>
</main>
<!-- Section Actions en haut -->
<section class="max-w-6xl mx-auto mb-10 fade-in">
<div class="flex justify-between items-center bg-gray-800 p-4 rounded-lg shadow-lg">
<div class="flex gap-3">
<button
class="bg-blue-500 hover:bg-blue-600 text-white font-bold p-2 rounded-lg shadow-lg glow-on-hover flex items-center">
<img src="/icons/share-icon.svg" alt="Partager" class="w-5 h-5 invert">
</button>
</div>
<!-- Actions de gestion -->
<div class="flex gap-3 items-center">
<form v-show="isFocus" @submit.prevent="edit">
<TextInput v-model="editForm.name" type="text"
class="w-full px-3 py-1 rounded bg-gray-700 text-white" />
<button type="submit"></button>
<InputError :message="form.errors.name" class="text-left mt-1"></InputError>
</form>
<button @click="isFocus = !isFocus"
class="bg-blue-500 hover:bg-blue-600 text-white font-bold p-2 rounded-lg shadow-lg glow-on-hover flex items-center gap-2">
<img src="/icons/edit-icon.svg" alt="Edit" class="w-5 h-5 invert">
</button>
<button v-if="props.server.public" @click="pub(props.server.uuid)"
class="bg-blue-500 hover:bg-blue-600 text-white font-bold p-2 rounded-lg shadow-lg glow-on-hover flex items-center">
<img src="/icons/lock.svg" alt="Rendre Privé" class="w-5 h-5 invert">
</button>
<button v-else @click="pub(props.server.uuid)"
class="bg-red-500 hover:bg-red-600 text-white font-bold p-2 rounded-lg shadow-lg glow-on-hover flex items-center">
<img src="/icons/public-icon.svg" alt="Rendre public" class="w-5 h-5 invert">
</button>
<button @click="del(props.server.uuid)"
class="bg-red-500 hover:bg-red-600 text-white font-bold p-2 rounded-lg shadow-lg glow-on-hover flex items-center gap-2">
<img src="/icons/delete-icon.svg" alt="Delete" class="w-5 h-5 invert">
</button>
</div>
</div>
</section>
</Layout>
</template>
Loading…
Cancel
Save