Merge branch 'tailwind'

This commit is contained in:
jb-alvarado 2024-04-09 21:55:52 +02:00
commit 35705aa868
6 changed files with 187 additions and 174 deletions

View File

@ -5,90 +5,29 @@
class="alert fixed top-20 right-3 z-40 w-auto min-w-64 max-w-[90%] md:max-w-[50%] xl:max-w-[40%]"
:class="`alert-${indexStore.alertVariant}`"
>
<div v-html="alertIcon(indexStore.alertVariant)" />
<SvgIcon :name="indexStore.alertVariant" />
{{ indexStore.alertMsg }}
</div>
<!-- HACK: init alerts to get the right colors, can be remove when bug is fixed -->
<div class="alert alert-success w-auto max-w-[700px] justify-start py-2 rounded hidden">
<div v-html="alertIcon('success')" />
<SvgIcon name="success" />
<span class="truncate w-full">bla bla bla</span>
</div>
<div class="alert alert-warning w-auto max-w-[700px] justify-start py-2 rounded hidden">
<div v-html="alertIcon('warning')" />
<SvgIcon name="warning" />
<span class="truncate w-full">bla bla bla</span>
</div>
<div class="alert alert-info w-auto max-w-[700px] justify-start py-2 rounded hidden">
<div v-html="alertIcon('info')" />
<SvgIcon name="info" />
<span class="truncate w-full">bla bla bla</span>
</div>
<div class="alert alert-error w-auto max-w-[700px] justify-start py-2 rounded hidden">
<div v-html="alertIcon('error')" />
<SvgIcon name="error" />
<span class="truncate w-full">bla bla bla</span>
</div>
</template>
<script setup lang="ts">
const indexStore = useIndex()
function alertIcon(variance: string) {
switch (variance) {
case 'error':
return `<svg
xmlns="http://www.w3.org/2000/svg"
class="stroke-current shrink-0 h-6 w-6"
fill="none"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>`
case 'success':
return ` <svg
xmlns="http://www.w3.org/2000/svg"
class="stroke-current shrink-0 h-6 w-6"
fill="none"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>`
case 'warning':
return `<svg
xmlns="http://www.w3.org/2000/svg"
class="stroke-current shrink-0 h-6 w-6"
fill="none"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
/>
</svg>`
default:
return ` <svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
class="stroke-current shrink-0 w-6 h-6"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>`
}
}
</script>

View File

@ -6,35 +6,12 @@
<div class="navbar-end w-1/5 grow">
<label class="swap swap-rotate me-2 md:hidden">
<input type="checkbox" @change="toggleDarkTheme" :checked="indexStore.darkMode" />
<svg class="swap-on fill-current w-5 h-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22">
<path
d="M5.64,17l-.71.71a1,1,0,0,0,0,1.41,1,1,0,0,0,1.41,0l.71-.71A1,1,0,0,0,5.64,17ZM5,12a1,1,0,0,0-1-1H3a1,1,0,0,0,0,2H4A1,1,0,0,0,5,12Zm7-7a1,1,0,0,0,1-1V3a1,1,0,0,0-2,0V4A1,1,0,0,0,12,5ZM5.64,7.05a1,1,0,0,0,.7.29,1,1,0,0,0,.71-.29,1,1,0,0,0,0-1.41l-.71-.71A1,1,0,0,0,4.93,6.34Zm12,.29a1,1,0,0,0,.7-.29l.71-.71a1,1,0,1,0-1.41-1.41L17,5.64a1,1,0,0,0,0,1.41A1,1,0,0,0,17.66,7.34ZM21,11H20a1,1,0,0,0,0,2h1a1,1,0,0,0,0-2Zm-9,8a1,1,0,0,0-1,1v1a1,1,0,0,0,2,0V20A1,1,0,0,0,12,19ZM18.36,17A1,1,0,0,0,17,18.36l.71.71a1,1,0,0,0,1.41,0,1,1,0,0,0,0-1.41ZM12,6.5A5.5,5.5,0,1,0,17.5,12,5.51,5.51,0,0,0,12,6.5Zm0,9A3.5,3.5,0,1,1,15.5,12,3.5,3.5,0,0,1,12,15.5Z"
/>
</svg>
<svg class="swap-off fill-current w-5 h-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22">
<path
d="M21.64,13a1,1,0,0,0-1.05-.14,8.05,8.05,0,0,1-3.37.73A8.15,8.15,0,0,1,9.08,5.49a8.59,8.59,0,0,1,.25-2A1,1,0,0,0,8,2.36,10.14,10.14,0,1,0,22,14.05,1,1,0,0,0,21.64,13Zm-9.5,6.69A8.14,8.14,0,0,1,7.08,5.22v.27A10.15,10.15,0,0,0,17.22,15.63a9.79,9.79,0,0,0,2.1-.22A8.11,8.11,0,0,1,12.14,19.73Z"
/>
</svg>
<SvgIcon name="swap-on" classes="w-5 h-5" />
<SvgIcon name="swap-off" classes="w-5 h-5" />
</label>
<div class="dropdown dropdown-end z-50">
<div tabindex="0" role="button" class="btn btn-ghost md:hidden">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-5 w-5"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4 6h16M4 12h8m-8 6h16"
/>
</svg>
<SvgIcon name="burger" classes="w-5 h-5" />
</div>
<ul class="menu menu-sm dropdown-content mt-1 z-[1] p-2 shadow bg-base-100 rounded-box w-52">
<li v-for="item in menuItems" :key="item.name" class="bg-base-100 rounded-md">
@ -61,7 +38,9 @@
</details>
</li>
<li class="bg-base-100 rounded-md">
<button class="h-[27px] text-base" exactActiveClass="is-active" @click="logout()">Logout</button>
<button class="h-[27px] text-base" exactActiveClass="is-active" @click="logout()">
Logout
</button>
</li>
</ul>
</div>
@ -69,7 +48,11 @@
<div class="navbar-end hidden md:flex w-4/5 min-w-[600px]">
<ul class="menu menu-sm menu-horizontal px-1">
<li v-for="item in menuItems" :key="item.name" class="bg-base-100 rounded-md p-0">
<NuxtLink :to="item.link" class="px-2 h-[27px] relative text-base text-base-content" activeClass="is-active">
<NuxtLink
:to="item.link"
class="px-2 h-[27px] relative text-base text-base-content"
activeClass="is-active"
>
<span>
{{ item.name }}
</span>
@ -97,26 +80,8 @@
<li class="p-0">
<label class="swap swap-rotate">
<input type="checkbox" @change="toggleDarkTheme" :checked="indexStore.darkMode" />
<svg
class="swap-on fill-current w-5 h-5"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 22 22"
>
<path
d="M5.64,17l-.71.71a1,1,0,0,0,0,1.41,1,1,0,0,0,1.41,0l.71-.71A1,1,0,0,0,5.64,17ZM5,12a1,1,0,0,0-1-1H3a1,1,0,0,0,0,2H4A1,1,0,0,0,5,12Zm7-7a1,1,0,0,0,1-1V3a1,1,0,0,0-2,0V4A1,1,0,0,0,12,5ZM5.64,7.05a1,1,0,0,0,.7.29,1,1,0,0,0,.71-.29,1,1,0,0,0,0-1.41l-.71-.71A1,1,0,0,0,4.93,6.34Zm12,.29a1,1,0,0,0,.7-.29l.71-.71a1,1,0,1,0-1.41-1.41L17,5.64a1,1,0,0,0,0,1.41A1,1,0,0,0,17.66,7.34ZM21,11H20a1,1,0,0,0,0,2h1a1,1,0,0,0,0-2Zm-9,8a1,1,0,0,0-1,1v1a1,1,0,0,0,2,0V20A1,1,0,0,0,12,19ZM18.36,17A1,1,0,0,0,17,18.36l.71.71a1,1,0,0,0,1.41,0,1,1,0,0,0,0-1.41ZM12,6.5A5.5,5.5,0,1,0,17.5,12,5.51,5.51,0,0,0,12,6.5Zm0,9A3.5,3.5,0,1,1,15.5,12,3.5,3.5,0,0,1,12,15.5Z"
/>
</svg>
<svg
class="swap-off fill-current w-5 h-5"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 22 22"
>
<path
d="M21.64,13a1,1,0,0,0-1.05-.14,8.05,8.05,0,0,1-3.37.73A8.15,8.15,0,0,1,9.08,5.49a8.59,8.59,0,0,1,.25-2A1,1,0,0,0,8,2.36,10.14,10.14,0,1,0,22,14.05,1,1,0,0,0,21.64,13Zm-9.5,6.69A8.14,8.14,0,0,1,7.08,5.22v.27A10.15,10.15,0,0,0,17.22,15.63a9.79,9.79,0,0,0,2.1-.22A8.11,8.11,0,0,1,12.14,19.73Z"
/>
</svg>
<SvgIcon name="swap-on" classes="w-5 h-5" />
<SvgIcon name="swap-off" classes="w-5 h-5" />
</label>
</li>
</ul>

View File

@ -1,6 +1,10 @@
<template>
<div class="z-50 fixed top-0 bottom-0 left-0 right-0 flex justify-center items-center bg-black/30 overflow-y-auto">
<div class="flex flex-col bg-base-100 w-[800px] min-w-[300px] max-w-[90%] h-[680px] rounded-md p-5 shadow-xl">
<div
class="z-50 fixed top-0 bottom-0 w-full h-full left-0 right-0 flex justify-center items-center bg-black/30 overflow-y-auto"
>
<div
class="relative flex flex-col bg-base-100 w-[800px] min-w-[300px] max-w-[90vw] h-[680px] rounded-md p-5 shadow-xl"
>
<div class="font-bold text-lg">Generate Program</div>
<div class="h-[calc(100%-95px)] mt-3">
@ -14,25 +18,28 @@
@change="advancedGenerator = false"
checked
/>
<div role="tabpanel" class="tab-content pt-3">
<div role="tabpanel" class="tab-content w-full pt-3">
<div class="w-full">
<nav class="breadcrumbs px-3 pt-0">
<ul>
<li v-for="(crumb, index) in mediaStore.folderCrumbs" :key="index">
<button
v-if="
mediaStore.folderCrumbs.length > 1 &&
mediaStore.folderCrumbs.length - 1 > index
"
@click="mediaStore.getTree(crumb.path, true)"
>
<i class="bi-folder-fill me-1" />
{{ crumb.text }}
</button>
<span v-else><i class="bi-folder-fill me-1" />{{ crumb.text }}</span>
</li>
</ul>
</nav>
<div class="grid">
<nav class="breadcrumbs px-3 pt-0">
<ul>
<li v-for="(crumb, index) in mediaStore.folderCrumbs" :key="index">
<button
v-if="
mediaStore.folderCrumbs.length > 1 &&
mediaStore.folderCrumbs.length - 1 > index
"
@click="mediaStore.getTree(crumb.path, true)"
>
<i class="bi-folder-fill me-1" />
{{ crumb.text }}
</button>
<span v-else><i class="bi-folder-fill me-1" />{{ crumb.text }}</span>
</li>
</ul>
</nav>
</div>
<ul class="h-[475px] border border-my-gray rounded overflow-auto bg-base-300 m-1 py-1">
<li
class="even:bg-base-200 px-2 w-full"
@ -89,7 +96,7 @@
/>
<div role="tabpanel" class="tab-content pt-3">
<div class="w-full">
<div class="grid grid-cols-2 px-3 pt-0">
<div class="grid grid-cols-[auto_48px] px-3 pt-0">
<nav class="breadcrumbs pt-0">
<ul>
<li v-for="(crumb, index) in mediaStore.folderCrumbs" :key="index">
@ -119,13 +126,15 @@
</div>
</div>
<div
class="h-[475px] border border-my-gray rounded grid grid-cols-[300px_auto] bg-base-300 m-1"
class="h-[475px] border border-my-gray rounded grid bg-base-300 m-1"
:class="width < 740 ? 'grid-cols-1' : 'grid-cols-[300px_auto]'"
>
<Sortable
:list="mediaStore.folderList.folders"
:options="templateBrowserSortOptions"
item-key="uid"
class="overflow-auto border-e border-my-gray py-1"
class="overflow-auto py-1 border-my-gray"
:class="width < 740 ? 'h-[240px] border-b' : 'border-e'"
tag="ul"
>
<template #item="{ element, index }">
@ -155,11 +164,11 @@
</li>
</template>
</Sortable>
<ul class="overflow-auto p-2">
<ul class="overflow-auto px-1 pb-1">
<li
v-for="item in template.sources"
:key="item.start"
class="flex flex-col gap-1 justify-center items-center border border-my-gray rounded p-1"
class="flex flex-col gap-1 justify-center items-center border border-my-gray rounded mt-1 p-1"
>
<div class="grid grid-cols-[50px_67px_70px_67px_50px] join">
<div
@ -201,7 +210,11 @@
@add="addFolderToTemplate($event, item)"
>
<template #item="{ element, index }">
<li :id="`path_${index}`" class="draggable grabbing py-0 even:bg-base-200 px-2" :key="index">
<li
:id="`path_${index}`"
class="draggable grabbing py-0 even:bg-base-200 px-2"
:key="index"
>
<i class="bi-folder-fill" />
{{ element.split(/[\\/]+/).pop() }}
</li>
@ -245,11 +258,7 @@
>
Cancel
</button>
<button
type="button"
class="btn btn-sm btn-primary join-item"
@click="generatePlaylist(), close()"
>
<button type="button" class="btn btn-sm btn-primary join-item" @click="generatePlaylist(), close()">
Ok
</button>
</div>
@ -260,6 +269,7 @@
<script setup lang="ts">
const { $dayjs } = useNuxtApp()
const { width } = useWindowSize({ initialWidth: 800 })
const authStore = useAuth()
const configStore = useConfig()
const indexStore = useIndex()
@ -282,7 +292,12 @@ const advancedGenerator = ref(false)
const selectedFolders = ref([] as string[])
const generateFromAll = ref(false)
const template = ref({
sources: [],
sources: [{
start: configStore.configPlayout.playlist.day_start,
duration: '02:00:00',
shuffle: false,
paths: [],
}],
} as Template)
const templateBrowserSortOptions = {

108
components/SvgIcon.vue Normal file
View File

@ -0,0 +1,108 @@
<template>
<svg
v-if="name === 'swap-on'"
class="swap-on fill-current"
:class="classes"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 22 22"
>
<path
d="M5.64,17l-.71.71a1,1,0,0,0,0,1.41,1,1,0,0,0,1.41,0l.71-.71A1,1,0,0,0,5.64,17ZM5,12a1,1,0,0,0-1-1H3a1,1,0,0,0,0,2H4A1,1,0,0,0,5,12Zm7-7a1,1,0,0,0,1-1V3a1,1,0,0,0-2,0V4A1,1,0,0,0,12,5ZM5.64,7.05a1,1,0,0,0,.7.29,1,1,0,0,0,.71-.29,1,1,0,0,0,0-1.41l-.71-.71A1,1,0,0,0,4.93,6.34Zm12,.29a1,1,0,0,0,.7-.29l.71-.71a1,1,0,1,0-1.41-1.41L17,5.64a1,1,0,0,0,0,1.41A1,1,0,0,0,17.66,7.34ZM21,11H20a1,1,0,0,0,0,2h1a1,1,0,0,0,0-2Zm-9,8a1,1,0,0,0-1,1v1a1,1,0,0,0,2,0V20A1,1,0,0,0,12,19ZM18.36,17A1,1,0,0,0,17,18.36l.71.71a1,1,0,0,0,1.41,0,1,1,0,0,0,0-1.41ZM12,6.5A5.5,5.5,0,1,0,17.5,12,5.51,5.51,0,0,0,12,6.5Zm0,9A3.5,3.5,0,1,1,15.5,12,3.5,3.5,0,0,1,12,15.5Z"
/>
</svg>
<svg
v-else-if="name === 'swap-off'"
class="swap-off fill-current"
:class="classes"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 22 22"
>
<path
d="M21.64,13a1,1,0,0,0-1.05-.14,8.05,8.05,0,0,1-3.37.73A8.15,8.15,0,0,1,9.08,5.49a8.59,8.59,0,0,1,.25-2A1,1,0,0,0,8,2.36,10.14,10.14,0,1,0,22,14.05,1,1,0,0,0,21.64,13Zm-9.5,6.69A8.14,8.14,0,0,1,7.08,5.22v.27A10.15,10.15,0,0,0,17.22,15.63a9.79,9.79,0,0,0,2.1-.22A8.11,8.11,0,0,1,12.14,19.73Z"
/>
</svg>
<svg
v-else-if="name === 'error'"
xmlns="http://www.w3.org/2000/svg"
class="stroke-current shrink-0 h-6 w-6"
fill="none"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
<svg
v-if="name === 'success'"
xmlns="http://www.w3.org/2000/svg"
class="stroke-current shrink-0 h-6 w-6"
fill="none"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
<svg
v-if="name === 'warning'"
xmlns="http://www.w3.org/2000/svg"
class="stroke-current shrink-0 h-6 w-6"
fill="none"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
/>
</svg>
<svg
v-if="name === 'info'"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
class="stroke-current shrink-0 w-6 h-6"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
<svg
v-else-if="name === 'burger'"
xmlns="http://www.w3.org/2000/svg"
:class="classes"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h8m-8 6h16" />
</svg>
</template>
<script setup lang="ts">
defineProps({
name: {
type: String,
default: '',
},
classes: {
type: String,
default: '',
},
})
</script>

View File

@ -4,7 +4,7 @@
<Menu />
</div>
<main :class="authStore.isLogin && route.name !== 'index' ? 'h-[calc(100%-52px)]': 'h-full'">
<main :class="authStore.isLogin && route.name !== 'index' ? 'h-[calc(100%-52px)]' : 'h-full'">
<slot />
</main>
@ -13,10 +13,18 @@
</template>
<script setup lang="ts">
const colorMode = useColorMode()
const configStore = useConfig()
const authStore = useAuth()
const indexStore = useIndex()
const route = useRoute()
await configStore.nuxtClientInit()
if (colorMode.value === 'dark') {
indexStore.darkMode = true
} else {
indexStore.darkMode = false
}
</script>

View File

@ -6,7 +6,7 @@
<NuxtLink to="/player" class="btn join-item btn-primary">Player</NuxtLink>
<NuxtLink to="/media" class="btn join-item btn-primary">Media</NuxtLink>
<NuxtLink to="/message" class="btn join-item btn-primary">Message</NuxtLink>
<NuxtLink to="logging" class="btn join-item btn-primary">Logging</NuxtLink>
<NuxtLink to="/logging" class="btn join-item btn-primary">Logging</NuxtLink>
<NuxtLink
v-if="authStore.role.toLowerCase() == 'admin'"
to="/configure"
@ -17,18 +17,8 @@
<button class="btn join-item btn-primary" @click="logout()">Logout</button>
<label class="join-item btn btn-primary swap swap-rotate me-2">
<input type="checkbox" @change="toggleDarkTheme" :checked="indexStore.darkMode" />
<svg class="swap-on fill-current w-5 h-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22">
<path
d="M5.64,17l-.71.71a1,1,0,0,0,0,1.41,1,1,0,0,0,1.41,0l.71-.71A1,1,0,0,0,5.64,17ZM5,12a1,1,0,0,0-1-1H3a1,1,0,0,0,0,2H4A1,1,0,0,0,5,12Zm7-7a1,1,0,0,0,1-1V3a1,1,0,0,0-2,0V4A1,1,0,0,0,12,5ZM5.64,7.05a1,1,0,0,0,.7.29,1,1,0,0,0,.71-.29,1,1,0,0,0,0-1.41l-.71-.71A1,1,0,0,0,4.93,6.34Zm12,.29a1,1,0,0,0,.7-.29l.71-.71a1,1,0,1,0-1.41-1.41L17,5.64a1,1,0,0,0,0,1.41A1,1,0,0,0,17.66,7.34ZM21,11H20a1,1,0,0,0,0,2h1a1,1,0,0,0,0-2Zm-9,8a1,1,0,0,0-1,1v1a1,1,0,0,0,2,0V20A1,1,0,0,0,12,19ZM18.36,17A1,1,0,0,0,17,18.36l.71.71a1,1,0,0,0,1.41,0,1,1,0,0,0,0-1.41ZM12,6.5A5.5,5.5,0,1,0,17.5,12,5.51,5.51,0,0,0,12,6.5Zm0,9A3.5,3.5,0,1,1,15.5,12,3.5,3.5,0,0,1,12,15.5Z"
/>
</svg>
<svg class="swap-off fill-current w-5 h-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22">
<path
d="M21.64,13a1,1,0,0,0-1.05-.14,8.05,8.05,0,0,1-3.37.73A8.15,8.15,0,0,1,9.08,5.49a8.59,8.59,0,0,1,.25-2A1,1,0,0,0,8,2.36,10.14,10.14,0,1,0,22,14.05,1,1,0,0,0,21.64,13Zm-9.5,6.69A8.14,8.14,0,0,1,7.08,5.22v.27A10.15,10.15,0,0,0,17.22,15.63a9.79,9.79,0,0,0,2.1-.22A8.11,8.11,0,0,1,12.14,19.73Z"
/>
</svg>
<SvgIcon name="swap-on" classes="w-5 h-5" />
<SvgIcon name="swap-off" classes="w-5 h-5" />
</label>
</div>
</div>
@ -60,21 +50,9 @@
<div
v-if="showLoginError"
role="alert"
class="alert error w-auto rounded z-2 h-12 p-[0.7rem]"
class="alert alert-error w-auto rounded z-2 h-12 p-[0.7rem]"
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="stroke-current shrink-0 h-6 w-6"
fill="none"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
<SvgIcon name="error" />
<span>{{ formError }}</span>
</div>
</div>