Skip to content

Commit f3a5aea

Browse files
feat: create a campaign application stepper skeleton (Epic #1842) (#1859)
* create a protected route for campaign applications * feat: create campaign-application steps components * feat: create campaign-application stepper component * feat: create stepper icon component * feat: render the stepper in the campaign application page * fix: adjust Stepper styles according to Figma design * feat: add actions buttons for the stepper * feat: add translations for the stepper action buttons * fix: adjust action buttons styles and functionality * lint: remove empty type
1 parent a4ccc0c commit f3a5aea

13 files changed

+305
-2
lines changed

Diff for: public/locales/bg/campaign-application.json

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"cta": {
3+
"next": "Запазете и продължете",
4+
"back": "Назад"
5+
}
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { useCallback, useState } from 'react'
2+
3+
import { Grid, StepLabel } from '@mui/material'
4+
5+
import { Step as StepType, Steps } from './helpers/campaignApplication.types'
6+
7+
import GenericForm from 'components/common/form/GenericForm'
8+
import CampaignApplicationStepperIcon from './CampaignApplicationStepperIcon'
9+
import CampaignApplicationOrganizer from './steps/CampaignApplicationOrganizer'
10+
import CampaignApplicationDetails from './steps/CampaignApplicationDetails'
11+
import CampaignApplication from './steps/CampaignApplication'
12+
import CampaignApplicationFormActions from './CampaignApplicationFormActions'
13+
import stepsHandler from './helpers/stepsHandler'
14+
15+
import {
16+
ActionsContainer,
17+
StyledCampaignApplicationStep,
18+
StyledCampaignApplicationStepper,
19+
StyledStepConnector,
20+
} from './helpers/campaignApplication.styled'
21+
22+
const initialValues: Record<string, string> = {}
23+
24+
const steps: StepType[] = [
25+
{
26+
title: 'campaign-application:steps.organizer.title',
27+
component: <CampaignApplicationOrganizer />,
28+
},
29+
{
30+
title: 'campaign-application:steps.campaign-application.title',
31+
component: <CampaignApplication />,
32+
},
33+
{
34+
title: 'campaign-application:steps.campaign-application-details.title',
35+
component: <CampaignApplicationDetails />,
36+
},
37+
]
38+
39+
export default function CampaignApplicationForm() {
40+
const [activeStep, setActiveStep] = useState<Steps>(Steps.ORGANIZER)
41+
42+
const handleSubmit = () => {
43+
stepsHandler({ activeStep, setActiveStep })
44+
}
45+
46+
const handleBack = useCallback(() => {
47+
setActiveStep((prevActiveStep) => prevActiveStep - 1)
48+
}, [])
49+
50+
return (
51+
<GenericForm<Record<string, string>> onSubmit={handleSubmit} initialValues={initialValues}>
52+
<StyledCampaignApplicationStepper activeStep={activeStep} connector={<StyledStepConnector />}>
53+
{steps.map((step) => (
54+
<StyledCampaignApplicationStep key={step.title}>
55+
<StepLabel StepIconComponent={CampaignApplicationStepperIcon} />
56+
</StyledCampaignApplicationStep>
57+
))}
58+
</StyledCampaignApplicationStepper>
59+
<ActionsContainer container spacing={5}>
60+
<Grid container item xs={12}>
61+
{activeStep < steps.length && steps[activeStep].component}
62+
</Grid>
63+
<Grid container item spacing={3}>
64+
<CampaignApplicationFormActions activeStep={activeStep} onBack={handleBack} />
65+
</Grid>
66+
</ActionsContainer>
67+
</GenericForm>
68+
)
69+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { MouseEvent } from 'react'
2+
3+
import { useTranslation } from 'next-i18next'
4+
5+
import { Grid } from '@mui/material'
6+
import ArrowBackIosIcon from '@mui/icons-material/ArrowBackIos'
7+
import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos'
8+
9+
import {
10+
ActionButton,
11+
ActionLinkButton,
12+
ActionSubmitButton,
13+
Root,
14+
} from './helpers/campaignApplicationFormActions.styled'
15+
16+
type CampaignApplicationFormActionsProps = {
17+
activeStep: number
18+
onBack?: (event: MouseEvent) => void
19+
}
20+
21+
export default function CampaignApplicationFormActions({
22+
onBack,
23+
activeStep,
24+
}: CampaignApplicationFormActionsProps) {
25+
const { t } = useTranslation('campaign-application')
26+
27+
return (
28+
<Root container item xs={12} spacing={3} justifyContent="space-between">
29+
<Grid item sx={{ textAlign: 'left' }}>
30+
{activeStep === 0 ? (
31+
<ActionLinkButton
32+
href=""
33+
variant="outlined"
34+
startIcon={<ArrowBackIosIcon fontSize="small" />}>
35+
{t('cta.back')}
36+
</ActionLinkButton>
37+
) : (
38+
<ActionButton onClick={onBack} startIcon={<ArrowBackIosIcon fontSize="small" />}>
39+
{t('cta.back')}
40+
</ActionButton>
41+
)}
42+
</Grid>
43+
<Grid item>
44+
<ActionSubmitButton
45+
label={t('cta.next')}
46+
endIcon={<ArrowForwardIosIcon fontSize="small" />}
47+
/>
48+
</Grid>
49+
</Root>
50+
)
51+
}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import Layout from '../layout/Layout'
2+
import CampaignApplicationForm from './CampaignApplicationForm'
23

34
export default function CampaignApplicationPage() {
4-
return <Layout />
5+
return (
6+
<Layout maxWidth="md">
7+
<CampaignApplicationForm />
8+
</Layout>
9+
)
510
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { StepIconProps } from '@mui/material/StepIcon'
2+
import { StyledCampaignApplicationStepperIcon } from './helpers/campaignApplication.styled'
3+
4+
export default function CampaignApplicationStepperIcon(props: StepIconProps) {
5+
const icons: { [index: string]: React.ReactElement } = {
6+
1: <span>1</span>,
7+
2: <span>2</span>,
8+
3: <span>3</span>,
9+
}
10+
11+
return (
12+
<StyledCampaignApplicationStepperIcon>
13+
{icons[String(props.icon)]}
14+
</StyledCampaignApplicationStepperIcon>
15+
)
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { styled } from '@mui/material/styles'
2+
import { Grid, Step, StepConnector, Stepper } from '@mui/material'
3+
4+
import theme from 'common/theme'
5+
6+
export const StyledCampaignApplicationStep = styled(Step)(() => ({
7+
padding: 0,
8+
9+
'& span': {
10+
padding: 0,
11+
},
12+
13+
'& .Mui-active': {
14+
display: 'flex',
15+
alignItems: 'center',
16+
justifyContent: 'center',
17+
backgroundColor: theme.palette.primary.main,
18+
height: '70px',
19+
width: '70px',
20+
borderRadius: theme.borders.round,
21+
22+
'& span': {
23+
color: theme.palette.common.white,
24+
fontSize: theme.typography.pxToRem(48),
25+
},
26+
},
27+
}))
28+
29+
export const StyledCampaignApplicationStepper = styled(Stepper)(() => ({
30+
backgroundColor: 'transparent',
31+
margin: '20px auto',
32+
maxWidth: '530px',
33+
}))
34+
35+
export const StyledStepConnector = styled(StepConnector)(() => ({
36+
height: 5,
37+
backgroundColor: theme.palette.primary.main,
38+
39+
'& span': {
40+
border: 'none',
41+
},
42+
}))
43+
44+
export const StyledCampaignApplicationStepperIcon = styled(Grid)(() => ({
45+
display: 'flex',
46+
alignItems: 'center',
47+
justifyContent: 'center',
48+
color: theme.palette.common.black,
49+
height: '52.05px',
50+
width: '52.05px',
51+
border: `5px solid ${theme.palette.primary.main}`,
52+
borderRadius: theme.borders.round,
53+
zIndex: 1,
54+
fontSize: theme.typography.pxToRem(36),
55+
}))
56+
57+
export const ActionsContainer = styled(Grid)(() => ({
58+
display: 'flex',
59+
justifyContent: 'center',
60+
marginTop: theme.spacing(1),
61+
}))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export type Step = {
2+
title: string
3+
component: JSX.Element
4+
}
5+
6+
export enum Steps {
7+
NONE = -1,
8+
ORGANIZER = 0,
9+
CAMPAIGN = 1,
10+
CAMPAIGN_DETAILS = 2,
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { styled } from '@mui/material/styles'
2+
import { Button, Grid } from '@mui/material'
3+
4+
import LinkButton from 'components/common/LinkButton'
5+
import SubmitButton from 'components/common/form/SubmitButton'
6+
7+
import theme from 'common/theme'
8+
9+
export const Root = styled(Grid)(() => ({
10+
marginTop: theme.spacing(15),
11+
textAlign: 'center',
12+
}))
13+
14+
export const ActionLinkButton = styled(LinkButton)(() => ({
15+
backgroundColor: theme.palette.common.white,
16+
border: `1px solid ${theme.palette.common.black}`,
17+
padding: theme.spacing(1, 5),
18+
borderRadius: theme.borders.round,
19+
color: theme.palette.common.black,
20+
fontSize: theme.typography.pxToRem(20),
21+
width: theme.spacing(50),
22+
fontWeight: 800,
23+
}))
24+
25+
export const ActionButton = styled(Button)(() => ({
26+
backgroundColor: theme.palette.common.white,
27+
border: `1px solid ${theme.palette.common.black}`,
28+
padding: theme.spacing(1, 5),
29+
borderRadius: theme.borders.round,
30+
color: theme.palette.common.black,
31+
fontSize: theme.typography.pxToRem(20),
32+
width: theme.spacing(50),
33+
fontWeight: 800,
34+
}))
35+
36+
export const ActionSubmitButton = styled(SubmitButton)(() => ({
37+
backgroundColor: '#62C4FB',
38+
border: `1px solid ${theme.palette.common.black}`,
39+
padding: theme.spacing(1, 5),
40+
borderRadius: theme.borders.round,
41+
color: theme.palette.common.black,
42+
fontSize: theme.typography.pxToRem(20),
43+
width: theme.spacing(50),
44+
fontWeight: 800,
45+
}))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { SetStateAction } from 'react'
2+
3+
import { Steps } from './campaignApplication.types'
4+
5+
interface stepsHandlerProps {
6+
activeStep: Steps
7+
setActiveStep: (value: SetStateAction<Steps>) => void
8+
}
9+
10+
export default function stepsHandler({ activeStep, setActiveStep }: stepsHandlerProps) {
11+
switch (activeStep) {
12+
case Steps.ORGANIZER:
13+
{
14+
setActiveStep((prevActiveStep) => prevActiveStep + 1)
15+
}
16+
break
17+
case Steps.CAMPAIGN:
18+
{
19+
setActiveStep((prevActiveStep) => prevActiveStep + 1)
20+
}
21+
break
22+
case Steps.CAMPAIGN_DETAILS:
23+
{
24+
setActiveStep((prevActiveStep) => prevActiveStep + 1)
25+
}
26+
break
27+
default:
28+
return 'Unknown step'
29+
}
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function CampaignApplication() {
2+
return <div>Campaign Application</div>
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function CampaignApplicationDetails() {
2+
return <div>Campaign Application Details</div>
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function CampaignApplicationOrganizer() {
2+
return <div>Campaign Application Organizer</div>
3+
}

Diff for: src/pages/campaigns/application.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { routes } from 'common/routes'
44
import CampaignApplicationPage from 'components/client/campaign-application/CampaignApplicationPage'
55

66
export const getServerSideProps: GetServerSideProps = securedPropsWithTranslation(
7-
['common', 'auth', 'validation', 'campaigns'],
7+
['common', 'auth', 'validation', 'campaigns', 'campaign-application'],
88
routes.campaigns.application,
99
)
1010

0 commit comments

Comments
 (0)