Skip to content

Commit

Permalink
Contact Us functionality saving to a datanbase
Browse files Browse the repository at this point in the history
  • Loading branch information
scosman committed Feb 28, 2024
1 parent f051c7b commit 10cb750
Show file tree
Hide file tree
Showing 5 changed files with 276 additions and 0 deletions.
14 changes: 14 additions & 0 deletions database_migration.sql
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,20 @@ create table stripe_customers (
);
alter table stripe_customers enable row level security;

-- Create a table for "Contact Us" form submissions
-- Limit RLS policies -- only server side access
create table contact_requests (
id uuid primary key default gen_random_uuid(),
updated_at timestamp with time zone,
first_name text,
last_name text,
email text,
phone text,
company_name text,
message_body text
);
alter table contact_requests enable row level security;

-- This trigger automatically creates a profile entry when a new user signs up via Supabase Auth.
-- See https://supabase.com/docs/guides/auth/managing-user-data#using-triggers for more details.
create function public.handle_new_user()
Expand Down
33 changes: 33 additions & 0 deletions src/DatabaseDefinitions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,39 @@ export type Json =
export interface Database {
public: {
Tables: {
contact_requests: {
Row: {
company_name: string | null
email: string | null
first_name: string | null
id: string
last_name: string | null
message_body: string | null
phone: string | null
updated_at: Date | null
}
Insert: {
company_name?: string | null
email?: string | null
first_name?: string | null
id?: string
last_name?: string | null
message_body?: string | null
phone?: string | null
updated_at?: Date | null
}
Update: {
company_name?: string | null
email?: string | null
first_name?: string | null
id?: string
last_name?: string | null
message_body?: string | null
phone?: string | null
updated_at?: Date | null
}
Relationships: []
}
profiles: {
Row: {
avatar_url: string | null
Expand Down
1 change: 1 addition & 0 deletions src/routes/(marketing)/+layout.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
<a class="link link-hover mb-1" href="/">Overview</a>
<a class="link link-hover my-1" href="/pricing">Pricing</a>
<a class="link link-hover my-1" href="/blog">Blog</a>
<a class="link link-hover my-1" href="/contact_us">Contact Us</a>
<a
class="link link-hover my-1"
href="https://github.com/CriticalMoments/CMSaasStarter">Github</a
Expand Down
71 changes: 71 additions & 0 deletions src/routes/(marketing)/contact_us/+page.server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { fail } from "@sveltejs/kit"

/** @type {import('./$types').Actions} */
export const actions = {
submitContactUs: async ({ request, locals: { supabaseServiceRole } }) => {
const formData = await request.formData()
const errors: { [fieldName: string]: string } = {}

const firstName = formData.get("first_name")?.toString() ?? ""
if (firstName.length < 2) {
errors["first_name"] = "First name is required"
}
if (firstName.length > 500) {
errors["first_name"] = "First name too long"
}

const lastName = formData.get("last_name")?.toString() ?? ""
if (lastName.length < 2) {
errors["last_name"] = "Last name is required"
}
if (lastName.length > 500) {
errors["last_name"] = "Last name too long"
}

const email = formData.get("email")?.toString() ?? ""
if (email.length < 6) {
errors["email"] = "Email is required"
} else if (email.length > 500) {
errors["email"] = "Email too long"
} else if (!email.includes("@") || !email.includes(".")) {
errors["email"] = "Invalid email"
}

const company = formData.get("company")?.toString() ?? ""
if (company.length > 500) {
errors["company"] = "Company too long"
}

const phone = formData.get("phone")?.toString() ?? ""
if (phone.length > 100) {
errors["phone"] = "Phone number too long"
}

const message = formData.get("message")?.toString() ?? ""
if (message.length > 2000) {
errors["message"] = "Message too long (" + message.length + " of 2000)"
}

console.log("errors:", errors)
if (Object.keys(errors).length > 0) {
return fail(400, { errors })
}

// Save to database
const { error: insertError } = await supabaseServiceRole
.from("contact_requests")
.insert({
first_name: firstName,
last_name: lastName,
email,
company_name: company,
phone,
message_body: message,
updated_at: new Date(),
})

if (insertError) {
return fail(500, { errors: { _: "Error saving" } })
}
},
}
157 changes: 157 additions & 0 deletions src/routes/(marketing)/contact_us/+page.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
<script lang="ts">
import { enhance, applyAction } from "$app/forms"
import type { SubmitFunction } from "@sveltejs/kit"
let errors: { [fieldName: string]: string } = {}
let loading = false
let showSuccess = false
const formFields = [
{
id: "first_name",
label: "First Name *",
inputType: "text",
autocomplete: "given-name",
},
{
id: "last_name",
label: "Last Name *",
inputType: "text",
autocomplete: "family-name",
},
{
id: "email",
label: "Email *",
inputType: "email",
autocomplete: "email",
},
{
id: "phone",
label: "Phone Number",
inputType: "tel",
autocomplete: "tel",
},
{
id: "company",
label: "Company Name",
inputType: "text",
autocomplete: "organization",
},
{
id: "message",
label: "Message",
inputType: "textarea",
autocomplete: "off",
},
]
const handleSubmit: SubmitFunction = () => {
loading = true
errors = {}
return async ({ update, result }) => {
await update({ reset: false })
await applyAction(result)
loading = false
console.log(result)
if (result.type === "success") {
showSuccess = true
} else if (result.type === "failure") {
errors = result.data?.errors ?? {}
} else if (result.type === "error") {
errors = { _: "An error occurred. Please check inputs and try again." }
}
}
}
</script>

<div
class="flex flex-col lg:flex-row mx-auto my-4 min-h-[70vh] place-items-center lg:place-items-start place-content-center"
>
<div
class="max-w-[400px] lg:max-w-[500px] flex flex-col place-content-center p-4 lg:mr-8 lg:mb-8 lg:min-h-[70vh]"
>
<div class="px-6">
<h1 class="text-2xl lg:text-4xl font-bold mb-4">Contact Us</h1>
<p class="text-lg">Talk to one of our service professionals to:</p>
<ul class="list-disc list-outside pl-6 py-4 space-y-1">
<li class="">Get a live demo</li>
<li class="">Discuss your specific needs</li>
<li>Get a quote</li>
<li>Answer any technical questions you have</li>
</ul>
<p>Once you complete the form, we'll reach out to you! *</p>
<p class="text-sm pt-8">
*Not really for this demo page, but you should say something like that
😉
</p>
</div>
</div>

<div
class="flex flex-col flex-grow m-4 lg:ml-10 min-w-[300px] stdphone:min-w-[360px] max-w-[400px] place-content-center"
>
{#if showSuccess}
<div class="flex flex-col place-content-center lg:min-h-[70vh]">
<div
class="card card-bordered shadow-lg py-6 px-6 mx-2 lg:mx-0 lg:p-6 mb-10"
>
<div class="text-2xl font-bold mb-4">Thank you!</div>
<p class="">We've received your message and will be in touch soon.</p>
</div>
</div>
{:else}
<div class="card card-bordered shadow-lg p-4 pt-6 mx-2 lg:mx-0 lg:p-6">
<form
class="form-widget flex flex-col"
method="POST"
action="?/submitContactUs"
use:enhance={handleSubmit}
>
{#each formFields as field}
<label for={field.id}>
<div class="flex flex-row">
<div class="text-base font-bold">{field.label}</div>
{#if errors[field.id]}
<div class="text-red-600 flex-grow text-sm ml-2 text-right">
{errors[field.id]}
</div>
{/if}
</div>
{#if field.inputType === "textarea"}
<textarea
id={field.id}
name={field.id}
autocomplete={field.autocomplete}
rows={4}
class="{errors[field.id]
? 'input-error'
: ''} h-24 input-sm mt-1 input input-bordered w-full mb-3 text-base py-4"
></textarea>
{:else}
<input
id={field.id}
name={field.id}
type={field.inputType}
autocomplete={field.autocomplete}
class="{errors[field.id]
? 'input-error'
: ''} input-sm mt-1 input input-bordered w-full mb-3 text-base py-4"
/>
{/if}
</label>
{/each}

{#if Object.keys(errors).length > 0}
<p class="text-red-600 text-sm mb-2">
Please resolve above issues.
</p>
{/if}

<button class="btn btn-primary {loading ? 'btn-disabled' : ''}"
>{loading ? "Submitting" : "Submit"}</button
>
</form>
</div>
{/if}
</div>
</div>

0 comments on commit 10cb750

Please sign in to comment.