@@ -11,8 +11,11 @@ import {
1111 useSearchParams ,
1212} from 'react-router-dom' ;
1313import {
14+ Alert ,
1415 Button ,
16+ InlineLayout ,
1517 ListView ,
18+ Modal ,
1619 Tab ,
1720 TabList ,
1821 TabPanel ,
@@ -28,6 +31,7 @@ import {
2831 listToMap ,
2932} from '@togglecorp/fujs' ;
3033import {
34+ analyzeErrors ,
3135 createSubmitHandler ,
3236 removeNull ,
3337 useForm ,
@@ -40,6 +44,7 @@ import useRouting from '#hooks/useRouting';
4044import {
4145 EAP_STATUS_NS_ADDRESSING_COMMENTS ,
4246 EAP_STATUS_UNDER_DEVELOPMENT ,
47+ EAP_STATUS_UNDER_REVIEW ,
4348} from '#utils/constants' ;
4449import {
4550 type GoApiBody ,
@@ -76,6 +81,8 @@ import i18n from './i18n.json';
7681type EapFullRequestBody = GoApiBody < '/api/v2/full-eap/' , 'POST' > ;
7782type GetFullEapResponse = GoApiResponse < '/api/v2/full-eap/{id}/' > ;
7883
84+ type EapStatusBody = GoApiBody < '/api/v2/eap-registration/{id}/status/' , 'POST' > ;
85+
7986function getNextStep ( current : TabKeys , direction : 1 | - 1 ) {
8087 const tabKeyList : TabKeys [ ] = [
8188 'overview' ,
@@ -99,11 +106,16 @@ export function Component() {
99106 const [ fileIdToUrlMap , setFileIdToUrlMap ] = useState < Record < number , string > > (
100107 { } ,
101108 ) ;
109+ const [ shouldSubmit , setShouldSubmit ] = useState ( false ) ;
110+
102111 const { eapId } = useParams < { eapId : string } > ( ) ;
103112 const [ searchParams ] = useSearchParams ( ) ;
113+
104114 const version = searchParams . get ( 'version' ) ?? undefined ;
105- const formContentRef = useRef < ElementRef < 'div' > > ( null ) ;
115+ const tabListRef = useRef < ElementRef < 'div' > > ( null ) ;
116+
106117 const strings = useTranslation ( i18n ) ;
118+
107119 const { navigate } = useRouting ( ) ;
108120
109121 const alert = useAlert ( ) ;
@@ -556,14 +568,13 @@ export function Component() {
556568 }
557569 } , [ fullEapResponse , loadResponseToFormValue ] ) ;
558570
559- const { pending : eapFullPending , trigger : createFullEap } = useLazyRequest ( {
571+ const { pending : createFullEapPending , trigger : triggerCreateFullEap } = useLazyRequest ( {
560572 method : 'POST' ,
561573 url : '/api/v2/full-eap/' ,
562574 body : ( body : EapFullRequestBody ) => body ,
563575 onSuccess : ( ) => {
564576 const message = strings . successMessage ;
565577 alert . show ( message , { variant : 'success' } ) ;
566- navigate ( 'accountMyFormsEap' ) ;
567578 } ,
568579 onFailure : ( err ) => {
569580 const {
@@ -579,18 +590,15 @@ export function Component() {
579590 } ,
580591 } ) ;
581592
582- const { pending : updateFullFormPending , trigger : updateFullEap } = useLazyRequest ( {
593+ const { pending : updateFullFormPending , trigger : triggerUpdateFullEap } = useLazyRequest ( {
583594 url : '/api/v2/full-eap/{id}/' ,
584595 method : 'PATCH' ,
585596 pathVariables : {
586597 id : Number ( latestFullEapId ) ,
587598 } ,
588599 body : ( formFields : EapFullRequestBody ) => formFields ,
589- onSuccess : ( response ) => {
600+ onSuccess : ( ) => {
590601 alert . show ( strings . updateSuccess , { variant : 'success' } ) ;
591-
592- // FIXME: only navigate to accounts page for the submit action
593- navigate ( 'accountMyFormsEap' , { params : { eapId : response . id } } ) ;
594602 } ,
595603 onFailure : ( err ) => {
596604 const {
@@ -606,6 +614,35 @@ export function Component() {
606614 } ,
607615 } ) ;
608616
617+ const { trigger : triggerStatusUpdate } = useLazyRequest ( {
618+ method : 'POST' ,
619+ url : '/api/v2/eap-registration/{id}/status/' ,
620+ pathVariables : isTruthyString ( eapId ) ? {
621+ id : Number ( eapId ) ,
622+ } : undefined ,
623+ body : ( ) => ( {
624+ status : EAP_STATUS_UNDER_REVIEW ,
625+ // FIXME: fix typings in the server
626+ } as EapStatusBody ) ,
627+ onSuccess : ( ) => {
628+ alert . show (
629+ strings . submitApprovalSuccess ,
630+ { variant : 'success' } ,
631+ ) ;
632+
633+ navigate ( 'accountMyFormsEap' ) ;
634+ } ,
635+ onFailure : ( error ) => {
636+ alert . show (
637+ strings . submitFailedSuccess ,
638+ {
639+ variant : 'danger' ,
640+ description : error . value . messageForNotification ,
641+ } ,
642+ ) ;
643+ } ,
644+ } ) ;
645+
609646 useEffect ( ( ) => {
610647 if ( isNotDefined ( eapDetailResponse ) ) {
611648 return ;
@@ -660,36 +697,39 @@ export function Component() {
660697
661698 const readOnly = ! isEditable ;
662699
663- const disabled = eapFullPending || updateFullFormPending || fetchingEap ;
700+ const disabled = createFullEapPending || updateFullFormPending || fetchingEap ;
664701
665702 const nextStep = getNextStep ( activeTab , 1 ) ;
666703 const prevStep = getNextStep ( activeTab , - 1 ) ;
667704
668705 const handleValidationSuccess = useCallback (
669706 ( validatedFormValue : PartialEapFullFormType ) => {
670707 if ( isNotDefined ( latestFullEapId ) ) {
671- createFullEap ( {
708+ triggerCreateFullEap ( {
672709 ...( validatedFormValue as unknown as EapFullRequestBody ) ,
673710 eap_registration : Number ( eapId ) ,
674711 } ) ;
675712 } else {
676- updateFullEap ( {
713+ triggerUpdateFullEap ( {
677714 ...validatedFormValue ,
678715 id : latestFullEapId ,
679716 } as unknown as EapFullRequestBody ) ;
680717 }
681718 } ,
682- [ eapId , createFullEap , latestFullEapId , updateFullEap ] ,
719+ [ eapId , triggerCreateFullEap , latestFullEapId , triggerUpdateFullEap ] ,
683720 ) ;
684721
685722 const handleTabChange = useCallback ( ( newTab : TabKeys ) => {
686- formContentRef . current ?. scrollIntoView ( ) ;
687723 setActiveTab ( newTab ) ;
724+ tabListRef . current ?. scrollIntoView ( { behavior : 'smooth' } ) ;
688725 } , [ ] ) ;
689726
690727 const handleFormError = useCallback ( ( ) => {
691- setTimeout ( ( ) => formContentRef . current ?. scrollIntoView ( ) , 200 ) ;
692- } , [ ] ) ;
728+ alert . show (
729+ strings . validationErrorAlertMessage ,
730+ { variant : 'warning' } ,
731+ ) ;
732+ } , [ alert , strings . validationErrorAlertMessage ] ) ;
693733
694734 const handleSave = useMemo (
695735 ( ) => createSubmitHandler (
@@ -701,6 +741,25 @@ export function Component() {
701741 [ handleFormError , handleValidationSuccess , validate , setError ] ,
702742 ) ;
703743
744+ const handleRequestForApprovalButtonClick = useCallback ( ( ) => {
745+ setShouldSubmit ( true ) ;
746+ handleSave ( ) ;
747+ } , [ handleSave ] ) ;
748+
749+ const handleRequestForApprovalCancel = useCallback ( ( ) => {
750+ setShouldSubmit ( false ) ;
751+ } , [ ] ) ;
752+
753+ const handleSubmitForApprovalConfirm = useCallback ( ( ) => {
754+ triggerStatusUpdate ( null ) ;
755+ setShouldSubmit ( false ) ;
756+ } , [ triggerStatusUpdate ] ) ;
757+
758+ const hasFormErrors = useMemo (
759+ ( ) => analyzeErrors ( formError ) ,
760+ [ formError ] ,
761+ ) ;
762+
704763 return (
705764 < Tabs value = { activeTab } onChange = { setActiveTab } styleVariant = "step" >
706765 < Page
@@ -740,7 +799,7 @@ export function Component() {
740799 )
741800 }
742801 info = { (
743- < TabList >
802+ < TabList elementRef = { tabListRef } >
744803 < Tab
745804 name = "overview"
746805 step = { 1 }
@@ -891,29 +950,78 @@ export function Component() {
891950 isRevision = { isRevision }
892951 />
893952 </ TabPanel >
894- < ListView withCenteredContents >
895- < Button
896- name = { prevStep ?? activeTab }
897- onClick = { handleTabChange }
898- disabled = { isNotDefined ( prevStep ) }
899- >
900- { strings . backButton }
901- </ Button >
902- { isDefined ( nextStep ) ? (
903- < Button name = { nextStep ?? activeTab } onClick = { handleTabChange } >
904- { strings . nextButton }
905- </ Button >
906- ) : (
953+ < InlineLayout
954+ after = { (
907955 < Button
908956 name = { undefined }
909957 onClick = { handleSave }
910- disabled = { readOnly }
911958 >
912959 { strings . saveButton }
913960 </ Button >
914961 ) }
915- </ ListView >
962+ >
963+ < ListView withCenteredContents >
964+ < Button
965+ name = { prevStep ?? activeTab }
966+ onClick = { handleTabChange }
967+ disabled = { isNotDefined ( prevStep ) }
968+ >
969+ { strings . backButton }
970+ </ Button >
971+ { isDefined ( nextStep ) ? (
972+ < Button
973+ name = { nextStep ?? activeTab }
974+ onClick = { handleTabChange }
975+ >
976+ { strings . nextButton }
977+ </ Button >
978+ ) : (
979+ < Button
980+ name = { undefined }
981+ onClick = { handleRequestForApprovalButtonClick }
982+ disabled = { readOnly }
983+ >
984+ { strings . submitButtonLabel }
985+ </ Button >
986+ ) }
987+ </ ListView >
988+ </ InlineLayout >
916989 </ Page >
990+ { shouldSubmit && (
991+ < Modal
992+ heading = { strings . submitConfirmHeading }
993+ onClose = { handleRequestForApprovalCancel }
994+ pending = { createFullEapPending || updateFullFormPending }
995+ pendingMessage = { strings . savingPendingMessage }
996+ footerActions = { (
997+ < Button
998+ name = { undefined }
999+ disabled = { hasFormErrors }
1000+ onClick = { handleSubmitForApprovalConfirm }
1001+ >
1002+ { strings . submitConfirmButtonLabel }
1003+ </ Button >
1004+ ) }
1005+ >
1006+ < ListView
1007+ layout = "block"
1008+ spacing = "sm"
1009+ >
1010+ < div >
1011+ { strings . submitConfirmMessage }
1012+ </ div >
1013+ { hasFormErrors && (
1014+ < Alert
1015+ name = "form-error-warning"
1016+ title = { strings . submitFormErrorMessage }
1017+ type = "warning"
1018+ withLightBackground
1019+ withoutShadow
1020+ />
1021+ ) }
1022+ </ ListView >
1023+ </ Modal >
1024+ ) }
9171025 </ Tabs >
9181026 ) ;
9191027}
0 commit comments