Skip to content

Commit

Permalink
form steps: contact details / funding
Browse files Browse the repository at this point in the history
  • Loading branch information
agnlez committed Mar 4, 2025
1 parent 1ed0291 commit b86b637
Show file tree
Hide file tree
Showing 19 changed files with 370 additions and 109 deletions.
10 changes: 5 additions & 5 deletions client/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion client/src/containers/my-projects/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@ export const FORM_STEPS = [
{
label: 'Project Details',
value: 'project-details',
disabled: false,
},
{
label: 'Contact Details',
value: 'contact-details',
disabled: false,
},
{
label: 'Funding',
value: 'funding',
disabled: false,
},
];
] as const;
15 changes: 10 additions & 5 deletions client/src/containers/my-projects/form/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import { FormRenderProps } from 'react-final-form';

import { useSearchParams } from 'next/navigation';

import FundingStep from '../new/funding';

import ContactDetailsStep from './steps/contact-details';
import FundingStep from './steps/funding';
import ProjectDetailsStep from './steps/project-details';

type STEP = 'project-details' | 'contact-details' | 'funding';
Expand All @@ -13,10 +14,14 @@ export default function Form({ handleSubmit }: { handleSubmit: FormRenderProps['
const currentStep = (searchParams.get('step') as STEP) || 'project-details';

return (
<form onSubmit={handleSubmit}>
{currentStep === 'project-details' && <ProjectDetailsStep />}
{currentStep === 'contact-details' && <ContactDetailsStep />}
<>
{['project-details', 'contact-details'].includes(currentStep) && (
<form onSubmit={handleSubmit}>
{currentStep === 'project-details' && <ProjectDetailsStep />}
{currentStep === 'contact-details' && <ContactDetailsStep />}
</form>
)}
{currentStep === 'funding' && <FundingStep />}
</form>
</>
);
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { Field as FieldRFF } from 'react-final-form';

import { usePathname } from 'next/navigation';

import { HiOutlineArrowLeft } from 'react-icons/hi';

import LinkButton from 'components/button';
import { Input } from 'components/forms';

import VisibilityLabel from '../../label';
import FormLegend from '../../legend';
import { ProjectSchema } from '../../validations';

import PrimaryOfficeCountrySelector from './primary-office-country';
import PrimaryOfficeStateSelector from './primary-office-state';

export default function ContactDetailsStep() {
const pathname = usePathname();

return (
<div className="flex flex-col gap-10 pb-10">
<div className="grid grid-cols-12 flex-col gap-2">
<h3 className="font-display text-2.5xl col-span-12">Contact Details</h3>
{/*@todo: update text*/}
<p className="font-semibold col-span-9">
Lorem ipsum dolor sit amet consectetur et fringilla pellentesque in ut congue at ultrices
nulla nibh dolor sit amet pellentesque consectetur.
</p>
</div>

<div className="space-y-4">
<FormLegend />
<p className="font-semibold text-grey-20 flex items-center gap-1">
<span className="align-super text-red-0">*</span>
All fields marked with a red asterisk are mandatory to fill
</p>
</div>

<div className="grid grid-cols-12 gap-4 items-end">
<div className="col-span-6">
<div className="space-y-2">
<VisibilityLabel
labelProps={{
htmlFor: 'country_id',
}}
required
>
Primary Office Country
</VisibilityLabel>
<PrimaryOfficeCountrySelector />
</div>
</div>
<div className="col-span-6">
<div className="space-y-2">
<VisibilityLabel
labelProps={{
htmlFor: 'stated_id',
}}
>
Primary Office State
</VisibilityLabel>
<PrimaryOfficeStateSelector />
</div>
</div>
</div>

<div className="space-y-2">
<VisibilityLabel
labelProps={{
htmlFor: 'city',
}}
required
>
Primary Office City
</VisibilityLabel>
<FieldRFF<ProjectSchema['name']> name="city" type="text">
{({ input }) => <Input {...input} id={input.name} className="h-[46px]" />}
</FieldRFF>
</div>

<footer className="flex justify-end">
<LinkButton
theme="outline"
href={`${pathname}?step=project-details`}
className="flex items-center gap-2"
>
<HiOutlineArrowLeft className="w-[20px] h-[20px]" />
<span>Project Details</span>
</LinkButton>
</footer>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
'use client';

import { ComponentProps } from 'react';

import { Field as FieldRFF } from 'react-final-form';

import { useSubGeographics } from 'hooks/geographics';

import { ProjectSchema } from 'containers/my-projects/form/validations';

import { Select } from 'components/forms';

export default function PrimaryOfficeCountrySelector() {
const {
data: countries,
isFetching: countriesFetching,
isFetched: countriesFetched,
} = useSubGeographics(
{
filters: { geographic: 'countries' },
},
{
select: ({ data }) => data,
}
);

const countriesOptions: ComponentProps<typeof Select>['options'] =
countries?.map(({ id, name }) => ({ value: id, label: name })) || [];

return (
<FieldRFF<ProjectSchema['name']> name="country_id">
{({ input }) => (
<Select
id="country_id"
placeholder="Select an option"
theme="light"
size="base"
options={countriesOptions}
value={input.value}
loading={countriesFetching && !countriesFetched}
onSelect={input.onChange}
/>
)}
</FieldRFF>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
'use client';

import { ComponentProps, useEffect } from 'react';

import { Field as FieldRFF, useForm, useFormState } from 'react-final-form';

import { useSubGeographics } from 'hooks/geographics';

import { ProjectSchema } from 'containers/my-projects/form/validations';

import { Select } from 'components/forms';

export default function PrimaryOfficeStateSelector() {
const {
values: { country_id: countryId },
} = useFormState<ProjectSchema>();

const { change, getFieldState } = useForm();

const stateFieldState = getFieldState('state_id');
const dirty = stateFieldState?.dirty;

const { data: countries } = useSubGeographics(
{
filters: { geographic: 'countries' },
},
{
select: ({ data }) => data,
}
);

const isUSA = countries.find(({ id }) => id === countryId)?.code === 'USA';

const {
data: states,
isFetching: statesFetching,
isFetched: statesFetched,
} = useSubGeographics(
{
filters: { geographic: 'states' },
},
{
select: ({ data }) => data,
enabled: isUSA,
}
);

const statesOptions: ComponentProps<typeof Select>['options'] =
states?.map(({ id, name }) => ({ value: id, label: name })) || [];

useEffect(() => {
if (dirty && !isUSA) {
change('state_id', undefined);
}
}, [dirty, isUSA, change]);

return (
<FieldRFF<ProjectSchema['name']> name="state_id">
{({ input }) => (
<Select
id="state_id"
placeholder="Select an option"
theme="light"
size="base"
options={statesOptions}
value={input.value}
loading={statesFetching && !statesFetched}
onSelect={input.onChange}
disabled={!isUSA}
/>
)}
</FieldRFF>
);
}
3 changes: 0 additions & 3 deletions client/src/containers/my-projects/form/steps/funding.tsx

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export default function DemographicsSelector() {
demographics?.map(({ id, name }) => ({ value: id, label: name })) || [];

return (
<FieldRFF<ProjectSchema['demographics']> name="demographics">
<FieldRFF<ProjectSchema['leadership_demographics']> name="leadership_demographics">
{({ input }) => (
<MultiSelect
id="leadership_demographics"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,12 @@ import { Field as FieldRFF, useField, useFormState } from 'react-final-form';
import Image from 'next/image';
import { usePathname } from 'next/navigation';

import { HiOutlineArrowRight } from 'react-icons/hi';

import LinkButton from 'components/button';
import DragNDrop from 'components/drag-n-drop';
import { Input, Radio } from 'components/forms';
import Textarea from 'components/forms/textarea';
import Icon from 'components/icon';

import ARROW_RIGHT_SVG from 'svgs/ui/arrow-right.svg?sprite';

import VisibilityLabel from '../../label';
import FormLegend from '../../legend';
Expand All @@ -25,7 +24,7 @@ export default function ProjectDetailsStep() {
const [collectsInformation, setCollectsInformation] = useState('yes');
const pathname = usePathname();
const {
values: { demographics: demographicsFormValues },
values: { leadership_demographics: demographicsFormValues },
} = useFormState<ProjectSchema>();
const logoField = useField('logo');
const [imageSrc, setImageSrc] = useState<string | null>(null);
Expand Down Expand Up @@ -197,15 +196,18 @@ export default function ProjectDetailsStep() {
<div className="space-y-2">
<VisibilityLabel
labelProps={{
htmlFor: 'demographics_other',
htmlFor: 'leadership_demographics_other',
className: 'normal-case',
'aria-required': true,
}}
required
>
Other demographics
</VisibilityLabel>
<FieldRFF<ProjectSchema['demographics_other']> name="demographics_other" type="text">
<FieldRFF<ProjectSchema['leadership_demographics_other']>
name="leadership_demographics_other"
type="text"
>
{({ input }) => <Input required {...input} />}
</FieldRFF>
</div>
Expand All @@ -219,7 +221,7 @@ export default function ProjectDetailsStep() {
className="flex items-center gap-1"
>
<span>Contact Details</span>
<Icon icon={ARROW_RIGHT_SVG} className="w-[14px] h-[14px]" />
<HiOutlineArrowRight className="w-[20px] h-[20px]" />
</LinkButton>
</footer>
</div>
Expand Down
8 changes: 6 additions & 2 deletions client/src/containers/my-projects/form/validations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@ const ZodProjectSchema = z.object({
name: z.string().nonempty().min(3),
website: z.string().url().optional(),
description: z.string().nonempty(),
recipient_legal_status: z.string(),
logo: z.instanceof(File).optional(),
demographics: z.array(z.string()).nonempty(),
demographics_other: z.string().optional(),
leadership_demographics: z.array(z.string()).nonempty(),
leadership_demographics_other: z.string().optional(),
country_id: z.string(),
state_id: z.string().optional(),
city: z.string(),
});

export type ProjectSchema = z.infer<typeof ZodProjectSchema>;
Expand Down
Loading

0 comments on commit b86b637

Please sign in to comment.