Implement AI Loading Messages and Enhance Locale Handling in Forms (#737)
- Added a new `AIFormLoadingMessages` component to display dynamic loading messages with emojis during form processing, improving user feedback. - Enhanced locale handling in `OpenCompleteForm.vue` to default to English if an invalid locale is provided, ensuring better user experience and error handling. These changes aim to provide clearer loading states and improve localization robustness in form components. Co-authored-by: Julien Nahum <julien@nahum.net>
This commit is contained in:
parent
7efa8ed9cb
commit
efb2aabe0a
|
|
@ -274,7 +274,11 @@ export default {
|
|||
watch: {
|
||||
'form.language': {
|
||||
handler(newLanguage) {
|
||||
this.setLocale(newLanguage)
|
||||
if (newLanguage && typeof newLanguage === 'string') {
|
||||
this.setLocale(newLanguage)
|
||||
} else {
|
||||
this.setLocale('en') // Default to English if invalid locale
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,97 @@
|
|||
<template>
|
||||
<div
|
||||
v-motion
|
||||
:initial="{ opacity: 0, height: 0 }"
|
||||
:enter="{ opacity: 1, height: 52, transition: { duration: 300, ease: 'easeOut' } }"
|
||||
:leave="{ opacity: 0, height: 0, transition: { duration: 300, ease: 'easeIn' } }"
|
||||
class="overflow-hidden w-full"
|
||||
>
|
||||
<div
|
||||
v-motion
|
||||
:initial="{ opacity: 0, scale: 0.95 }"
|
||||
:enter="{ opacity: 1, scale: 1, transition: { duration: 300 } }"
|
||||
class="flex items-center gap-3 bg-blue-50 px-4 py-3 rounded-lg w-full"
|
||||
>
|
||||
<div
|
||||
v-motion
|
||||
class="w-7 h-7 rounded-full bg-gradient-to-r from-blue-500 to-blue-300 flex items-center justify-center text-base"
|
||||
:initial="{ rotate: 0 }"
|
||||
:enter="{
|
||||
rotate: [-8, 8, -8, 8, 0],
|
||||
transition: {
|
||||
repeat: Infinity,
|
||||
duration: 1500,
|
||||
ease: 'easeInOut',
|
||||
times: [0, 0.25, 0.5, 0.75, 1]
|
||||
}
|
||||
}"
|
||||
>
|
||||
{{ currentEmoji }}
|
||||
</div>
|
||||
<span
|
||||
:key="currentMessage"
|
||||
v-motion
|
||||
:initial="{ opacity: 0, y: 5 }"
|
||||
:enter="{ opacity: 1, y: 0, transition: { duration: 200 } }"
|
||||
class="text-sm text-gray-700 font-medium"
|
||||
>
|
||||
{{ currentMessage }}
|
||||
</span>
|
||||
<span class="text-xs text-gray-500 ml-auto tabular-nums">{{ elapsedTime }}s</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const messages = [
|
||||
{ text: "Starting AI magic", emoji: "✨" },
|
||||
{ text: "Analyzing requirements", emoji: "🤔" },
|
||||
{ text: "Designing layout", emoji: "📐" },
|
||||
{ text: "Adding form fields", emoji: "📝" },
|
||||
{ text: "Fine-tuning validation", emoji: "🎯" },
|
||||
{ text: "Optimizing UX", emoji: "💫" },
|
||||
{ text: "Adding smart features", emoji: "🧠" },
|
||||
{ text: "Polishing design", emoji: "✨" },
|
||||
{ text: "Running final checks", emoji: "🔍" },
|
||||
{ text: "Almost ready", emoji: "🚀" },
|
||||
]
|
||||
|
||||
const currentMessage = ref(messages[0].text)
|
||||
const currentEmoji = ref(messages[0].emoji)
|
||||
const messageIndex = ref(0)
|
||||
const startTime = ref(Date.now())
|
||||
const elapsedTime = ref(0)
|
||||
|
||||
// Update message every 2.5 seconds
|
||||
const interval = setInterval(() => {
|
||||
const nextIndex = (messageIndex.value + 1) % messages.length
|
||||
messageIndex.value = nextIndex
|
||||
currentMessage.value = messages[nextIndex].text
|
||||
currentEmoji.value = messages[nextIndex].emoji
|
||||
}, 2500)
|
||||
|
||||
// Update elapsed time every second
|
||||
const timeInterval = setInterval(() => {
|
||||
elapsedTime.value = Math.floor((Date.now() - startTime.value) / 1000)
|
||||
}, 1000)
|
||||
|
||||
onUnmounted(() => {
|
||||
clearInterval(interval)
|
||||
clearInterval(timeInterval)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.animate-shimmer {
|
||||
animation: shimmer 2s infinite linear;
|
||||
}
|
||||
|
||||
@keyframes shimmer {
|
||||
0% {
|
||||
background-position: 200% 0;
|
||||
}
|
||||
100% {
|
||||
background-position: -200% 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -151,8 +151,14 @@
|
|||
help="Give us a description of the form you want to build (the more details the better)"
|
||||
placeholder="A simple contact form, with a name, email and message field"
|
||||
/>
|
||||
<div
|
||||
v-if="loading"
|
||||
class="my-4"
|
||||
>
|
||||
<AIFormLoadingMessages />
|
||||
</div>
|
||||
<v-button
|
||||
class="w-full"
|
||||
class="w-full mt-4"
|
||||
:loading="loading"
|
||||
@click.prevent="generateForm"
|
||||
>
|
||||
|
|
@ -166,7 +172,12 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import AIFormLoadingMessages from "~/components/open/forms/components/AIFormLoadingMessages.vue"
|
||||
|
||||
export default {
|
||||
components: {
|
||||
AIFormLoadingMessages,
|
||||
},
|
||||
props: {
|
||||
show: { type: Boolean, required: true },
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in New Issue