Skip to content

Commit

Permalink
feat(landing): contact form (#1636)
Browse files Browse the repository at this point in the history
* feat(utils): new error messages

* feat(landing): contact form

* style(landing): contact form

* style(landing): webhook message

* feat(helm): slack secret

* style(landing): mobile friendly contact form

* feat(helm): slack webhook

* chore(components): delete unused file

* style(landing): webhook formatting

* style(contact): responsive contact form

* fix(contact): client and server validation

* fix(helm): correct reference

* chore(contact): refactor

* chore(contact): refactor

* chore(shared): delete unused

* chore(utils): refactor
  • Loading branch information
purusott authored Sep 25, 2024
1 parent b80cf6a commit a869992
Show file tree
Hide file tree
Showing 10 changed files with 289 additions and 35 deletions.
7 changes: 7 additions & 0 deletions tavla/app/(admin)/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,13 @@ export function getFormFeedbackForError(
variant: 'error',
}
}
case 'contact/message-missing': {
return {
form_type: 'user',
feedback: 'Vennligst legg igjen en melding.',
variant: 'error',
}
}
}

return {
Expand Down
124 changes: 124 additions & 0 deletions tavla/app/components/ContactForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
'use client'
import { TextArea, TextField } from '@entur/form'
import { Label, Paragraph } from '@entur/typography'
import { SubmitButton } from 'components/Form/SubmitButton'
import { postForm } from './actions'
import {
TFormFeedback,
getFormFeedbackForError,
getFormFeedbackForField,
} from 'app/(admin)/utils'
import { useState } from 'react'
import { FormError } from 'app/(admin)/components/FormError'
import { useToast } from '@entur/alert'
import { Expandable } from './Expandable'
import { usePostHog } from 'posthog-js/react'
import { isEmptyOrSpaces } from 'app/(admin)/edit/utils'
import { validEmail } from 'utils/email'
function ContactForm() {
const posthog = usePostHog()

const { addToast } = useToast()
const [isOpen, setIsOpen] = useState(false)
const [formState, setFormError] = useState<TFormFeedback | undefined>(
undefined,
)

const submit = async (data: FormData) => {
const email = data.get('email') as string
const message = data.get('message') as string

if (!validEmail(email))
return setFormError(getFormFeedbackForError('auth/missing-email'))

if (isEmptyOrSpaces(message))
return setFormError(
getFormFeedbackForError('contact/message-missing'),
)
const error = await postForm(formState, data)

if (error) return setFormError(error)
else {
setIsOpen(false)
setFormError(undefined)
addToast('Takk for tilbakemelding!')
}
}

return (
<div
className="flex items-center justify-center w-full xl:w-1/6 h-14"
onClick={() =>
isOpen
? posthog.capture('CONTACT_FORM_OPENED')
: setFormError(undefined)
}
>
<Expandable
title="Send oss en melding!"
isOpen={isOpen}
setIsOpen={setIsOpen}
>
<form
action={submit}
className="flex flex-col gap-4 p-4 sm:p-6 "
>
<Paragraph as="h1" margin="none" className="font-bold">
Vi setter stor pris på tilbakemeldinger og innspill, og
bistår gjerne hvis du vil ha hjelp til å komme i gang
med Tavla!
</Paragraph>
<div>
<Label
htmlFor="email"
className="font-bold"
aria-required
>
E-post *
</Label>

<TextField
label="E-postadresse"
name="email"
id="email"
aria-label="E-postadresse"
{...getFormFeedbackForField('email', formState)}
/>
</div>
<div>
<Label
htmlFor="message"
className="font-bold"
aria-required
>
Melding *
</Label>
<TextArea
name="message"
id="message"
label="Melding"
aria-label="Skriv her"
aria-required
{...getFormFeedbackForField('user', formState)}
/>
</div>
<Paragraph margin="none">
Hvis du ønsker å legge ved bilder, kan du sende en
e-post til [email protected].
</Paragraph>
<FormError
{...getFormFeedbackForField('general', formState)}
/>
<SubmitButton
variant="primary"
width="fluid"
aria-label="Send"
>
Send
</SubmitButton>
</form>
</Expandable>
</div>
)
}
export { ContactForm }
36 changes: 36 additions & 0 deletions tavla/app/components/Expandable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { IconButton } from '@entur/button'
import { DownArrowIcon, UpArrowIcon } from '@entur/icons'
import { Heading5 } from '@entur/typography'
import { Dispatch, SetStateAction } from 'react'

function Expandable({
title,
isOpen,
setIsOpen,
children,
}: {
title: string
isOpen: boolean
setIsOpen: Dispatch<SetStateAction<boolean>>
children: React.ReactNode
}) {
return (
<div className="fixed bottom-0 md:right-3 w-full lg:w-1/2 xl:w-1/3 z-10 drop-shadow-lg ">
<div
onClick={() => setIsOpen(!isOpen)}
className="flex justify-between items-center px-6 py-4 bg-blue80 w-full rounded-t"
>
<Heading5 margin="none" className=" sm:text-base !text-lg">
{title}
</Heading5>
<IconButton className="border-0!">
{isOpen ? <DownArrowIcon /> : <UpArrowIcon />}
</IconButton>
</div>
{isOpen && (
<div className="rounded-b p-4 bg-blue90">{children}</div>
)}
</div>
)
}
export { Expandable }
24 changes: 0 additions & 24 deletions tavla/app/components/FloatingContact.tsx

This file was deleted.

110 changes: 110 additions & 0 deletions tavla/app/components/actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
'use server'

import { isEmptyOrSpaces } from 'app/(admin)/edit/utils'
import { TFormFeedback, getFormFeedbackForError } from 'app/(admin)/utils'
import { validEmail } from 'utils/email'
async function postForm(prevState: TFormFeedback | undefined, data: FormData) {
const email = data.get('email') as string
const message = data.get('message') as string

if (!validEmail(email)) return getFormFeedbackForError('auth/missing-email')

if (isEmptyOrSpaces(message))
return getFormFeedbackForError('contact/message-missing')

const timestamp = Math.floor(Date.now() / 1000)

const payload = {
blocks: [
{
type: 'header',
text: {
type: 'plain_text',
text: ':email: Ny melding :email:',
emoji: true,
},
},
{
type: 'divider',
},
{
type: 'rich_text',
elements: [
{
type: 'rich_text_section',
elements: [
{
type: 'date',
timestamp: timestamp,
format: '{date_num} klokken {time}',
fallback: 'timey',
},
],
},
],
},
{
type: 'section',
block_id: 'email',
fields: [
{
type: 'mrkdwn',
text: `*Fra:* \n${email}`,
},
],
},

{
type: 'rich_text',
elements: [
{
type: 'rich_text_section',
elements: [
{
type: 'text',
text: 'Melding:',
style: {
bold: true,
},
},
],
},
],
},
{
type: 'rich_text',
elements: [
{
type: 'rich_text_section',
elements: [
{
type: 'text',
text: `${message}`,
},
],
},
],
},
],
}

try {
const url = process.env.SLACK_WEBHOOK_URL
if (!url) throw Error('Could not find url')
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
})

if (!response.ok) {
throw Error('Error in request')
}
} catch (e: unknown) {
return getFormFeedbackForError('general')
}
}

export { postForm }
4 changes: 2 additions & 2 deletions tavla/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ import { Metadata } from 'next'
import dynamic from 'next/dynamic'
import { EnturToastProvider, PHProvider } from './providers'
import { Footer } from './(admin)/components/Footer'
import { FloatingContact } from './components/FloatingContact'
import { TopNavigation } from './(admin)/components/TopNavigation'
import { cookies } from 'next/headers'
import { verifySession } from './(admin)/utils/firebase'
import { ContactForm } from './components/ContactForm'

export const metadata: Metadata = {
title: 'Entur Tavla',
Expand Down Expand Up @@ -52,7 +52,7 @@ async function RootLayout({ children }: { children: ReactNode }) {
<TopNavigation loggedIn={loggedIn} />
<PostHogPageView />
{children}
<FloatingContact />
<ContactForm />
<Footer />
</body>
</EnturToastProvider>
Expand Down
7 changes: 7 additions & 0 deletions tavla/helm/tavla/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@ common:
secretKeyRef:
name: backend
key: api-key

- name: SLACK_WEBHOOK_URL
valueFrom:
secretKeyRef:
name: slack-webhook
key: url

probes:
enabled: true
spec:
Expand Down
1 change: 0 additions & 1 deletion tavla/src/Shared/types/featureFlag.ts

This file was deleted.

3 changes: 3 additions & 0 deletions tavla/src/Shared/utils/email.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function validEmail(string: string) {
return new RegExp(/^[\w.-]+@([\w-]+\.)+[\w-]{2,4}$/g).test(string)
}
8 changes: 0 additions & 8 deletions tavla/src/Shared/utils/featureFlags.ts

This file was deleted.

0 comments on commit a869992

Please sign in to comment.