Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(landing): contact form #1636

Merged
merged 16 commits into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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',
purusott marked this conversation as resolved.
Show resolved Hide resolved
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'),
)
purusott marked this conversation as resolved.
Show resolved Hide resolved
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">
purusott marked this conversation as resolved.
Show resolved Hide resolved
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 *
emilielr marked this conversation as resolved.
Show resolved Hide resolved
</Label>

<TextField
label="E-postadresse"
name="email"
purusott marked this conversation as resolved.
Show resolved Hide resolved
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"
purusott marked this conversation as resolved.
Show resolved Hide resolved
aria-label="Skriv her"
aria-required
{...getFormFeedbackForField('user', formState)}
/>
</div>
SelmaBergstrand marked this conversation as resolved.
Show resolved Hide resolved
<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({
emilielr marked this conversation as resolved.
Show resolved Hide resolved
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 = {
purusott marked this conversation as resolved.
Show resolved Hide resolved
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')
}
SelmaBergstrand marked this conversation as resolved.
Show resolved Hide resolved
}

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 />
purusott marked this conversation as resolved.
Show resolved Hide resolved
<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.