diff --git a/samples/ManagementSite/Alloy.ManagementSite.csproj b/samples/ManagementSite/Alloy.ManagementSite.csproj index dffa3ef..e5b90e1 100644 --- a/samples/ManagementSite/Alloy.ManagementSite.csproj +++ b/samples/ManagementSite/Alloy.ManagementSite.csproj @@ -19,7 +19,7 @@ - + diff --git a/samples/sample-react-app/src/App.tsx b/samples/sample-react-app/src/App.tsx index 85f0908..959936d 100644 --- a/samples/sample-react-app/src/App.tsx +++ b/samples/sample-react-app/src/App.tsx @@ -2,13 +2,13 @@ import './App.css'; import { useFetch } from './hooks/useFetch'; import { Form, FormLogin } from '@episerver/forms-react'; import { extractParams } from './helpers/urlHelper'; -import { FormCache, FormConstants, IdentityInfo, isNullOrEmpty } from '@episerver/forms-sdk'; -import { useEffect, useState } from 'react'; -import { BrowserRouter, useHistory, useLocation } from 'react-router-dom'; +import { FormCache, FormConstants, IdentityInfo } from '@episerver/forms-sdk'; +import { useState } from 'react'; +import { useHistory, useLocation } from 'react-router-dom'; function App() { const location = useLocation(); - const { relativePath, language } = extractParams(window.location.pathname) + const { language } = extractParams(window.location.pathname) const url = `${process.env.REACT_APP_ENDPOINT_GET_FORM_BY_PAGE_URL}${location.pathname}`; const { data: pageData, loading } = useFetch(url); @@ -44,7 +44,7 @@ function App() { /> ))} -
+

Login

{ }); } + useEffect(()=>{ + let intervalId = setInterval(()=>{ + let accessToken = formCache.get(FormConstants.FormAccessToken); + if(isNullOrEmpty(accessToken)){ + setIsAuthenticated(false); + } + },1000); + return ()=>{ + clearInterval(intervalId); + } + },[]); + return ( <> {isAuthenticated ? (
Authenticated
) : ( diff --git a/src/@episerver/forms-react/src/components/FormBody.tsx b/src/@episerver/forms-react/src/components/FormBody.tsx index fdd71de..cb3c5ff 100644 --- a/src/@episerver/forms-react/src/components/FormBody.tsx +++ b/src/@episerver/forms-react/src/components/FormBody.tsx @@ -1,6 +1,6 @@ import React, { useEffect, useRef } from "react"; import { useForms } from "../context/store"; -import { FormContainer, FormSubmitter, IdentityInfo, equals, isInArray, isNull, isNullOrEmpty, FormSubmitModel, FormSubmitResult, SubmitButton, FormValidationResult, FormCache, FormConstants } from "@episerver/forms-sdk"; +import { FormContainer, FormSubmitter, IdentityInfo, equals, isInArray, isNull, isNullOrEmpty, FormSubmitModel, FormSubmitResult, SubmitButton, FormValidationResult, FormCache, FormConstants, ProblemDetail } from "@episerver/forms-sdk"; import { RenderElementInStep } from "./RenderElementInStep"; import { DispatchFunctions } from "../context/dispatchFunctions"; import { FormStepNavigation } from "./FormStepNavigation"; @@ -39,7 +39,7 @@ export const FormBody = (props: FormBodyProps) => { isStepValidToDisplay = true; if((isFormFinalized.current || isProgressiveSubmit.current) && isSuccess.current) - { + { statusDisplay.current = "Form__Success__Message"; statusMessage.current = form.properties.submitSuccessMessage ?? message.current; } @@ -48,6 +48,10 @@ export const FormBody = (props: FormBodyProps) => { statusDisplay.current = "Form__Warning__Message"; statusMessage.current = message.current; } + else { + statusDisplay.current = "hide"; + statusMessage.current = ""; + } const validationCssClass = validateFail.current ? "ValidationFail" : "ValidationSuccess"; @@ -66,6 +70,11 @@ export const FormBody = (props: FormBodyProps) => { )[0]?.elementKey; } + const showError = (error: string) => { + submissionWarning.current = !isNullOrEmpty(error); + message.current = error; + } + const handleSubmit = (e: any) => { e.preventDefault(); @@ -114,9 +123,8 @@ export const FormBody = (props: FormBodyProps) => { message.current = response.messages.map(m => m.message).join("
"); } else { - submissionWarning.current = true; //ignore validation message - message.current = response.messages.filter(m => isNullOrEmpty(m.identifier)).map(m => m.message).join("
"); + showError(response.messages.filter(m => isNullOrEmpty(m.identifier)).map(m => m.message).join("
")); } //update validation message if(response.validationFail){ @@ -141,26 +149,31 @@ export const FormBody = (props: FormBodyProps) => { isSuccess.current = response.success; isFormFinalized.current = isLastStep && response.success; dispatchFunctions.updateSubmissionKey(response.submissionKey); - dispatchFunctions.updateIsSubmitting(false); - localFormCache.set(submissionStorageKey, response.submissionKey) if (isFormFinalized.current) { formCache.remove(submissionStorageKey) localFormCache.remove(submissionStorageKey) } + }).catch((e: ProblemDetail) => { + if(e.status === 401) { + //clear access token to ask login again + dispatchFunctions.updateIdentity({} as IdentityInfo); + formCache.remove(FormConstants.FormAccessToken); + showError(e.detail); + } + }).finally(()=>{ + dispatchFunctions.updateIsSubmitting(false); }); } useEffect(() => { dispatchFunctions.updateIdentity(props.identityInfo); if (isNullOrEmpty(props.identityInfo?.accessToken) && !form.properties.allowAnonymousSubmission) { - statusDisplay.current = "Form__Warning__Message"; - statusMessage.current = "You must be logged in to submit this form. If you are logged in and still cannot post, make sure \"Do not track\" in your browser settings is disabled."; + showError(form.localizations["allowAnonymousSubmissionErrorMessage"]); } else { - statusMessage.current = ""; - statusDisplay.current = "hide"; + showError(""); } }, [props.identityInfo?.accessToken]); diff --git a/src/@episerver/forms-react/src/components/FormStepNavigation.tsx b/src/@episerver/forms-react/src/components/FormStepNavigation.tsx index b2abaea..836b73a 100644 --- a/src/@episerver/forms-react/src/components/FormStepNavigation.tsx +++ b/src/@episerver/forms-react/src/components/FormStepNavigation.tsx @@ -1,8 +1,7 @@ import React, { useRef } from "react"; -import { useForms, useFormsDispatch } from "../context/store"; +import { useForms } from "../context/store"; import { FormCache, FormConstants, FormContainer, FormStep, StepDependCondition, SubmitButtonType, isNull } from "@episerver/forms-sdk"; import { DispatchFunctions } from "../context/dispatchFunctions"; -import { useHistory } from "react-router-dom"; interface FormStepNavigationProps { isFormFinalized: boolean diff --git a/src/@episerver/forms-sdk/package.json b/src/@episerver/forms-sdk/package.json index 6f11bec..ded3b2c 100644 --- a/src/@episerver/forms-sdk/package.json +++ b/src/@episerver/forms-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@episerver/forms-sdk", - "version": "1.0.0", + "version": "0.1.0", "description": "Forms SDK with client validation, step navigation, submit form, element depends", "author": "Optimizely", "license": "ISC", diff --git a/src/@episerver/forms-sdk/src/form-submit/formSubmit.ts b/src/@episerver/forms-sdk/src/form-submit/formSubmit.ts index 7489221..f767970 100644 --- a/src/@episerver/forms-sdk/src/form-submit/formSubmit.ts +++ b/src/@episerver/forms-sdk/src/form-submit/formSubmit.ts @@ -1,7 +1,7 @@ import { FormStorage } from "../form-storage"; import { FormValidator } from "../form-validator"; import { equals, isNull } from "../helpers"; -import { FormConstants, FormContainer, FormSubmission, FormValidationResult } from "../models"; +import { FormConstants, FormContainer, FormSubmission, FormValidationResult, ProblemDetail } from "../models"; import { ApiConstant } from "../form-loader/apiConstant"; export interface FormSubmitModel { @@ -149,8 +149,9 @@ export class FormSubmitter { fetch(`${this._baseUrl}${ApiConstant.apiEndpoint}`, requestInit) .then(async (response: Response) => { + let json = await response.json(); if(response.ok){ - let result = (await response.json()) as FormSubmitResult; + let result = json as FormSubmitResult; if(result.success && model.isFinalized){ //clear cache of form submission formStorage.removeFormDataInStorage(); @@ -158,7 +159,7 @@ export class FormSubmitter { resolve(result); } else { - reject(response); + reject(json as ProblemDetail); } }) .catch((error: any) => { diff --git a/src/@episerver/forms-sdk/src/models/IdentityInfo.ts b/src/@episerver/forms-sdk/src/models/IdentityInfo.ts index 8da8e1b..4bb1b74 100644 --- a/src/@episerver/forms-sdk/src/models/IdentityInfo.ts +++ b/src/@episerver/forms-sdk/src/models/IdentityInfo.ts @@ -1,3 +1,6 @@ +/** + * Represent type of object that is identity information + */ export interface IdentityInfo { username: string, accessToken: string diff --git a/src/@episerver/forms-sdk/src/models/ProblemDetail.ts b/src/@episerver/forms-sdk/src/models/ProblemDetail.ts new file mode 100644 index 0000000..b791729 --- /dev/null +++ b/src/@episerver/forms-sdk/src/models/ProblemDetail.ts @@ -0,0 +1,11 @@ +/** + * Represent type of API exception + */ +export interface ProblemDetail { + type: string; + title: string; + status: number; + detail: string; + instance: string; + traceId: string; +} \ No newline at end of file diff --git a/src/@episerver/forms-sdk/src/models/enums/ConditionCombinationType.ts b/src/@episerver/forms-sdk/src/models/enums/ConditionCombinationType.ts index 8747ec6..424092d 100644 --- a/src/@episerver/forms-sdk/src/models/enums/ConditionCombinationType.ts +++ b/src/@episerver/forms-sdk/src/models/enums/ConditionCombinationType.ts @@ -1,3 +1,6 @@ +/** + * How to combine result of all condition satisfy + */ export enum ConditionCombinationType{ All = "All", Any = "Any" diff --git a/src/@episerver/forms-sdk/src/models/enums/ConditionFunctionType.ts b/src/@episerver/forms-sdk/src/models/enums/ConditionFunctionType.ts index db8f5eb..187533c 100644 --- a/src/@episerver/forms-sdk/src/models/enums/ConditionFunctionType.ts +++ b/src/@episerver/forms-sdk/src/models/enums/ConditionFunctionType.ts @@ -1,3 +1,6 @@ +/** + * enum represent a dependent condition Operator (in the Rule to evaluate depend value). + */ export enum ConditionFunctionType { MatchRegularExpression = "MatchRegularExpression", Contains = "Contains", diff --git a/src/@episerver/forms-sdk/src/models/enums/FormConstants.ts b/src/@episerver/forms-sdk/src/models/enums/FormConstants.ts index fe85602..45ef87e 100644 --- a/src/@episerver/forms-sdk/src/models/enums/FormConstants.ts +++ b/src/@episerver/forms-sdk/src/models/enums/FormConstants.ts @@ -1,3 +1,6 @@ +/** + * The list of constants is the keyword used for cached values + */ export enum FormConstants { FormAccessToken = "form_access_token", FormFieldPrefix = "__field_", diff --git a/src/@episerver/forms-sdk/src/models/enums/SatisfiedActionType.ts b/src/@episerver/forms-sdk/src/models/enums/SatisfiedActionType.ts index 99601c8..20d66f6 100644 --- a/src/@episerver/forms-sdk/src/models/enums/SatisfiedActionType.ts +++ b/src/@episerver/forms-sdk/src/models/enums/SatisfiedActionType.ts @@ -1,3 +1,6 @@ +/** + * The action when condition is satisfied + */ export enum SatisfiedActionType{ Show = "show", Hide = "hide" diff --git a/src/@episerver/forms-sdk/src/models/enums/SubmitButtonType.ts b/src/@episerver/forms-sdk/src/models/enums/SubmitButtonType.ts index 3675590..f9bf872 100644 --- a/src/@episerver/forms-sdk/src/models/enums/SubmitButtonType.ts +++ b/src/@episerver/forms-sdk/src/models/enums/SubmitButtonType.ts @@ -1,3 +1,6 @@ +/** + * Enum represent type name of step navigation + */ export enum SubmitButtonType { PreviousStep = "PreviousStep", NextStep = "NextStep" diff --git a/src/@episerver/forms-sdk/src/models/enums/ValidatorType.ts b/src/@episerver/forms-sdk/src/models/enums/ValidatorType.ts index c6b3e1e..48516c6 100644 --- a/src/@episerver/forms-sdk/src/models/enums/ValidatorType.ts +++ b/src/@episerver/forms-sdk/src/models/enums/ValidatorType.ts @@ -1,3 +1,6 @@ +/** + * The list of validator type + */ export enum ValidatorType{ RequiredValidator = "RequiredValidator", EmailValidator = "EmailValidator", diff --git a/src/@episerver/forms-sdk/src/models/index.ts b/src/@episerver/forms-sdk/src/models/index.ts index 5b84daf..db4bf95 100644 --- a/src/@episerver/forms-sdk/src/models/index.ts +++ b/src/@episerver/forms-sdk/src/models/index.ts @@ -4,4 +4,5 @@ export * from "./elements"; export * from "./validators"; export * from "./enums"; export * from "./states"; -export * from "./IdentityInfo"; \ No newline at end of file +export * from "./IdentityInfo"; +export * from "./ProblemDetail"; \ No newline at end of file diff --git a/src/@episerver/forms-sdk/src/models/states/FormState.ts b/src/@episerver/forms-sdk/src/models/states/FormState.ts index 5224c7d..1b5ede9 100644 --- a/src/@episerver/forms-sdk/src/models/states/FormState.ts +++ b/src/@episerver/forms-sdk/src/models/states/FormState.ts @@ -5,7 +5,7 @@ import { FormValidationResult } from "./FormValidation" import { StepDependencies } from "./StepDependencies" /** - * Interface to define type of object that is form context + * Represent type of object that is form context */ export interface FormState { isReset: boolean diff --git a/src/@episerver/forms-sdk/src/models/states/FormSubmission.ts b/src/@episerver/forms-sdk/src/models/states/FormSubmission.ts index 21257f5..5ab107a 100644 --- a/src/@episerver/forms-sdk/src/models/states/FormSubmission.ts +++ b/src/@episerver/forms-sdk/src/models/states/FormSubmission.ts @@ -1,3 +1,6 @@ +/** + * Represent type of object that is form submission + */ export interface FormSubmission { elementKey: string value: any diff --git a/src/@episerver/forms-sdk/src/models/states/FormValidation.ts b/src/@episerver/forms-sdk/src/models/states/FormValidation.ts index b14aa71..6383623 100644 --- a/src/@episerver/forms-sdk/src/models/states/FormValidation.ts +++ b/src/@episerver/forms-sdk/src/models/states/FormValidation.ts @@ -1,5 +1,5 @@ /** - * Interface to define type of object that is form validation result + * Represent type of object that is form validation result */ export interface FormValidationResult { @@ -8,7 +8,7 @@ export interface FormValidationResult } /** - * Interface to define type of object that is element validation result + * Represent type of object that is element validation result */ export interface ElementValidationResult { type: string