import React, { useEffect, useReducer } from 'react' import { clearBookingData, loadBookingData, storeBookingData, } from '../../../helpers/storage' import { ValidationError } from './validationError' interface WizardFormData { startDate: string endDate: string purpose: string org: string destination: string name: string email: string street: string zip: string city: string } interface Booking { uuid: string } interface WizardStoreState { postData?: boolean postDataError?: string postDataSuccess?: boolean formData: WizardFormData formDataChanged: string[] booking?: Booking dataStored: boolean dataStoredLoaded: boolean } interface WizardStore { state: WizardStoreState dispatch: React.Dispatch onChange: (data: object) => void onChangeEvent: (event: React.ChangeEvent>) => void onSubmit: () => void storeData: (value: boolean) => void forgetData: () => void } interface WizardAction { type: string payload?: any } export const WizardContext: React.Context = React.createContext< WizardStore >(null) export const ACTIONS = { SET_FORM_DATA: 'setFormData', POST_DATA: 'postData', POST_DATA_ERROR: 'postDataError', POST_DATA_SUCCESS: 'postDataSuccess', DATA_STORED: 'dataStored', DATA_STORED_LOADED: 'dataStoredLoaded', DATA_STORED_FORGOTTEN: 'dataStoredForgotten', } function reducer(state: WizardStoreState, action: WizardAction) { switch (action.type) { case ACTIONS.SET_FORM_DATA: return { ...state, formData: { ...state.formData, ...action.payload, }, formDataChanged: [ ...state.formDataChanged, ...Object.keys(action.payload), ], } case ACTIONS.POST_DATA: return { ...state, postData: true, postDataError: null, postDataSuccess: null, } case ACTIONS.POST_DATA_ERROR: return { ...state, postData: false, postDataError: action.payload, postDataSuccess: null, } case ACTIONS.POST_DATA_SUCCESS: return { ...state, formData: { ...state.formData, }, booking: { ...action.payload }, postData: false, postDataError: null, postDataSuccess: true, } case ACTIONS.DATA_STORED_LOADED: return { ...state, dataStoredLoaded: true, formData: { ...state.formData, ...action.payload, }, } case ACTIONS.DATA_STORED_FORGOTTEN: return { ...state, dataStored: undefined, dataStoredLoaded: undefined, formData: { ...initialState.formData }, } case ACTIONS.DATA_STORED: return { ...state, dataStored: action.payload, } default: throw new Error(`Unkown Action type ${action.type}`) } } const initialState: WizardStoreState = { postData: false, postDataError: null, postDataSuccess: null, formData: { startDate: '', endDate: '', purpose: '', org: '', destination: '', name: '', email: '', street: '', zip: '', city: '', }, formDataChanged: [], dataStored: undefined, dataStoredLoaded: undefined, } async function createBooking(formData: WizardFormData) { const response = await fetch('/api/booking', { method: 'POST', mode: 'cors', cache: 'no-cache', credentials: 'same-origin', headers: { 'Content-Type': 'application/json', }, referrerPolicy: 'no-referrer', body: JSON.stringify(formData), }) if (response.status === 400) { const error = await response.json() throw new ValidationError(error.errors) } if (!response.ok) { throw Error( 'Sorry, konnte nicht gespeichert werden. Bitte versuch es später nochmal!' ) } return response.json() } export default function WizardStore({ children }) { const [state, dispatch] = useReducer(reducer, initialState) useEffect(() => { const data = loadBookingData() if (data !== null) { dispatch({ type: ACTIONS.DATA_STORED_LOADED, payload: data }) } }, []) const onChangeEvent = ( event: React.ChangeEvent> ) => { const { name, value } = event.target dispatch({ type: ACTIONS.SET_FORM_DATA, payload: { [name]: value }, }) } const onChange = (data: object) => { dispatch({ type: ACTIONS.SET_FORM_DATA, payload: data, }) } const onSubmit = async () => { dispatch({ type: ACTIONS.POST_DATA }) try { const booking = await createBooking(state.formData) dispatch({ type: ACTIONS.POST_DATA_SUCCESS, payload: booking }) } catch (error) { console.error(error) dispatch({ type: ACTIONS.POST_DATA_ERROR, payload: error.message }) } } const storeData = (value: boolean) => { if (value) { storeBookingData(state.booking) } dispatch({ type: ACTIONS.DATA_STORED, payload: value }) } const forgetData = () => { clearBookingData() dispatch({ type: ACTIONS.DATA_STORED_FORGOTTEN }) } return ( {children} ) }