mirror of
https://github.com/tomru/pfadi-bussle.git
synced 2026-03-03 06:27:11 +01:00
add a wizard
This commit is contained in:
@@ -1,18 +1,14 @@
|
|||||||
import { useContext } from 'react'
|
import { useContext } from 'react'
|
||||||
import { AppContext, ACTIONS } from '../context/appStore'
|
import { WizardContext, ACTIONS } from '../context/wizardStore'
|
||||||
|
|
||||||
import Form from 'react-bootstrap/Form'
|
import Form from 'react-bootstrap/Form'
|
||||||
import Col from 'react-bootstrap/Col'
|
import Col from 'react-bootstrap/Col'
|
||||||
|
|
||||||
export default function Contact() {
|
export default function Contact() {
|
||||||
const { state, dispatch } = useContext(AppContext)
|
const { state, dispatch } = useContext(WizardContext)
|
||||||
|
|
||||||
const { multipleDays, startDate, endDate } = state
|
const { multipleDays, startDate, endDate } = state
|
||||||
|
|
||||||
if (!startDate || (multipleDays && !endDate)) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Form.Group>
|
<Form.Group>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useContext } from 'react'
|
import { useContext } from 'react'
|
||||||
import { AppContext, ACTIONS } from '../context/appStore'
|
import { WizardContext, ACTIONS } from '../context/wizardStore'
|
||||||
|
|
||||||
import Form from 'react-bootstrap/Form'
|
import Form from 'react-bootstrap/Form'
|
||||||
|
|
||||||
@@ -8,7 +8,7 @@ import 'react-dates/initialize'
|
|||||||
import { DateRangePicker, SingleDatePicker } from 'react-dates'
|
import { DateRangePicker, SingleDatePicker } from 'react-dates'
|
||||||
|
|
||||||
export default function DateSelect() {
|
export default function DateSelect() {
|
||||||
const { state, dispatch } = useContext(AppContext)
|
const { state, dispatch } = useContext(WizardContext)
|
||||||
|
|
||||||
const {
|
const {
|
||||||
multipleDays,
|
multipleDays,
|
||||||
@@ -24,65 +24,88 @@ export default function DateSelect() {
|
|||||||
return bookedOn.some((rawDay) => momentDay.isSame(rawDay))
|
return bookedOn.some((rawDay) => momentDay.isSame(rawDay))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (multipleDays === null || multipleDays === undefined) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Form.Group>
|
<Form.Group controlId="dateSelect">
|
||||||
<Form.Label component="legend" style={{ display: 'block' }}>
|
<Form.Label>Willst du einen odere mehrere Tage buchen?</Form.Label>
|
||||||
Datum
|
<Form.Check
|
||||||
</Form.Label>
|
type="radio"
|
||||||
{state.multipleDays === false && (
|
id={'multipleDays-single'}
|
||||||
<SingleDatePicker
|
label="Einen Tag"
|
||||||
small={true}
|
name="multipleDays"
|
||||||
date={startDate && moment(startDate)}
|
value="single"
|
||||||
onDateChange={(date) =>
|
checked={state.multipleDays === false}
|
||||||
dispatch({
|
onChange={() =>
|
||||||
type: ACTIONS.SET_DATE,
|
dispatch({ type: ACTIONS.SET_MULTIPLE_DAYS, payload: false })
|
||||||
payload: { startDate: date.toISOString() },
|
}
|
||||||
})
|
/>
|
||||||
}
|
<Form.Check
|
||||||
focused={typeof focusedInput === 'boolean' && focusedInput}
|
type="radio"
|
||||||
onFocusChange={({ focused }) =>
|
id={'multipleDays-multiple'}
|
||||||
dispatch({
|
label="Mehrere Tage"
|
||||||
type: ACTIONS.SET_FOCUSED_INPUT,
|
name="multipleDays"
|
||||||
payload: focused,
|
value="multiple"
|
||||||
})
|
checked={state.multipleDays === true}
|
||||||
}
|
onChange={() =>
|
||||||
isDayBlocked={isDayBlocked}
|
dispatch({ type: ACTIONS.SET_MULTIPLE_DAYS, payload: true })
|
||||||
id="your_unique_id"
|
}
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
{state.multipleDays === true && (
|
|
||||||
<DateRangePicker
|
|
||||||
small={true}
|
|
||||||
startDate={startDate && moment(startDate)}
|
|
||||||
startDateId="bussle_start_date_id"
|
|
||||||
endDate={endDate && moment(endDate)}
|
|
||||||
endDateId="bussle_end_date_id"
|
|
||||||
onDatesChange={({ startDate, endDate }) => {
|
|
||||||
dispatch({
|
|
||||||
type: ACTIONS.SET_DATE,
|
|
||||||
payload: {
|
|
||||||
startDate: startDate && startDate.toISOString(),
|
|
||||||
endDate: endDate && endDate.toISOString(),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
focusedInput={focusedInput}
|
|
||||||
onFocusChange={(focusedInput) =>
|
|
||||||
dispatch({
|
|
||||||
type: ACTIONS.SET_FOCUSED_INPUT,
|
|
||||||
payload: focusedInput,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
isDayBlocked={isDayBlocked}
|
|
||||||
minDate={moment()}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Form.Group>
|
</Form.Group>
|
||||||
|
{multipleDays !== null && (
|
||||||
|
<Form.Group>
|
||||||
|
<Form.Label component="legend" style={{ display: 'block' }}>
|
||||||
|
Datum
|
||||||
|
</Form.Label>
|
||||||
|
{state.multipleDays === false && (
|
||||||
|
<SingleDatePicker
|
||||||
|
small={true}
|
||||||
|
date={startDate && moment(startDate)}
|
||||||
|
onDateChange={(date) =>
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.SET_DATE,
|
||||||
|
payload: { startDate: date.toISOString() },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
focused={typeof focusedInput === 'boolean' && focusedInput}
|
||||||
|
onFocusChange={({ focused }) =>
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.SET_FOCUSED_INPUT,
|
||||||
|
payload: focused,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
isDayBlocked={isDayBlocked}
|
||||||
|
id="your_unique_id"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{state.multipleDays === true && (
|
||||||
|
<DateRangePicker
|
||||||
|
small={true}
|
||||||
|
startDate={startDate && moment(startDate)}
|
||||||
|
startDateId="bussle_start_date_id"
|
||||||
|
endDate={endDate && moment(endDate)}
|
||||||
|
endDateId="bussle_end_date_id"
|
||||||
|
onDatesChange={({ startDate, endDate }) => {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.SET_DATE,
|
||||||
|
payload: {
|
||||||
|
startDate: startDate && startDate.toISOString(),
|
||||||
|
endDate: endDate && endDate.toISOString(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
focusedInput={focusedInput}
|
||||||
|
onFocusChange={(focusedInput) =>
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.SET_FOCUSED_INPUT,
|
||||||
|
payload: focusedInput,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
isDayBlocked={isDayBlocked}
|
||||||
|
minDate={moment()}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Form.Group>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,50 +0,0 @@
|
|||||||
import { useContext } from 'react'
|
|
||||||
import { AppContext, ACTIONS } from '../context/appStore'
|
|
||||||
|
|
||||||
import Form from 'react-bootstrap/Form'
|
|
||||||
|
|
||||||
export default function RangeSelect() {
|
|
||||||
const { state, dispatch } = useContext(AppContext)
|
|
||||||
|
|
||||||
function getValue() {
|
|
||||||
const { multipleDays } = state
|
|
||||||
|
|
||||||
if (multipleDays === null || multipleDays === undefined) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
if (multipleDays === true) {
|
|
||||||
return 'multiple'
|
|
||||||
}
|
|
||||||
|
|
||||||
return 'single'
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Form.Group controlId="dateSelect">
|
|
||||||
<Form.Label>Willst du einen odere mehrere Tage buchen?</Form.Label>
|
|
||||||
<Form.Check
|
|
||||||
type="radio"
|
|
||||||
id={'multipleDays-single'}
|
|
||||||
label="Einen Tag"
|
|
||||||
name="multipleDays"
|
|
||||||
value="single"
|
|
||||||
checked={state.multipleDays === false}
|
|
||||||
onChange={() =>
|
|
||||||
dispatch({ type: ACTIONS.SET_MULTIPLE_DAYS, payload: false })
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Form.Check
|
|
||||||
type="radio"
|
|
||||||
id={'multipleDays-multiple'}
|
|
||||||
label="Mehrere Tage"
|
|
||||||
name="multipleDays"
|
|
||||||
value="multiple"
|
|
||||||
checked={state.multipleDays === true}
|
|
||||||
onChange={() =>
|
|
||||||
dispatch({ type: ACTIONS.SET_MULTIPLE_DAYS, payload: true })
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Form.Group>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
55
components/wizard.js
Normal file
55
components/wizard.js
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
import { useContext } from 'react'
|
||||||
|
|
||||||
|
import Button from 'react-bootstrap/Button'
|
||||||
|
import Form from 'react-bootstrap/Form'
|
||||||
|
|
||||||
|
import WizardStore, { WizardContext, ACTIONS } from '../context/wizardStore'
|
||||||
|
|
||||||
|
import DateSelect from './dateSelect'
|
||||||
|
import Contact from './contact'
|
||||||
|
import Driver from './driver'
|
||||||
|
|
||||||
|
const STEPS = [
|
||||||
|
{ id: 'DATE_SELECT', component: DateSelect },
|
||||||
|
{ id: 'CONTACT', component: Contact },
|
||||||
|
]
|
||||||
|
|
||||||
|
function WizardInternal() {
|
||||||
|
const { state, dispatch } = useContext(WizardContext)
|
||||||
|
const { currentStep } = state
|
||||||
|
|
||||||
|
const isFirstStep = currentStep === 0
|
||||||
|
const isLastStep = currentStep === STEPS.length - 1
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form>
|
||||||
|
{STEPS.map(({ id, component: Component }, index) => (
|
||||||
|
<>{currentStep === index && <Component />}</>
|
||||||
|
))}
|
||||||
|
{!isFirstStep && (
|
||||||
|
<Button
|
||||||
|
variant="secondary"
|
||||||
|
onClick={() => dispatch({ type: ACTIONS.PREV_STEP })}
|
||||||
|
>
|
||||||
|
Zurück
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{!isLastStep && (
|
||||||
|
<Button onClick={() => dispatch({ type: ACTIONS.NEXT_STEP })}>
|
||||||
|
Weiter
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{isLastStep && (
|
||||||
|
<Button onClick={() => console.log('SEND OFF', state)}>Absenden</Button>
|
||||||
|
)}
|
||||||
|
</Form>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Wizard() {
|
||||||
|
return (
|
||||||
|
<WizardStore>
|
||||||
|
<WizardInternal />
|
||||||
|
</WizardStore>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
import React, { useReducer } from 'react'
|
import React, { useReducer } from 'react'
|
||||||
|
|
||||||
export const AppContext = React.createContext()
|
export const WizardContext = React.createContext()
|
||||||
|
|
||||||
export const ACTIONS = {
|
export const ACTIONS = {
|
||||||
|
NEXT_STEP: 'nextStep',
|
||||||
|
PREV_STEP: 'prevStep',
|
||||||
SET_MULTIPLE_DAYS: 'setMultipleDays',
|
SET_MULTIPLE_DAYS: 'setMultipleDays',
|
||||||
SET_DATE: 'setDate',
|
SET_DATE: 'setDate',
|
||||||
SET_FOCUSED_INPUT: 'setFocusedInput',
|
SET_FOCUSED_INPUT: 'setFocusedInput',
|
||||||
@@ -12,6 +14,16 @@ export const ACTIONS = {
|
|||||||
|
|
||||||
function reducer(state, action) {
|
function reducer(state, action) {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
|
case ACTIONS.NEXT_STEP:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
currentStep: state.currentStep + 1, // wizards steps unkown here
|
||||||
|
}
|
||||||
|
case ACTIONS.PREV_STEP:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
currentStep: Math.max(0, state.currentStep - 1),
|
||||||
|
}
|
||||||
case ACTIONS.SET_MULTIPLE_DAYS:
|
case ACTIONS.SET_MULTIPLE_DAYS:
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
@@ -45,6 +57,7 @@ function debugReducer(state, action) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const initialState = {
|
const initialState = {
|
||||||
|
currentStep: 0,
|
||||||
multipleDays: null,
|
multipleDays: null,
|
||||||
bookedOn: [
|
bookedOn: [
|
||||||
'2020-07-10',
|
'2020-07-10',
|
||||||
@@ -55,12 +68,12 @@ const initialState = {
|
|||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function AppStore({ children }) {
|
export default function WizardStore({ children }) {
|
||||||
const [state, dispatch] = useReducer(debugReducer, initialState)
|
const [state, dispatch] = useReducer(debugReducer, initialState)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppContext.Provider value={{ state, dispatch }}>
|
<WizardContext.Provider value={{ state, dispatch }}>
|
||||||
{children}
|
{children}
|
||||||
</AppContext.Provider>
|
</WizardContext.Provider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -2,15 +2,7 @@ import { useContext } from 'react'
|
|||||||
|
|
||||||
import Head from 'next/head'
|
import Head from 'next/head'
|
||||||
|
|
||||||
import AppStore from '../context/appStore'
|
import Wizard from '../components/wizard'
|
||||||
|
|
||||||
import RangeSelect from '../components/rangeSelect'
|
|
||||||
import DateSelect from '../components/dateSelect'
|
|
||||||
import Contact from '../components/contact'
|
|
||||||
import Driver from '../components/driver'
|
|
||||||
|
|
||||||
import Form from 'react-bootstrap/Form'
|
|
||||||
import Button from 'react-bootstrap/Button'
|
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
return (
|
return (
|
||||||
@@ -25,15 +17,7 @@ export default function Home() {
|
|||||||
|
|
||||||
<p>Du willst das Pfadi Bussle buchen? Hier bist du richtig!</p>
|
<p>Du willst das Pfadi Bussle buchen? Hier bist du richtig!</p>
|
||||||
|
|
||||||
<AppStore>
|
<Wizard />
|
||||||
<Form>
|
|
||||||
<RangeSelect />
|
|
||||||
<DateSelect />
|
|
||||||
<Contact />
|
|
||||||
|
|
||||||
<Button variant="primary">Reservieren</Button>
|
|
||||||
</Form>
|
|
||||||
</AppStore>
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
|
|||||||
Reference in New Issue
Block a user