2021-01-31 11:48:39 +03:00
import { Button } from 'antd' ;
2021-02-01 08:36:14 +03:00
import classNames from 'classnames' ;
2022-09-07 10:00:28 +03:00
import React , { FC , useContext , useEffect , useState } from 'react' ;
2021-07-09 21:42:01 +03:00
import { UpdateArgs } from '../../types/config-section' ;
import { postConfigUpdateToAPI , RESET_TIMEOUT } from '../../utils/config-constants' ;
2021-01-31 12:38:20 +03:00
import {
createInputStatus ,
StatusState ,
STATUS_ERROR ,
STATUS_PROCESSING ,
STATUS_SUCCESS ,
2021-02-07 06:38:58 +03:00
} from '../../utils/input-statuses' ;
2021-07-09 21:42:01 +03:00
import { ServerStatusContext } from '../../utils/server-status-context' ;
2022-09-07 10:00:28 +03:00
import { FormStatusIndicator } from './FormStatusIndicator' ;
import { TextField , TextFieldProps } from './TextField' ;
2021-01-31 11:48:39 +03:00
export const TEXTFIELD_TYPE_TEXT = 'default' ;
export const TEXTFIELD_TYPE_PASSWORD = 'password' ; // Input.Password
export const TEXTFIELD_TYPE_NUMBER = 'numeric' ;
export const TEXTFIELD_TYPE_TEXTAREA = 'textarea' ;
export const TEXTFIELD_TYPE_URL = 'url' ;
2022-09-07 10:00:28 +03:00
export type TextFieldWithSubmitProps = TextFieldProps & {
2021-01-31 11:48:39 +03:00
apiPath : string ;
configPath? : string ;
initialValue? : string ;
2022-09-07 10:00:28 +03:00
} ;
2021-01-31 11:48:39 +03:00
2022-09-07 10:00:28 +03:00
export const TextFieldWithSubmit : FC < TextFieldWithSubmitProps > = ( {
apiPath ,
configPath = '' ,
initialValue ,
useTrim ,
useTrimLead ,
. . . textFieldProps // rest of props
} ) = > {
2021-02-01 09:07:00 +03:00
const [ submitStatus , setSubmitStatus ] = useState < StatusState > ( null ) ;
2021-01-31 11:48:39 +03:00
const [ hasChanged , setHasChanged ] = useState ( false ) ;
const serverStatusData = useContext ( ServerStatusContext ) ;
const { setFieldInConfigState } = serverStatusData || { } ;
let resetTimer = null ;
2023-03-04 01:40:03 +03:00
const { fieldName , required , tip , status , value , onChange , onSubmit } = textFieldProps ;
2021-01-31 11:48:39 +03:00
// Clear out any validation states and messaging
const resetStates = ( ) = > {
2021-02-01 09:07:00 +03:00
setSubmitStatus ( null ) ;
2021-01-31 11:48:39 +03:00
setHasChanged ( false ) ;
clearTimeout ( resetTimer ) ;
resetTimer = null ;
} ;
useEffect ( ( ) = > {
// TODO: Add native validity checks here, somehow
// https://developer.mozilla.org/en-US/docs/Web/API/ValidityState
// const hasValidity = (type !== TEXTFIELD_TYPE_NUMBER && e.target.validity.valid) || type === TEXTFIELD_TYPE_NUMBER ;
if ( ( required && ( value === '' || value === null ) ) || value === initialValue ) {
setHasChanged ( false ) ;
} else {
// show submit button
resetStates ( ) ;
setHasChanged ( true ) ;
}
} , [ value ] ) ;
// if field is required but value is empty, or equals initial value, then don't show submit/update button. otherwise clear out any result messaging and display button.
const handleChange = ( { fieldName : changedFieldName , value : changedValue } : UpdateArgs ) = > {
if ( onChange ) {
2021-07-09 21:42:01 +03:00
let newValue : string = changedValue ;
if ( useTrim ) {
newValue = changedValue . trim ( ) ;
} else if ( useTrimLead ) {
newValue = changedValue . replace ( /^\s+/g , '' ) ;
}
2021-05-23 09:28:21 +03:00
onChange ( {
fieldName : changedFieldName ,
2021-07-09 21:42:01 +03:00
value : newValue ,
2021-05-23 09:28:21 +03:00
} ) ;
2021-01-31 11:48:39 +03:00
}
} ;
// if you blur a required field with an empty value, restore its original value in state (parent's state), if an onChange from parent is available.
const handleBlur = ( { value : changedValue } : UpdateArgs ) = > {
if ( onChange && required && changedValue === '' ) {
onChange ( { fieldName , value : initialValue } ) ;
}
} ;
// how to get current value of input
const handleSubmit = async ( ) = > {
2021-01-31 12:55:19 +03:00
if ( ( required && value !== '' ) || value !== initialValue ) {
2021-02-01 09:07:00 +03:00
setSubmitStatus ( createInputStatus ( STATUS_PROCESSING ) ) ;
2021-01-31 11:48:39 +03:00
await postConfigUpdateToAPI ( {
apiPath ,
2021-01-31 12:55:19 +03:00
data : { value } ,
2021-01-31 11:48:39 +03:00
onSuccess : ( ) = > {
2021-01-31 12:55:19 +03:00
setFieldInConfigState ( { fieldName , value , path : configPath } ) ;
2021-02-01 09:07:00 +03:00
setSubmitStatus ( createInputStatus ( STATUS_SUCCESS ) ) ;
2021-01-31 11:48:39 +03:00
} ,
onError : ( message : string ) = > {
2021-02-01 09:07:00 +03:00
setSubmitStatus ( createInputStatus ( STATUS_ERROR , ` There was an error: ${ message } ` ) ) ;
2021-01-31 11:48:39 +03:00
} ,
} ) ;
resetTimer = setTimeout ( resetStates , RESET_TIMEOUT ) ;
// if an extra onSubmit handler was sent in as a prop, let's run that too.
if ( onSubmit ) {
onSubmit ( ) ;
}
}
2021-01-31 12:38:20 +03:00
} ;
2021-01-31 11:48:39 +03:00
2021-02-01 08:36:14 +03:00
const textfieldContainerClass = classNames ( {
'textfield-with-submit-container' : true ,
submittable : hasChanged ,
} ) ;
2021-01-31 11:48:39 +03:00
return (
2021-02-01 08:36:14 +03:00
< div className = { textfieldContainerClass } >
< div className = "textfield-component" >
< TextField
{ . . . textFieldProps }
onSubmit = { null }
onBlur = { handleBlur }
onChange = { handleChange }
/ >
< / div >
2021-02-13 10:55:59 +03:00
< div className = "formfield-container lower-container" >
2021-02-01 08:36:14 +03:00
< p className = "label-spacer" / >
< div className = "lower-content" >
< div className = "field-tip" > { tip } < / div >
2021-02-01 11:36:27 +03:00
< FormStatusIndicator status = { status || submitStatus } / >
2021-02-01 08:36:14 +03:00
< div className = "update-button-container" >
2023-03-04 01:40:03 +03:00
< Button
type = "primary"
size = "small"
className = "submit-button"
onClick = { handleSubmit }
disabled = { ! hasChanged }
>
Update
< / Button >
2021-02-01 08:36:14 +03:00
< / div >
2021-01-31 21:13:35 +03:00
< / div >
2021-02-01 08:36:14 +03:00
< / div >
2021-01-31 11:48:39 +03:00
< / div >
2021-01-31 12:38:20 +03:00
) ;
2022-09-07 10:00:28 +03:00
} ;
2021-01-31 11:48:39 +03:00
TextFieldWithSubmit . defaultProps = {
configPath : '' ,
initialValue : '' ,
} ;