feat: simple Formspree integration

This commit is contained in:
Serhii Bedrytskyi committed 2022-12-19 16:04:32 +02:00
1 parent b52bd93487
commit 413a2dcd06
7 files changed
+57 -55

No files matched your search

+8
View File
@@ -33,6 +33,14 @@ export default defineAppConfig({
label: 'LinkedIn',
href: 'https://www.linkedin.com/company/nuxtlabs'
}
},
form: {
successMessage: 'Message sent. Thank you!'
},
integrations: {
formspree: {
url: 'https://formspree.io/f/xdovneqb'
}
}
}
})
+2 -2
View File
@@ -100,8 +100,8 @@ css({
display: 'flex',
justifyContent: 'center',
'@xs': {
gridColumn: 'span 1 / span 1',
gridColumnStart: 12,
gridColumn: 'span 2 / span 2',
gridColumnStart: 11,
justifyContent: 'flex-end',
}
}
+3
View File
@@ -49,6 +49,9 @@ css({
py: 'var(--header-padding)',
'.menu': {
position: 'absolute',
':hover': {
color: '{color.primary.500}',
},
'.left &&': {
right: 0
},
+4 -4
View File
@@ -1,14 +1,14 @@
<script setup lang="ts">
defineProps({
onClick: {
type: Function,
required: true
type: {
type: String,
default: 'submit'
}
})
</script>
<template>
<button @click="onClick()">
<button :type="type">
<slot />
</button>
</template>
+3
View File
@@ -26,6 +26,9 @@ css({
'--color-mode-switcher-size': '24px',
width: 'var(--color-mode-switcher-size)',
height: 'var(--color-mode-switcher-size)',
':hover': {
color: '{color.primary.500}',
},
svg: {
width: '100%',
height: '100%',
+36 -43
View File
@@ -1,20 +1,15 @@
<script setup lang="ts">
import type { PropType } from 'vue'
import type { Field, ContactField } from '../types/contact'
import type { Field } from '../types/contact'
const alpine = useAppConfig().alpine
const status = ref()
const props = defineProps({
sendButton: {
type: String,
default: 'Send message'
},
email: {
type: String,
required: true
},
subject: {
type: String,
default: 'Contact from Alpine Theme !'
},
fields: {
type: Array as PropType<Field[]>,
default: () => [
@@ -49,57 +44,55 @@ const props = defineProps({
required: true,
layout: 'big'
}
] /* ,
validator: (fields: Field[]) => {
for (const field of fields) {
if (!field.name) {
return false
}
if (!field.required) {
return false
}
if (!field.layout) {
return false
}
}
return true
} */
]
}
})
const form = reactive(props.fields.map(v => ({ ...v, data: '' })))
const onSend = async () => {
const formatedData: ContactField[] = []
await form.forEach((field) => {
const data: ContactField = {
name: field.name,
data: field.data
const onSend = async (e) => {
e.preventDefault()
const data = new FormData(e.target)
fetch(e.target.action, {
method: e.target.method,
body: data,
headers: {
'Accept': 'application/json'
}
formatedData.push(data)
}).then(response => {
if (response.ok) {
status.value = alpine.form.successMessage
e.target.reset()
} else {
// Handle errors from API
response.json().then(data => {
if (Object.hasOwn(data, 'errors')) {
console.error(data["errors"].map(error => error["message"]).join(", "))
} else {
console.error("There was a problem submitting your form")
}
})
}
}).catch(error => {
// Catch all other errors
console.error("There was a problem submitting your form")
})
// eslint-disable-next-line no-console
console.log(formatedData)
}
</script>
<template>
<div class="contact-form">
<form class="contact-form" @submit="onSend" method="POST" :action="alpine.integrations.formspree.url">
<div class="inputs">
<Input
v-for="(field, index) in form"
:key="index"
v-model="field.data"
:field="field"
/>
<Input v-for="(field, index) in form" :key="index" v-model="field.data" :field="field" />
</div>
<div>
<Button :on-click="() => onSend()">
{{ sendButton }}
<Button type="submit">
{{ status ? status : sendButton }}
</Button>
</div>
</div>
</form>
</template>
<style scoped lang="ts">
+1 -6
View File
@@ -4,9 +4,4 @@ export interface Field {
placeholder?: string;
label?: string;
required?: boolean;
}
export interface ContactField {
name: string;
data: string;
}
}