feat: add email sending via Mail API

This commit is contained in:
Pablo de la Torre Jamardo 2025-09-21 09:16:54 +02:00
parent 0141657e14
commit 6cd1f7d620
4 changed files with 49 additions and 22 deletions

10
package-lock.json generated
View File

@ -12,6 +12,7 @@
"vue": "^3.4.21", "vue": "^3.4.21",
"vue-loading-overlay": "^6.0.6", "vue-loading-overlay": "^6.0.6",
"vue-preloader": "^1.1.4", "vue-preloader": "^1.1.4",
"vue-toastification": "^2.0.0-rc.5",
"vue-typer": "^1.2.0", "vue-typer": "^1.2.0",
"vue3-typer": "^1.0.0" "vue3-typer": "^1.0.0"
}, },
@ -2963,6 +2964,15 @@
"node": ">=14" "node": ">=14"
} }
}, },
"node_modules/vue-toastification": {
"version": "2.0.0-rc.5",
"resolved": "https://registry.npmjs.org/vue-toastification/-/vue-toastification-2.0.0-rc.5.tgz",
"integrity": "sha512-q73e5jy6gucEO/U+P48hqX+/qyXDozAGmaGgLFm5tXX4wJBcVsnGp4e/iJqlm9xzHETYOilUuwOUje2Qg1JdwA==",
"license": "MIT",
"peerDependencies": {
"vue": "^3.0.2"
}
},
"node_modules/vue-typer": { "node_modules/vue-typer": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/vue-typer/-/vue-typer-1.2.0.tgz", "resolved": "https://registry.npmjs.org/vue-typer/-/vue-typer-1.2.0.tgz",

View File

@ -30,6 +30,7 @@
"vue": "^3.4.21", "vue": "^3.4.21",
"vue-loading-overlay": "^6.0.6", "vue-loading-overlay": "^6.0.6",
"vue-preloader": "^1.1.4", "vue-preloader": "^1.1.4",
"vue-toastification": "^2.0.0-rc.5",
"vue-typer": "^1.2.0", "vue-typer": "^1.2.0",
"vue3-typer": "^1.0.0" "vue3-typer": "^1.0.0"
}, },

View File

@ -2,7 +2,8 @@
import {ref} from 'vue' import {ref} from 'vue'
import {Github, Globe, Linkedin, Mail, MapPin, Phone} from 'lucide-vue-next' import {Github, Globe, Linkedin, Mail, MapPin, Phone} from 'lucide-vue-next'
import type {Profile} from '@/domain/models/Profile' import type {Profile} from '@/domain/models/Profile'
import { useToast } from "vue-toastification";
const toast = useToast();
defineProps<{ defineProps<{
profile: Profile profile: Profile
}>() }>()
@ -27,23 +28,37 @@ function getSocialIcon(platform) {
async function handleSubmit() { async function handleSubmit() {
isSubmitting.value = true isSubmitting.value = true
// Simulate form submission try {
await new Promise(resolve => setTimeout(resolve, 1000)) const payload = {
from: form.value.email,
subject: form.value.name,
body: form.value.message
}
// Here you would typically send the form data to your backend const response = await fetch(`${import.meta.env.VITE_MAIL_API_URL}/mail`, {
console.log('Form submitted:', form.value) method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
})
if (!response.ok) {
throw new Error(`Error en el envío: ${response.status}`)
}
// Reset form
form.value = { form.value = {
name: '', name: '',
email: '', email: '',
message: '' message: ''
} }
toast.success("✅ ¡Mensaje enviado correctamente! Te responderé pronto.")
} catch (error) {
toast.error("❌ Hubo un error al enviar el mensaje. Inténtalo de nuevo.")
} finally {
isSubmitting.value = false isSubmitting.value = false
}
// Show success message (you could use a toast notification)
alert('¡Mensaje enviado correctamente! Te responderé pronto.')
} }
</script> </script>
@ -117,15 +132,12 @@ async function handleSubmit() {
<!-- Contact Form --> <!-- Contact Form -->
<div class="bg-gray-50 dark:bg-gray-700 rounded-xl p-8"> <div class="bg-gray-50 dark:bg-gray-700 rounded-xl p-8">
<p class="m-5">¡Hola! Por el momento mi servidor SMTP está de vacaciones 😅.</p>
<p class="m-5">Si quieres contactarme, envíame un correo electrónico directamente y prometo responderte
rápido.</p>
<form @submit.prevent="handleSubmit" class="space-y-6"> <form @submit.prevent="handleSubmit" class="space-y-6">
<div> <div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2"> <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Nombre Nombre
</label> </label>
<input disabled <input
v-model="form.name" v-model="form.name"
type="text" type="text"
required required
@ -137,7 +149,7 @@ async function handleSubmit() {
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2"> <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Email Email
</label> </label>
<input disabled <input
v-model="form.email" v-model="form.email"
type="email" type="email"
required required
@ -149,7 +161,7 @@ async function handleSubmit() {
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2"> <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Mensaje Mensaje
</label> </label>
<textarea disabled <textarea
v-model="form.message" v-model="form.message"
rows="4" rows="4"
required required
@ -159,7 +171,7 @@ async function handleSubmit() {
<button <button
type="submit" type="submit"
:disabled="isSubmitting || 1===1" :disabled="isSubmitting"
class="w-full px-6 py-3 bg-purple-600 hover:bg-purple-700 disabled:opacity-50 text-white rounded-lg font-semibold transition-colors" class="w-full px-6 py-3 bg-purple-600 hover:bg-purple-700 disabled:opacity-50 text-white rounded-lg font-semibold transition-colors"
> >
{{ isSubmitting ? 'Enviando...' : 'Enviar Mensaje' }} {{ isSubmitting ? 'Enviando...' : 'Enviar Mensaje' }}

View File

@ -3,10 +3,14 @@ import "./style.css"
import VueTyper from 'vue3-typer' import VueTyper from 'vue3-typer'
import "vue3-typer/dist/vue-typer.css" import "vue3-typer/dist/vue-typer.css"
import App from "./App.vue"; import App from "./App.vue";
import Toast from "vue-toastification";
import "vue-toastification/dist/index.css";
const app = createApp(App) const app = createApp(App)
app.component('VueTyper', VueTyper) app.component('VueTyper', VueTyper)
app.use(Toast, {});
// Global error handler // Global error handler
app.config.errorHandler = (err, vm, info) => { app.config.errorHandler = (err, vm, info) => {
console.error("Global error:", err, info) console.error("Global error:", err, info)