Skip to content

Commit

Permalink
feat: add contact form
Browse files Browse the repository at this point in the history
  • Loading branch information
Ravy committed Apr 9, 2022
1 parent 4cb1fb1 commit e6ff4a8
Show file tree
Hide file tree
Showing 9 changed files with 467 additions and 567 deletions.
18 changes: 9 additions & 9 deletions components/Navbar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,34 +5,34 @@
<nav class="hidden lg:flex max-w-7xl mx-auto items-center justify-between px-4 sm:px-6 py-6" aria-label="Global">
<div class="flex items-center flex-1">
<div class="flex items-center justify-between w-auto">
<a href="#">
<NuxtLink to="/">
<span class="sr-only">Vaccinator</span>
<img class="h-8 w-auto sm:h-10" src="~/assets/logo.svg" alt="" />
</a>
</NuxtLink>
</div>
<div class="space-x-8 flex ml-10">
<a v-for="(item,index) in navigation" :key="index" :href="item.href" class="text-base font-medium text-zinc-900 hover:text-zinc-600 dark:text-zinc-100 dark:hover:text-zinc-300">{{ item.name }}</a>
<NuxtLink v-for="(item,index) in navigation" :key="index" :to="item.href" class="text-base font-medium text-zinc-900 hover:text-zinc-600 dark:text-zinc-100 dark:hover:text-zinc-300">{{ item.name }}</NuxtLink>
</div>
</div>
<div class="flex items-center space-x-6">
<a v-show="cta.show" :href="cta.href" class="btn btn-secondary text-base"> {{ cta.name }} </a>
<NuxtLink v-show="cta.show" :to="cta.href" class="btn btn-secondary text-base"> {{ cta.name }} </NuxtLink>
</div>
</nav>

<!-- mobile -->
<div class="top-0 w-full" :class="{'h-screen': opened, 'bg-zinc-100 dark:bg-zinc-900': opened }">
<div class="inset-x-0 p-4 transition transform origin-top-right lg:hidden h-full">
<div class="flex flex-row justify-between items-center">
<a href="#">
<NuxtLink to="/">
<span class="sr-only">Vaccinator</span>
<img class="h-8 w-auto sm:h-10" src="~/assets/logo.svg" alt="" />
</a>
</NuxtLink>
<FaIcon :icon="opened ? 'xmark' : 'bars'" class="text-primary-9 cursor-pointer text-base" @click="toggle()"/>
</div>
<div class="bg-zinc-100 dark:bg-zinc-900 overflow-hidden h-full" :class="{hidden: !opened}">
<div class="pt-2 flex flex-col space-y-8 justify-center h-full items-center text-4xl">
<a v-for="item in navigation" :key="item.name" :href="item.href" class="font-medium text-primary-11 hover:text-primary-12">{{ item.name }}</a>
<a v-show="cta.show" :href="cta.href" class="btn btn-primary"> {{ cta.name }} </a>
<NuxtLink v-for="item in navigation" :key="item.name" :to="item.href" class="font-medium text-primary-11 hover:text-primary-12">{{ item.name }}</NuxtLink>
<NuxtLink v-show="cta.show" :to="cta.href" class="btn btn-primary"> {{ cta.name }} </NuxtLink>
</div>
</div>
</div>
Expand Down Expand Up @@ -65,7 +65,7 @@ export default {
},
{
name: "Contact",
href: "mailto:[email protected]"
href: "/contact"
}
],
opened: false,
Expand Down
8 changes: 8 additions & 0 deletions layouts/default.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<template>
<div>
<Navbar />
<Wrapper>
<slot />
</Wrapper>
</div>
</template>
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,17 @@
"start": "node .output/server/index.mjs"
},
"devDependencies": {
"@nuxtjs/tailwindcss": "5",
"@nuxtjs/color-mode": "^3.0.2",
"@nuxtjs/tailwindcss": "5",
"@tailwindcss/forms": "^0.5.0",
"nuxt3": "latest"
},
"dependencies": {
"@aero/http": "^1.1.1",
"@fortawesome/fontawesome-svg-core": "^6.1.1",
"@fortawesome/free-solid-svg-icons": "^6.1.1",
"@fortawesome/vue-fontawesome": "^3.0.0-5",
"@hcaptcha/vue3-hcaptcha": "^1.0.1",
"@radix-ui/colors": "^0.1.8"
}
}
115 changes: 115 additions & 0 deletions pages/contact.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
<template>
<div class="mt-8 sm:mt-12 lg:mt-16 mb-8">
<h1 class="text-5xl font-extrabold py-6">Get in touch</h1>

<main class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="col-span-1">
<label for="first-name" class="block text-sm font-bold py-2">Name</label>
<input type="text" placeholder="" class="rounded-md bg-zinc-100 dark:bg-zinc-900 w-full" required v-model="name"/>
</div>

<div class="col-span-1">
<label for="email" class="block text-sm font-bold py-2">Email</label>
<input type="email" placeholder="" class="rounded-md bg-zinc-100 dark:bg-zinc-900 w-full" required v-model="email"/>
</div>

<div class="col-span-1 md:col-span-2">
<label for="subject" class="block text-sm font-bold py-2">Subject</label>
<input type="text" placeholder="" class="rounded-md bg-zinc-100 dark:bg-zinc-900 w-full" required v-model="subject"/>
</div>

<div class="col-span-1 md:col-span-2">
<label for="message" class="block text-sm font-bold py-2">Message</label>
<textarea rows="4" type="text" placeholder="" class="rounded-md bg-zinc-100 dark:bg-zinc-900 w-full" required v-model="message"/>
</div>

<div class="col-span-1 md:col-span-2">
<vue-hcaptcha sitekey="84bf7ae5-a5c3-48de-9bb9-3aff727b0926" :theme="$colorMode.value" @verify="captchaSolved"></vue-hcaptcha>
</div>

<div class="col-span-1 md:col-span-2 flex flex-col md:flex-row gap-8">
<p class="dark:text-zinc-600 text-zinc-500 max-w-md">
When submitting this form, you voluntarily provide us with personal information, including your name, email address, and message.
We will use this information to respond to your inquiry and may store it for future correspondence.
</p>
<button type="submit" @click="submit" class="mt-2 w-full md:w-auto h-auto my-auto inline-flex items-center justify-center px-6 py-3 border border-transparent rounded-md shadow-sm text-base font-medium text-white bg-primary-9 hover:bg-primary-10">Submit</button>
</div>
</main>

<div class="transition ease-in-out duration-100" :class="{hidden: !alert.visible}">
<div class="rounded-md p-4 my-8" :class="alert.success ? ['bg-success-3', 'dark:bg-success-12'] : ['bg-error-3', 'dark:bg-error-12']">
<div class="flex flex-row items-center justify-center">
<div class="flex-shrink-0">
<FaIcon class="h-5 w-5" :class="alert.success ? ['text-success-10', 'dark:text-success-8'] : ['text-error-10', 'dark:text-error-8']" aria-hidden="true" :icon="alert.success ? 'check' : 'exclamation'"/>
</div>
<div class="ml-3">
<p class="text-sm" :class="alert.success ? ['text-success-9', 'dark:text-success-7'] : ['text-error-9', 'dark:text-error-7']">{{alert.message}}</p>
</div>
</div>
</div>
</div>
</div>
</template>

<script setup>
import VueHcaptcha from '@hcaptcha/vue3-hcaptcha'
</script>

<script>
const required = ['name', 'email', 'subject', 'message'];
const emailReg = /^\S+@\S+\.\S+$/;
export default {
data() {
return {
name: '',
email: '',
subject: '',
message: '',
captcha: '',
alert: {
visible: false,
message: 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Aliquid pariatur, ipsum similique veniam quo totam eius aperiam dolorum.',
success: true
}
}
},
methods: {
captchaSolved(token) {
this.captcha = token;
},
async submit() {
const data = {
name: this.name,
email: this.email,
subject: this.subject,
message: this.message,
captcha: this.captcha
};
const missing = required.filter(prop => !data[prop]);
if (missing.length) {
this.alert.visible = true;
this.alert.message = `Please fill out the ${missing[0]} field.`;
this.alert.success = false;
} else if (!emailReg.test(this.email)) {
this.alert.visible = true;
this.alert.message = 'Please enter a valid email address.';
this.alert.success = false;
} else if (!this.captcha) {
this.alert.visible = true;
this.alert.message = 'Please solve the captcha.';
this.alert.success = false;
} else {
const res = await $fetch('/api/contact', { method: 'post', body: data });
this.alert.visible = true;
this.alert.message = res.message;
this.alert.success = res.success;
}
}
}
}
</script>
5 changes: 1 addition & 4 deletions pages/index.vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
<template>
<div>
<Navbar />
<Wrapper>
<Hero />
</Wrapper>
<Hero/>
</div>
</template>
4 changes: 3 additions & 1 deletion plugins/fa.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { library } from '@fortawesome/fontawesome-svg-core'
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'

import { faBars, faXmark } from '@fortawesome/free-solid-svg-icons'
import { faBars, faXmark, faCheck, faExclamation } from '@fortawesome/free-solid-svg-icons'

library.add(faBars)
library.add(faXmark)
library.add(faCheck)
library.add(faExclamation)

export default defineNuxtPlugin(nuxtApp => {
nuxtApp.vueApp.component('FaIcon', FontAwesomeIcon)
Expand Down
Loading

0 comments on commit e6ff4a8

Please sign in to comment.