Skip to content
Open
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
5 changes: 0 additions & 5 deletions .vscode/settings.json

This file was deleted.

25 changes: 14 additions & 11 deletions apps/www/src/components/ui/atoms/button/button.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,36 @@ import {cn} from '@/utils/toolbox'
export const buttonSizes = ['small', 'medium', 'large'] as const
export const buttonShapes = ['square', 'rounded', 'circle'] as const
export const buttonVariants = ['default', 'link', 'ghost'] as const
export const buttonTones = ['primary', 'secondary'] as const
export const buttonTones = ['primary', 'secondary', 'slate'] as const
export const buttonAlignments = ['start', 'center', 'end'] as const

const toneClasses: Record<(typeof buttonTones)[number], Record<(typeof buttonVariants)[number], string>> = {
primary: {
default:
'bg-primary-800 text-white hover:bg-white hover:text-primary-800 focus-visible:ring-primary-800 \
focus-visible:outline-primary-200',
link: 'text-primary-800 focus-visible:outline-primary-600 focus-visible:underline',
'bg-sky-300 text-white text-slate-900 hover:bg-violet-300 focus-visible:ring-sky-300 focus-visible:outline-sky-300',
link: 'text-sky-300 focus-visible:outline-sky-200 focus-visible:underline',
ghost:
'text-primary-800 hover:bg-primary-100/30 focus-visible:outline-primary-600 \
data-[active=true]:underline data-[active=true]:decoration-primary-200 data-[active=true]:underline-offset-4 \
data-[active=true]:decoration-2'
'bg-transparent border-2 border-sky-300 text-sky-300 text-md \
hover:border-violet-300 hover:bg-transparent hover:text-violet-300'
},
secondary: {
default:
'text-white bg-secondary-500 hover:bg-secondary-600 focus-visible:ring-secondary-500 \
focus-visible:outline-secondary-600',
'text-white bg-secondary-500 hover:bg-secondary-600 focus-visible:ring-secondary-500 focus-visible:outline-secondary-600',
link: 'text-white data-[active=true]:text-white',
ghost: 'text-secondary-500 hover:bg-secondary-50 focus-visible:outline-secondary-600'
},
slate: {
default:
'text-slate-900 bg-slate-100 hover:bg-slate-200 focus-visible:ring-slate-100 focus-visible:outline-slate-100',
link: 'text-slate-400 data-[active=true]:text-white font-light',
ghost: 'text-slate-400 hover:bg-slate-50 focus-visible:outline-slate-100'
}
}

const sizeClasses: Record<(typeof buttonSizes)[number], string> = {
small: 'px-2.5 py-1.5 text-sm',
medium: 'px-3 py-2 text-sm',
large: 'px-3.5 py-2.5 text-sm'
medium: 'px-3 py-2.5 text-sm',
large: 'px-5 py-2.5 text-sm'
}

const shapeClasses: Record<(typeof buttonShapes)[number], string> = {
Expand Down
10 changes: 6 additions & 4 deletions apps/www/src/components/ui/atoms/inputs/input/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,31 @@ import React, {forwardRef} from 'react'
export type InputProps = {
id: string
label: string
labelSrOnly?: boolean
className?: string
type?: HTMLInputTypeAttribute
error?: string
placeholder?: string
}

const Input = forwardRef<HTMLInputElement, InputProps & Omit<JSX.IntrinsicElements['input'], 'type'>>(
({id, label, className, type = 'text', error, ...other}, ref) => {
({id, label, className, type = 'text', error, labelSrOnly, ...other}, ref) => {
return (
<div className={className}>
<label
htmlFor={id}
className='block text-base font-normal leading-6 text-white'
className={cn('block text-base font-normal leading-6 text-white', labelSrOnly ? 'sr-only' : 'mb-2.5')}
>
{label}
</label>
<div className='mt-2.5'>
<div>
<input
type={type}
id={id}
ref={ref}
{...other}
className={cn(
'block w-full rounded-md border-0 bg-slate-900 px-3.5 py-2 text-slate-400 font-light shadow-sm ring-1 \
'block w-full rounded-full border-0 bg-slate-900 px-3.5 py-2 text-slate-400 font-light shadow-sm ring-1 \
ring-inset ring-slate-700/60 focus:ring-2 focus:ring-inset focus:ring-primary-500 sm:text-sm sm:leading-6',
error && 'text-red-900 ring-red-500 focus:ring-red-500'
)}
Expand Down
2 changes: 1 addition & 1 deletion apps/www/src/components/ui/atoms/inputs/textarea/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const TextArea = forwardRef<HTMLTextAreaElement, InputProps & JSX.IntrinsicEleme
{...other}
rows={4}
className={cn(
'block w-full rounded-md border-0 bg-slate-900 px-3.5 py-2 text-slate-400 font-light shadow-sm ring-1 \
'block w-full rounded-2xl border-0 bg-slate-900 px-3.5 py-2 text-slate-400 font-light shadow-sm ring-1 \
ring-inset ring-slate-700/60 focus:ring-2 focus:ring-inset focus:ring-primary-500 sm:text-sm sm:leading-6',
className,
error && 'text-red-900 ring-red-500 focus:ring-red-500'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
import {BuildingOffice2Icon, CheckCircleIcon} from '@heroicons/react/24/outline'
import {Controller, FieldValues, RegisterOptions, useForm} from 'react-hook-form'
import {Dictionary} from '@/models/dictionary.model'
import {EmailRegex, createHsformsPayload} from '@/utils/toolbox'
import {LocationObject} from '@/models/location.model'
import {contactUsFormData} from '@/models/contact-us-form-data.model'
import {createHsformsPayload} from '@/utils/toolbox'
import {sendEmail} from '@/utils/hubspot'
import {getInTouch} from '@/utils/hubspot'
import {useRouter} from 'next/navigation'
import {useState} from 'react'
import Button from '@/components/ui/atoms/button'
Expand All @@ -28,7 +28,7 @@ export default function ContactSplitWithPattern(props: ContactSplitWithPatternPr
control,
handleSubmit,
reset,
formState: {errors}
formState: {errors, isSubmitting}
} = useForm({
defaultValues: {
firstName: '',
Expand All @@ -46,7 +46,8 @@ export default function ContactSplitWithPattern(props: ContactSplitWithPatternPr
const firstNameInputProps: InputProps = {
id: firstNameInputName,
label: props.dictionary.inputFirstName,
error: errors.lastName?.message?.toString()
error: errors.lastName?.message?.toString(),
placeholder: props.dictionary.inputFirstNamePlaceholder
}
const firstNameInputRules: RegisterOptions<FieldValues, string> = {
required: 'Your first name is required'
Expand All @@ -56,7 +57,8 @@ export default function ContactSplitWithPattern(props: ContactSplitWithPatternPr
const lastNameInputProps: InputProps = {
id: lastNameInputName,
label: props.dictionary.inputLastName,
error: errors.lastName?.message?.toString()
error: errors.lastName?.message?.toString(),
placeholder: props.dictionary.inputLastNamePlaceholder
}
const lastNameInputRules: RegisterOptions<FieldValues, string> = {
required: 'Your last name is required'
Expand All @@ -66,12 +68,13 @@ export default function ContactSplitWithPattern(props: ContactSplitWithPatternPr
const emailInputProps: InputProps = {
id: emailInputName,
label: props.dictionary.inputEmail,
error: errors.email?.message?.toString()
error: errors.email?.message?.toString(),
placeholder: props.dictionary.inputEmailPlaceholder
}
const emailInputRules: RegisterOptions<FieldValues, string> = {
required: 'An email is required',
pattern: {
value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
value: EmailRegex,
message: 'Email is invalid'
}
}
Expand All @@ -80,7 +83,8 @@ export default function ContactSplitWithPattern(props: ContactSplitWithPatternPr
const messageInputProps: TextAreaProps = {
id: messageInputName,
label: props.dictionary.inputMessage,
error: errors.message?.message?.toString()
error: errors.message?.message?.toString(),
placeholder: props.dictionary.inputMessagePlaceholder
}
const messageInputRules: RegisterOptions<FieldValues, string> = {
required: 'A message is required'
Expand All @@ -94,7 +98,7 @@ export default function ContactSplitWithPattern(props: ContactSplitWithPatternPr

async function onSubmit(data: contactUsFormData) {
try {
await sendEmail(createHsformsPayload(data))
await getInTouch(createHsformsPayload(data))
Router.push('#contact-section')
setIsExploding(true)
setShowThanksMessage(true)
Expand Down Expand Up @@ -171,6 +175,7 @@ export default function ContactSplitWithPattern(props: ContactSplitWithPatternPr
name={firstNameInputName}
rules={firstNameInputRules}
control={control}
disabled={isSubmitting}
render={({field}) => (
<Input
{...field}
Expand All @@ -182,6 +187,7 @@ export default function ContactSplitWithPattern(props: ContactSplitWithPatternPr
name={lastNameInputName}
rules={lastNameInputRules}
control={control}
disabled={isSubmitting}
render={({field}) => (
<Input
{...field}
Expand All @@ -193,6 +199,7 @@ export default function ContactSplitWithPattern(props: ContactSplitWithPatternPr
name={emailInputName}
rules={emailInputRules}
control={control}
disabled={isSubmitting}
render={({field}) => (
<Input
{...field}
Expand All @@ -205,6 +212,7 @@ export default function ContactSplitWithPattern(props: ContactSplitWithPatternPr
name={messageInputName}
rules={messageInputRules}
control={control}
disabled={isSubmitting}
render={({field}) => (
<TextArea
{...field}
Expand All @@ -216,6 +224,7 @@ export default function ContactSplitWithPattern(props: ContactSplitWithPatternPr
<Controller
name={legalConsentInputName}
control={control}
disabled={isSubmitting}
render={({field: {value, ...other}}) => (
<Checkbox
checked={value}
Expand All @@ -231,7 +240,7 @@ export default function ContactSplitWithPattern(props: ContactSplitWithPatternPr
type='submit'
shape='circle'
size='large'
className='px-5 text-md bg-sky-300 text-slate-900 hover:bg-violet-300'
disabled={isSubmitting}
>
{props.dictionary.sendMessage}
</Button>
Expand Down
11 changes: 6 additions & 5 deletions apps/www/src/components/ui/organisms/footer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {Solution} from '@/models/solution.model'
import Button from '../../atoms/button'
import Linkedin from '../../svgs/logos/linkedin'
import Logo from '../../svgs/logos/56k'
import NewsletterForm from '@/components/ui/organisms/footer/newsletter-form'
import X from '../../svgs/logos/x'

export type FooterProps = {
Expand Down Expand Up @@ -79,7 +80,7 @@ export default function Footer(props: FooterProps) {
<li key={item.slug}>
<Button
asChild
tone='secondary'
tone='slate'
variant='link'
className='font-light text-slate-400'
>
Expand All @@ -104,7 +105,7 @@ export default function Footer(props: FooterProps) {
<li key={item.slug}>
<Button
asChild
tone='secondary'
tone='slate'
variant='link'
className='font-light text-slate-400'
>
Expand All @@ -125,9 +126,8 @@ export default function Footer(props: FooterProps) {
<Button
key={item.text}
asChild
tone='secondary'
tone='slate'
variant='link'
className='font-light text-slate-400'
>
<a href={item.link}>{item.text}</a>
</Button>
Expand All @@ -137,7 +137,8 @@ export default function Footer(props: FooterProps) {
</div>
</div>
</div>
<div className='pt-8 mt-16 border-t border-slate-800 sm:mt-20'>
<NewsletterForm dictionary={props.dictionary} />
<div className='pt-8 mt-10 border-t border-slate-800'>
<p className='text-xs font-light text-slate-400'>&copy; 56K.Cloud 2023 – All rights reserved.</p>
</div>
</div>
Expand Down
112 changes: 112 additions & 0 deletions apps/www/src/components/ui/organisms/footer/newsletter-form.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
'use client'

import {CheckCircleIcon} from '@heroicons/react/24/outline'
import {Controller, FieldValues, RegisterOptions, useForm} from 'react-hook-form'
import {Dictionary} from '@/models/dictionary.model'
import {EmailRegex, createHsformsPayload} from '@/utils/toolbox'
import {subscribeToNewsletter} from '@/utils/hubspot'
import {useState} from 'react'
import Button from '@/components/ui/atoms/button'
import Input, {InputProps} from '@/components/ui/atoms/inputs/input'

export default function NewsletterForm(props: {dictionary: Dictionary}) {
const [showThanksMessage, setShowThanksMessage] = useState(false)
const [serverError, setServerError] = useState<string | null>(null)

const {
control,
handleSubmit,
reset,
formState: {errors, isSubmitting}
} = useForm({
defaultValues: {
email: ''
}
})

const emailInputName = 'email'
const emailInputProps: InputProps = {
id: emailInputName,
label: props.dictionary.inputEmail,
error: errors.email?.message?.toString()
}
const emailInputRules: RegisterOptions<FieldValues, string> = {
required: 'An email is required',
pattern: {
value: EmailRegex,
message: 'Email is invalid'
}
}

async function onSubscribe(data: FieldValues) {
try {
await subscribeToNewsletter(createHsformsPayload(data))
setShowThanksMessage(true)
reset()
} catch (e) {
setServerError((e as Error).toString())
}
}

return (
<div className='mt-24 flex flex-col xl:flex-row xl:items-center xl:justify-between border-t border-slate-800 pt-8'>
<div>
<h3 className='text-sm font-semibold leading-6 text-white'>{props.dictionary.subscribeToNewsletter}</h3>
<p className='mt-2 text-sm font-light leading-6 text-slate-400'>
{props.dictionary.subscribeToNewsletterSubtitle}
</p>
</div>
{showThanksMessage ? (
<div className='text-white flex space-x-1 items-center'>
<CheckCircleIcon className='w-6 h-6' />
<h3 className='text-sm font-semibold leading-6'>{props.dictionary.subscribeToNewsletterThanks}</h3>
</div>
) : (
<form
className='mt-6 md:mt-0 md:max-w-sm'
onSubmit={handleSubmit((data) => onSubscribe(data))}
>
<div className='md:flex'>
<Controller
name={emailInputName}
rules={emailInputRules}
control={control}
disabled={isSubmitting}
render={({field}) => (
<Input
{...field}
{...emailInputProps}
label='Email'
labelSrOnly
className='md:w-full [&>div>input]:rounded-full'
placeholder={props.dictionary.inputEmailPlaceholder}
/>
)}
/>
<div className='mt-4 md:ml-4 md:mt-0 md:flex-shrink-0'>
<Button
tone='primary'
type='submit'
shape='circle'
disabled={isSubmitting}
className='w-full md:w-auto px-5'
>
{props.dictionary.subscribe}
</Button>
</div>
</div>
<div>
{serverError ? (
<div
className='p-2 sm:p-3 text-sm text-red-600 bg-red-500/10 border border-red-500/50 rounded-lg \
pl-7 sm:pl-7 mt-5 min-[1700px]:mt-6 sm:text-sm min-[1700px]:text-base'
>
{serverError}
</div>
) : null}
</div>
</form>
)}
</div>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ export default function HeroSimpleCenterWithBackground(props: HeroSimpleCenterWi
size='large'
tone={props.leftCTA.tone}
shape='circle'
className='px-5 bg-transparent border-2 border-sky-300 text-sky-300 text-md \
hover:border-violet-300 hover:bg-transparent hover:text-violet-300'
variant='ghost'
className='px-5'
>
<Link href={props.leftCTA.link}>{props.leftCTA.text}</Link>
</Button>
Expand Down
Loading