Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
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
1 change: 1 addition & 0 deletions src/components/Page/Page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const Page = ({ children, title, 'data-cy': dataCy }: PageProps) => {
sx={{
color: '#60718c',
maxHeight: '55vh',
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this height of 85vh for mobile to reflect figma design

width: '100%',
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Usually, 100% width will be the default. Is that not the case here?

overflowY: 'auto',
gap: '2.5rem'
}}
Expand Down
213 changes: 181 additions & 32 deletions src/components/Pages/Contact.tsx
Comment thread
AnilKumar3494 marked this conversation as resolved.
Outdated
Original file line number Diff line number Diff line change
@@ -1,42 +1,191 @@
import { useState } from 'react';
import { Box, CircularProgress } from '@mui/material';
import {
Checkbox,
Stack,
Button,
FormControlLabel,
Alert,
Collapse,
Typography
} from '@mui/material';
import { FormProvider, useForm, Controller } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { addFeedback } from 'services/db';
import Page from 'components/Page/Page';
import feedbackFormSchema, {
type FeedbackFormValues
} from 'schemas/feedbackFormSchema';
import FormTextField from 'components/forms/FormTextField/FormTextField';
import ClearFormIcon from 'icons/ClearFormIcon';

type FormValues = FeedbackFormValues;

const TITLE = 'Contact Us';
const PRIMARY_COLOR = '#10b6ff';
const textFieldFocus = {
Comment thread
AnilKumar3494 marked this conversation as resolved.
Outdated
'& .MuiOutlinedInput-root': {
'&.Mui-focused fieldset': {
borderColor: PRIMARY_COLOR
}
},
'& .MuiInputLabel-root.Mui-focused': {
color: PRIMARY_COLOR
}
};
const SCHEMA = feedbackFormSchema;

const Contact = () => {
const [loading, setLoading] = useState(true);
const [status, setStatus] = useState<'idle' | 'success' | 'error'>('idle');
Comment thread
AnilKumar3494 marked this conversation as resolved.
Outdated

const methods = useForm<FormValues>({
defaultValues: {
name: '',
email: '',
feedback: '',
interest: false
},
resolver: zodResolver(SCHEMA)
});

const {
handleSubmit,
control,
reset,
formState: { isSubmitting }
} = methods;

const onSubmit = async (data: FormValues) => {
setStatus('idle');
try {
await addFeedback(data);
setStatus('success');
reset();
} catch (error) {
console.error(error);
setStatus('error');
}
};

const handleLoading = () => {
setLoading(false);
const handleClear = () => {
reset();
setStatus('idle');
};

return (
<Box sx={{ height: '460px', width: '100%', position: 'relative' }}>
{loading && (
<Box
sx={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}}
>
<CircularProgress />
</Box>
)}

<iframe
title="Contact Us"
className="airtable-embed"
src="https://airtable.com/embed/appyNdhZZn3gpovFh/pagDtKnlb6n3mCpgd/form"
width="100%"
height="460px"
style={{ background: 'transparent', border: 'none' }}
onLoad={handleLoading}
/>
</Box>
<Page title={TITLE} data-cy="contact-us">
<FormProvider {...methods}>
<form onSubmit={handleSubmit(onSubmit)} style={{ width: '75%' }}>
<Stack gap={2}>
<Collapse in={status !== 'idle'}>
{status === 'success' && (
<Alert severity="success" onClose={() => setStatus('idle')}>
Thank you! Your feedback has been received!
</Alert>
)}
{status === 'error' && (
<Alert severity="error" onClose={() => setStatus('idle')}>
Something went wrong please try again after sometime.
</Alert>
)}
</Collapse>

<FormTextField<FormValues>
name="name"
label="Name"
placeholder="Enter Your Name"
required
fullWidth
sx={textFieldFocus}
/>

<FormTextField<FormValues>
name="email"
label="Email"
placeholder="[email protected]"
required
fullWidth
sx={textFieldFocus}
/>

<FormTextField<FormValues>
name="feedback"
label="Feedback"
placeholder="Share your feedback and thoughts"
multiline
minRows={3}
fullWidth
sx={textFieldFocus}
helperText="Please do not include any sensitive personal information."
slotProps={{
formHelperText: {
Comment thread
AnilKumar3494 marked this conversation as resolved.
Outdated
sx: {
color: 'gray',
fontSize: '0.75rem',
fontStyle: 'italic',
marginLeft: '1px'
}
}
}}
/>

<Controller
name="interest"
control={control}
render={({ field }) => (
<FormControlLabel
control={
<Checkbox
{...field}
checked={field.value}
sx={{
'&.Mui-checked': { color: PRIMARY_COLOR }
}}
/>
}
label={
<Typography
sx={{ fontSize: 14, fontWeight: 500, lineHeight: '24px' }}
>
I'm interested in helping PHLASK with future research
</Typography>
}
/>
)}
/>

<Stack direction="row" gap={5} mt={2}>
<Button
Comment thread
AnilKumar3494 marked this conversation as resolved.
Outdated
variant="text"
onClick={handleClear}
sx={{ color: PRIMARY_COLOR }}
startIcon={<ClearFormIcon />}
>
clear form
</Button>
<Button
variant="contained"
type="submit"
disabled={isSubmitting || status === 'success'}
sx={{
fontWeight: 'bold',
backgroundColor: PRIMARY_COLOR,
'&:hover': {
backgroundColor: PRIMARY_COLOR,
boxShadow: `0 0 10px ${PRIMARY_COLOR}80`
}
}}
>
{isSubmitting
? 'Submitting...'
: status === 'success'
? 'Submitted'
: 'Submit'}
</Button>
</Stack>
</Stack>
</form>
</FormProvider>
</Page>
);
};

Expand Down
20 changes: 17 additions & 3 deletions src/components/forms/FormTextField/FormTextField.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { TextField } from '@mui/material';
import { TextField, type TextFieldProps } from '@mui/material';
import type { ReactNode } from 'react';
import {
useFormContext,
Expand All @@ -10,23 +10,29 @@ import {
type FormTextFieldProps<Values extends FieldValues> = {
name: Path<Values>;
label: ReactNode;
placeholder?: string;
disabled?: boolean;
helperText?: ReactNode;
fullWidth?: boolean;
required?: boolean;
multiline?: boolean;
minRows?: number;
slotProps?: TextFieldProps['slotProps'];
sx?: TextFieldProps['sx'];
Comment thread
AnilKumar3494 marked this conversation as resolved.
};

const FormTextField = <Values extends FieldValues>({
name,
label,
placeholder,
helperText,
disabled = false,
required = false,
fullWidth = false,
multiline = false,
minRows = undefined
minRows = undefined,
slotProps,
sx
}: FormTextFieldProps<Values>) => {
const { register, getFieldState } = useFormContext<Values>();
const formState = useFormState<Values>({
Expand All @@ -39,12 +45,20 @@ const FormTextField = <Values extends FieldValues>({
<TextField
{...register(name, { required, disabled })}
label={label}
placeholder={placeholder}
helperText={error?.message || helperText || ' '}
fullWidth={fullWidth}
slotProps={{ inputLabel: { required } }}
slotProps={{
...slotProps,
inputLabel: {
required,
...slotProps?.inputLabel
}
}}
error={invalid}
multiline={multiline}
minRows={minRows}
sx={sx}
/>
);
};
Expand Down
20 changes: 20 additions & 0 deletions src/icons/ClearFormIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import type { SVGProps } from 'react';

const SvgClearFormIcon = (props: SVGProps<SVGSVGElement>) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width="0.7em"
height="0.7em"
viewBox="0 0 16 16"
fill="#10b6ff"
{...props}
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M13.9907 0c.8909 0 1.337.1077 1.337 1.1077L14.6978 1.7071l-.8422.8421c.5654.6073 1.0348 1.301 1.3892 2.0577.558 1.1914.8135 2.502.744 3.8158-.0695 1.3138-.462 2.5902-1.1426 3.7161-.6806 1.1258-1.6284 2.0665-2.7594 2.7385-1.131.6721-2.4103 1.0549-3.7245 1.1145-1.3143.0596-2.623-.2059-3.8101-.7729-1.1872-.5669-2.2162-1.418-2.9959-2.4776C.7766 11.6816.2703 10.446.0822 9.1439c-.079-.5466.3001-1.0537.8467-1.1327s1.0538.3001 1.1327.8467c.1411.9766.5209 1.9033 1.1057 2.6981.5848.7947 1.3565 1.433 2.2469 1.8582.8904.4252 1.8719.6243 2.8576.5797.9857-.0447 1.9452-.3318 2.7934-.8359.8482-.5041 1.5591-1.2095 2.0695-2.0539.5105-.8444.8049-1.8017.857-2.787.0521-.9853-.1395-1.9683-.558-2.8619-.2556-.5457-.591-1.0478-.9934-1.4906l-.7331.7332c-.63 1.263-1.7071.8168-1.7071-.0741V0h3.9907zM1.5 4c.5523 0 1 .4477 1 1s-.4477 1-1 1-1-.4477-1-1 .4477-1 1-1zm2.25-2.75c.5523 0 1 .4477 1 1s-.4477 1-1 1-1-.4477-1-1 .4477-1 1-1zm3.25-1.25c.5523 0 1 .4477 1 1s-.4477 1-1 1-1-.4477-1-1 .4477-1 1-1z"
/>
</svg>
);

export default SvgClearFormIcon;
34 changes: 21 additions & 13 deletions src/icons/JoinDataIcon.tsx
Comment thread
AnilKumar3494 marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -1,27 +1,35 @@
import type { SVGProps } from 'react';

const SvgJoinDataIcon = (props: SVGProps<SVGSVGElement>) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width="1em"
height="1em"
viewBox="0 0 256 256"
fill="none"
viewBox="0 0 30 31"
{...props}
>
<path
stroke="#2D3848"
<g
stroke="#000"
strokeWidth={12}
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={1.622}
d="m22.047 4.288 4.174 4.175L7.517 27.167a.22.22 0 0 1-.321 0l-3.853-3.853a.22.22 0 0 1 0-.321zM27.424 2.158l.928.929a2.31 2.31 0 0 1 0 3.257l-2.121 2.121-4.174-4.174 2.121-2.122a2.31 2.31 0 0 1 3.257 0zM9.635 14.102l-7.93-7.93 4.93-4.93 7.395 7.395-2.027 2.01M18.864 19.26l2.886-2.887 7.38 7.363-4.93 4.93-7.931-7.93M7.121 5.766l2.011-2.011M8.679 9.058l2.887-2.886M22.122 20.768l2.011-2.027M23.858 24.24l2.887-2.888"
/>
<path
stroke="#2D3848"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={1.622}
d="m7.43 27.32-6.261 2.027 2.011-6.212"
/>
>
<path
fill="#000"
fillRule="evenodd"
d="M239.985 55.994c0 22.089-50.138 39.994-111.985 39.994S16.015 78.083 16.015 55.994 66.153 16 128 16s111.985 17.905 111.985 39.994z"
/>

<path d="M239.985 103.987c-13.998 21.648-55.992 34.637-111.985 34.637s-97.987-12.989-111.985-34.637" />

<path d="M239.985 151.981c-13.998 21.647-55.992 34.636-111.985 34.636s-97.987-12.989-111.985-34.636" />

<path d="M239.985 199.974c-13.998 21.648-55.992 34.637-111.985 34.637s-97.987-12.989-111.985-34.637" />

<path d="M16.015 55.994v143.98M239.985 55.994v143.98" />
</g>
</svg>
);

export default SvgJoinDataIcon;
11 changes: 11 additions & 0 deletions src/schemas/feedbackFormSchema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import * as z from 'zod';

const feedbackFormSchema = z.object({
name: z.string().min(1, 'Name is required'),
email: z.string().min(1, 'Email is required').email('Invalid email'),
feedback: z.string().optional(),
interest: z.boolean()
Comment thread
AnilKumar3494 marked this conversation as resolved.
Outdated
});

export type FeedbackFormValues = z.infer<typeof feedbackFormSchema>;
export default feedbackFormSchema;
Loading