react-hook-form#FormProvider TypeScript Examples
The following examples show how to use
react-hook-form#FormProvider.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: contexts.tsx From plasmic with MIT License | 6 votes |
export function ProductProvider({
product,
children,
}: {
product: Product;
children: React.ReactNode;
}) {
const methods = useForm();
return (
<ProductContext.Provider value={product} key={product.id}>
<FormProvider {...methods}>{children}</FormProvider>
</ProductContext.Provider>
);
}
Example #2
Source File: VariantNameFormProvider.tsx From calories-in with MIT License | 6 votes |
function VariantNameFormProvider({ children, variantFormIndex }: Props) {
const dietForm = useDietForm()
const { variantsForms } = dietForm
const variantForm =
variantFormIndex !== undefined ? variantsForms[variantFormIndex] : undefined
const defaultValues = getVariantNameForm(
variantFormIndex !== undefined
? dietForm.variantsForms[variantFormIndex].name
: ''
)
const formMethods = useForm<VariantNameForm, VariantNameFormSchemaContext>({
defaultValues,
mode: 'onChange',
context: {
variantsForms,
variantForm,
},
resolver: yupResolver(variantNameFormSchema),
})
return <FormProvider {...formMethods}>{children}</FormProvider>
}
Example #3
Source File: CreditCardForm.test.tsx From rn-credit-card with MIT License | 6 votes |
Wrapper = () => {
const formMethods = useForm({
mode: 'onBlur',
defaultValues: {
holderName: '',
cardNumber: '',
expiration: '',
cvv: '',
},
})
const { handleSubmit } = formMethods
const onSubmit = (model: FormModel) => {
get.onSubmit(model)
}
return (
<FormProvider {...formMethods}>
<CreditCardForm />
<Button onPress={handleSubmit(onSubmit)} title={'Submit'} />
</FormProvider>
)
}
Example #4
Source File: index.tsx From tobira with Apache License 2.0 | 6 votes |
EditMode: React.FC<EditModeProps> = props => {
const { realm: realmRef, index } = props;
const result = useFragment(graphql`
fragment EditModeRealmData on Realm {
blocks {
# Querying only the type and the fragments bugs out Relay type generation
id
__typename
... on TitleBlock { ...TitleEditModeBlockData }
... on TextBlock { ...TextEditModeBlockData }
... on SeriesBlock { ...SeriesEditModeBlockData }
... on VideoBlock { ...VideoEditModeBlockData }
}
...EditModeFormRealmData
}
`, realmRef);
const block = result.blocks[index];
const { __typename: type } = block;
const form = useForm();
return <EditModeFormContext.Provider value={{ ...props, realm: result }}>
<FormProvider {...form}>
{match(type, {
TitleBlock: () => <EditTitleBlock block={block} />,
TextBlock: () => <EditTextBlock block={block} />,
SeriesBlock: () => <EditSeriesBlock block={block} />,
VideoBlock: () => <EditVideoBlock block={block} />,
}, () => bug("unknown block type"))}
</FormProvider>
</EditModeFormContext.Provider>;
}
Example #5
Source File: FormContainer.tsx From react-hook-form-mui with MIT License | 6 votes |
FormContainer: FunctionComponent<FormContainerProps> = props => {
if (!props.formContext && !props.handleSubmit) {
return <FormContainerCore {...props} />
} else if (props.handleSubmit && props.formContext) {
return (
<FormProvider {...props.formContext}>
<form
noValidate
{...props.FormProps}
onSubmit={props.handleSubmit}>
{props.children}
</form>
</FormProvider>
)
}
if (props.formContext && props.onSuccess) {
return (
<FormProvider {...props.formContext}>
<form
onSubmit={props.formContext.handleSubmit(props.onSuccess)}
noValidate
{...props.FormProps}
>
{props.children}
</form>
</FormProvider>
)
}
return (
<div>
Incomplete setup of FormContainer..
</div>
)
}
Example #6
Source File: MultiStepForm.tsx From frontend with GNU General Public License v3.0 | 6 votes |
MultiStepForm = ({
children,
title,
onNext,
onBack,
onSubmit,
onCancel,
...props
}: IMultiStepForm): JSX.Element => {
const methods = useForm();
const handleCancel = () => {
onCancel();
methods.reset();
};
return (
<div style={{ padding: '65px' }} {...props}>
<FormProvider {...methods}>
<MultiStepFormBody
title={title}
onNext={onNext}
onBack={onBack}
onSubmit={onSubmit}
onCancel={handleCancel}
>
{children}
</MultiStepFormBody>
</FormProvider>
</div>
);
}
Example #7
Source File: PreparePullRequestForm.tsx From backstage with Apache License 2.0 | 6 votes |
PreparePullRequestForm = < TFieldValues extends Record<string, any>, >( props: PreparePullRequestFormProps<TFieldValues>, ) => { const { defaultValues, onSubmit, render } = props; const methods = useForm<TFieldValues>({ mode: 'onTouched', defaultValues }); const { handleSubmit, watch, control, register, formState, setValue } = methods; return ( <FormProvider {...methods}> <form onSubmit={handleSubmit(onSubmit)}> {render({ values: watch(), formState, register, control, setValue })} </form> </FormProvider> ); }
Example #8
Source File: SpouseAndDependent.tsx From UsTaxes with GNU Affero General Public License v3.0 | 5 votes |
AddDependentForm = (): ReactElement => {
const dependents = useSelector(
(state: TaxesState) => state.information.taxPayer.dependents
)
const defaultValues = blankUserDependentForm
const dispatch = useDispatch()
const methods = useForm<UserDependentForm>({
defaultValues
})
const onSubmitAdd = (formData: UserDependentForm): void => {
dispatch(addDependent(toDependent(formData)))
}
const onSubmitEdit =
(index: number) =>
(formData: UserDependentForm): void => {
dispatch(
editDependent({
index,
value: toDependent(formData)
})
)
}
const page = (
<FormListContainer<UserDependentForm>
defaultValues={defaultValues}
onSubmitAdd={onSubmitAdd}
onSubmitEdit={onSubmitEdit}
items={dependents.map((a) => toDependentForm(a))}
primary={(a) => `${a.firstName} ${a.lastName}`}
secondary={(a) => formatSSID(a.ssid)}
icon={() => <Person />}
removeItem={(i) => dispatch(removeDependent(i))}
>
<Grid container spacing={2}>
<PersonFields />
<LabeledInput
label="Relationship to Taxpayer"
name="relationship"
patternConfig={Patterns.name}
/>
<LabeledInput
label="How many months did you live together this year?"
patternConfig={Patterns.numMonths}
name="numberOfMonths"
/>
<LabeledCheckbox
label="Is this person a full-time student?"
name="isStudent"
/>
</Grid>
</FormListContainer>
)
return <FormProvider {...methods}>{page}</FormProvider>
}
Example #9
Source File: Form.tsx From react-hook-form-generator with MIT License | 5 votes |
Form: FC<FormProps> = ({
title,
schema,
handleSubmit,
formOptions,
overwriteDefaultStyles,
buttons,
styles = {},
}) => {
const form = useForm(formOptions);
const baseStyles = useMemo(() => {
return overwriteDefaultStyles ? styles : merge(defaultStyles, styles);
}, [styles, overwriteDefaultStyles]);
return (
<StyleCtx.Provider value={baseStyles}>
<FormProvider {...form}>
<Box
as="form"
onSubmit={form.handleSubmit(handleSubmit)}
{...baseStyles.form?.container}
>
{!!title && <Heading {...baseStyles.form?.title}>{title}</Heading>}
<Stack spacing={baseStyles.form?.fieldSpacing}>
{Object.entries(schema).map(renderField)}
</Stack>
<ButtonGroup {...baseStyles.form?.buttonGroup}>
{buttons?.reset?.hidden ? null : (
<Button type="reset" {...baseStyles.form?.resetButton}>
{buttons?.reset?.text || 'Reset'}
</Button>
)}
<Button type="submit" {...baseStyles.form?.submitButton}>
{buttons?.submit?.text || 'Submit'}
</Button>
</ButtonGroup>
</Box>
</FormProvider>
</StyleCtx.Provider>
);
}
Example #10
Source File: EstimatedTaxes.tsx From UsTaxes with GNU Affero General Public License v3.0 | 5 votes |
export default function EstimatedTaxes(): ReactElement {
const defaultValues = blankUserInput
const activeYear: TaxYear = useSelector(
(state: YearsTaxesState) => state.activeYear
)
const estimatedTaxes = useYearSelector(
(state) => state.information.estimatedTaxes
)
const dispatch = useDispatch()
const methods = useForm<EstimatedTaxesUserInput>({ defaultValues })
const { navButtons, onAdvance } = usePager()
const onSubmitAdd = (formData: EstimatedTaxesUserInput): void => {
dispatch(addEstimatedPayment(toPayments(formData)))
}
const onSubmitEdit =
(index: number) =>
(formData: EstimatedTaxesUserInput): void => {
dispatch(editEstimatedPayment({ index, value: toPayments(formData) }))
}
const w2sBlock = (
<FormListContainer<EstimatedTaxesUserInput>
defaultValues={defaultValues}
items={estimatedTaxes.map((a) => toEstimatedTaxesUserInput(a))}
onSubmitAdd={onSubmitAdd}
onSubmitEdit={onSubmitEdit}
removeItem={(i) => dispatch(removeEstimatedPayment(i))}
icon={() => <Work />}
primary={(estimatedTaxes: EstimatedTaxesUserInput) =>
estimatedTaxes.label
}
secondary={(estimatedTaxes: EstimatedTaxesUserInput) => (
<span>
Payment: <Currency value={toPayments(estimatedTaxes).payment} />
</span>
)}
>
<Grid container spacing={2}>
<LabeledInput
name="label"
label="label or date of this payment"
patternConfig={Patterns.plain}
sizes={{ xs: 12, lg: 6 }}
/>
<LabeledInput
name="payment"
label="Estimated tax payment"
patternConfig={Patterns.currency}
sizes={{ xs: 12, lg: 6 }}
/>
</Grid>
</FormListContainer>
)
const form: ReactElement = <>{w2sBlock}</>
return (
<form tabIndex={-1} onSubmit={onAdvance}>
<h2>Estimated Taxes</h2>
<p>
Did you already make payments towards your {activeYear} taxes this year
or last year?
</p>
<FormProvider {...methods}>{form}</FormProvider>
{navButtons}
</form>
)
}
Example #11
Source File: Calculator.tsx From po8klasie with GNU General Public License v3.0 | 5 votes |
Calculator: FC = () => {
const calc = useMemo(() => new PointsCalculator(CONFIG_2018_2019), []);
const [points, setPoints] = useState<CalculatedPoints>(initialCalculatedPoints);
const formMethods = useForm({
defaultValues: initialInputData,
});
useEffect(() => {
const subscription = formMethods.watch((values, { name }) => {
let data = values;
if (name) {
data = JSON.parse(JSON.stringify(calc.data)); // deep clone. Object.assign won't work
let val = _.get({ ...values }, name as string);
if (['string', 'number'].includes(typeof val)) {
const parsed = parseFloat(val);
val = !Number.isNaN(parsed) ? parsed : null;
}
_.set(data, name as string, val);
}
calc.setData(data as InputData);
setPoints(calc.points);
});
return () => subscription.unsubscribe();
}, [formMethods, calc]);
const handleReset = () => {
formMethods.reset(initialInputData);
};
return (
<div className="lg:flex mt-4">
<div className="border border-lighten bg-white rounded lg:w-1/2 xl:w-1/3">
<FormProvider {...formMethods}>
{formConfig.matrices.map((sectionData) => (
<CalculatorMatrixSection
key={sectionData.sectionId}
sectionPoints={points[sectionData.sectionId] as ExamResultPoints | GradesPoints}
matrixSectionData={sectionData}
/>
))}
<CalculatorActivitiesSection
checkboxesData={formConfig.checkboxes}
sectionPoints={points}
/>
</FormProvider>
</div>
<div className="mt-5 lg:mt-0 lg:ml-5">
<CalculatorTotal total={points.total} onReset={handleReset} />
</div>
</div>
);
}
Example #12
Source File: gift-card-form-context.tsx From admin with MIT License | 5 votes |
GiftCardFormProvider = ({
giftCard = defaultProduct,
onSubmit,
children,
}) => {
const [images, setImages] = React.useState<any[]>([])
const [hasImagesChanged, setHasImagesChanged] = React.useState(false)
const appendImage = (image) => {
setHasImagesChanged(true)
setImages([...images, image])
}
const removeImage = (image) => {
setHasImagesChanged(true)
const tmp = images.filter((img) => img.url !== image.url)
setImages(tmp)
}
const methods = useForm()
const resetForm = () => {
methods.reset({
...giftCard,
})
setHasImagesChanged(false)
setImages(giftCard.images)
}
useEffect(() => {
resetForm()
}, [giftCard])
const handleSubmit = (values) => {
onSubmit({
...trimValues(values),
images,
})
}
return (
<FormProvider {...methods}>
<GiftCardFormContext.Provider
value={{
images,
setImages,
appendImage,
removeImage,
onSubmit: handleSubmit,
resetForm,
additionalDirtyState: {
images: hasImagesChanged,
},
}}
>
{children}
</GiftCardFormContext.Provider>
</FormProvider>
)
}
Example #13
Source File: App.tsx From rn-credit-card with MIT License | 5 votes |
App: React.FC = () => {
let [fontsLoaded] = useFonts({
RobotoMono_400Regular,
RobotoMono_700Bold,
})
const formMethods = useForm<FormModel>({
// to trigger the validation on the blur event
mode: 'onBlur',
defaultValues: {
holderName: '',
cardNumber: '',
expiration: '',
cvv: '',
},
})
const { handleSubmit, formState } = formMethods
function onSubmit(model: FormModel) {
Alert.alert('Success: ' + JSON.stringify(model, null, 2))
}
if (!fontsLoaded) {
return <AppLoading />
}
return (
<FormProvider {...formMethods}>
<SafeAreaView style={styles.container}>
<KeyboardAvoidingView
style={styles.avoider}
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
>
<CreditCardForm
LottieView={LottieView}
horizontalStart
overrides={{
labelText: {
marginTop: 16,
},
}}
/>
</KeyboardAvoidingView>
{formState.isValid && (
<Button
style={styles.button}
title={'CONFIRM PAYMENT'}
onPress={handleSubmit(onSubmit)}
/>
)}
</SafeAreaView>
</FormProvider>
)
}
Example #14
Source File: GeoPointEditor.test.tsx From firebase-tools-ui with Apache License 2.0 | 5 votes |
TestForm: React.FC<React.PropsWithChildren<unknown>> = ({ children }) => {
const methods = useForm({ mode: 'all' });
return <FormProvider {...methods}>{children}</FormProvider>;
}
Example #15
Source File: index.tsx From tobira with Apache License 2.0 | 5 votes |
EditModeForm = <T extends object, >( { save, create, children }: React.PropsWithChildren<EditModeFormProps<T>>, ) => { const { realm: realmRef, index, onSave, onCancel, onCompleted, onError } = useContext(EditModeFormContext) ?? bug("missing context provider"); const { id: realm, blocks } = useFragment(graphql` fragment EditModeFormRealmData on Realm { id blocks { id } } `, realmRef); const { id } = blocks[index]; const form = useFormContext<T>(); const onSubmit = form.handleSubmit(data => { onSave?.(); if (id.startsWith("cl")) { create({ variables: { realm, index, block: data, }, onCompleted, onError, }); } else { save({ variables: { id, set: data, }, onCompleted, onError, }); } }); return <FormProvider<T> {...form}> <form onSubmit={onSubmit}> {children} <EditModeButtons onCancel={onCancel} /> </form> </FormProvider>; }
Example #16
Source File: index.tsx From firebase-tools-ui with Apache License 2.0 | 5 votes |
DocumentEditor: React.FC<
React.PropsWithChildren<{
value: FirestoreAny;
onChange?: (value?: FirestoreMap) => void;
areRootNamesMutable?: boolean;
areRootFieldsMutable?: boolean;
rtdb?: boolean;
startingIndex?: number;
supportNestedArrays?: boolean;
firestore?: firebase.firestore.Firestore;
}>
> = ({
value,
onChange,
areRootNamesMutable,
areRootFieldsMutable,
rtdb = false,
startingIndex,
supportNestedArrays,
firestore,
}) => {
const initialState = normalize(value);
const [store, dispatch] = React.useReducer(storeReducer, initialState);
const methods = useForm({ mode: 'onChange' });
const denormalizedStore = React.useMemo(
() => denormalize(store, firestore),
[store, firestore]
);
const errorCount = React.useMemo(
() => Object.keys(methods.formState.errors).length,
[methods]
);
useEffect(() => {
if (errorCount === 0) {
onChange && onChange(denormalizedStore as FirestoreMap);
} else {
onChange && onChange(undefined);
}
}, [denormalizedStore, errorCount, onChange]);
return (
<FormProvider {...methods}>
<DocumentStore store={store} dispatch={dispatch}>
<div className="DocumentEditor">
{
<>
{store.uuid !== undefined && (
<FieldEditor
uuid={store.uuid}
isRtdb={rtdb}
areNamesMutable={areRootNamesMutable}
areFieldsMutable={areRootFieldsMutable}
startingIndex={startingIndex}
supportNestedArrays={supportNestedArrays}
/>
)}
</>
}
</div>
</DocumentStore>
</FormProvider>
);
}
Example #17
Source File: DelegationChangeProposalForm.tsx From homebase-app with MIT License | 5 votes |
DelegationChangeProposalForm: React.FC<Props> = ({ open, handleClose, defaultValues }) => {
const daoId = useDAOID();
const { data: dao } = useDAO(daoId);
const { data: daoHoldings } = useDAOHoldings(daoId);
const methods = useForm<Values>({
defaultValues: useMemo(
() => ({
newDelegationAddress: "",
...defaultValues,
}),
[defaultValues]
),
// resolver: yupResolver(validationSchema as any),
});
const newDelegationAddress = methods.watch("newDelegationAddress");
useEffect(() => {
methods.reset(defaultValues);
}, [defaultValues, methods]);
const { mutate } = useProposeDelegationChange();
const onSubmit = useCallback(
(values: Values) => {
if (dao) {
mutate({ dao, newDelegationAddress: values.newDelegationAddress });
handleClose();
}
},
[dao, handleClose, mutate]
);
return (
<FormProvider {...methods}>
<ResponsiveDialog
open={open}
onClose={handleClose}
aria-labelledby='alert-dialog-title'
aria-describedby='alert-dialog-description'
title={"Change Delegate"}>
<Content container direction={"column"} style={{ gap: 18 }}>
<Grid item>
<ProposalFormInput label={"New Delegate Address"}>
<Controller
control={methods.control}
name={`newDelegationAddress`}
render={({ field }) => (
<TextField
{...field}
type='text'
placeholder=' tz1...'
InputProps={{ disableUnderline: true }}
/>
)}
/>
</ProposalFormInput>
</Grid>
<Grid item>
<Typography align='left' variant='subtitle2' color='textPrimary' display={"inline"}>
Proposal Fee:{" "}
</Typography>
<Typography align='left' variant='subtitle2' color='secondary' display={"inline"}>
{dao && dao.data.extra.frozen_extra_value.toString()} {dao ? dao.data.token.symbol : ""}
</Typography>
</Grid>
<SendButton
onClick={methods.handleSubmit(onSubmit as any)}
disabled={!dao || !daoHoldings || !newDelegationAddress}>
Submit
</SendButton>
</Content>
</ResponsiveDialog>
</FormProvider>
);
}
Example #18
Source File: healthSavingsAccounts.tsx From UsTaxes with GNU Affero General Public License v3.0 | 4 votes |
export default function HealthSavingsAccounts(): ReactElement {
const defaultValues = blankUserInput
const hsa = useYearSelector(
(state: TaxesState) => state.information.healthSavingsAccounts
)
const people: Person[] = useYearSelector((state: TaxesState) => [
state.information.taxPayer.primaryPerson,
state.information.taxPayer.spouse
])
.filter((p) => p !== undefined)
.map((p) => p as Person)
const dispatch = useYearDispatch()
const methods = useForm<HSAUserInput>({ defaultValues })
const { handleSubmit } = methods
const activeYear: TaxYear = useSelector(
(state: YearsTaxesState) => state.activeYear
)
const { navButtons, onAdvance } = usePager()
const onSubmitAdd = (formData: HSAUserInput): void => {
dispatch(addHSA(toHSA(formData)))
}
const onSubmitEdit =
(index: number) =>
(formData: HSAUserInput): void => {
dispatch(editHSA({ index, value: toHSA(formData) }))
}
const hsaBlock = (
<FormListContainer<HSAUserInput>
defaultValues={defaultValues}
items={hsa.map((a) => toHSAUserInput(a))}
onSubmitAdd={onSubmitAdd}
onSubmitEdit={onSubmitEdit}
removeItem={(i) => dispatch(removeHSA(i))}
icon={() => <Work />}
primary={(hsa: HSAUserInput) => hsa.label}
secondary={(hsa: HSAUserInput) => (
<span>
contributions: <Currency value={toHSA(hsa).contributions} />
<br />
total distributions:{' '}
<Currency value={toHSA(hsa).totalDistributions} />
<br />
qualified distributions:{' '}
<Currency value={toHSA(hsa).qualifiedDistributions} />
<br />
coverage type: {hsa.coverageType}
<br />
coverage span: {format(hsa.startDate, 'MMMM do, yyyy')} to{' '}
{format(hsa.endDate, 'MMMM do, yyyy')}
</span>
)}
>
<Grid container spacing={2}>
<LabeledInput<HSAUserInput>
name="label"
label="label for this account"
patternConfig={Patterns.plain}
sizes={{ xs: 12, lg: 6 }}
/>
<LabeledInput<HSAUserInput>
name="contributions"
label="Your total contributions to this account."
patternConfig={Patterns.currency}
sizes={{ xs: 12, lg: 6 }}
/>
<LabeledInput<HSAUserInput>
name="totalDistributions"
label="Total distributions from this account."
patternConfig={Patterns.currency}
sizes={{ xs: 12, lg: 6 }}
/>
<LabeledInput<HSAUserInput>
name="qualifiedDistributions"
label="Qualified medical distributions from this account."
patternConfig={Patterns.currency}
sizes={{ xs: 12, lg: 6 }}
/>
<LabeledDropdown<HSAUserInput>
dropDownData={['self-only', 'family']}
label="Coverage Type"
name="coverageType"
/>
<GenericLabeledDropdown<Person, HSAUserInput>
dropDownData={people}
label="Recipient"
valueMapping={(p: Person, i: number) =>
[PersonRole.PRIMARY, PersonRole.SPOUSE][i]
}
name="personRole"
keyMapping={(p: Person, i: number) => i}
textMapping={(p: Person) =>
`${p.firstName} ${p.lastName} (${formatSSID(p.ssid)})`
}
/>
<DatePicker
name="startDate"
label="Starting Date"
sizes={{ xs: 12, lg: 6 }}
minDate={new Date(TaxYears[activeYear], 0, 1)}
maxDate={new Date(TaxYears[activeYear], 11, 31)}
/>
<DatePicker
name="endDate"
label="Ending Date"
sizes={{ xs: 12, lg: 6 }}
minDate={new Date(TaxYears[activeYear], 0, 1)}
maxDate={new Date(TaxYears[activeYear], 11, 31)}
/>
</Grid>
</FormListContainer>
)
const form: ReactElement = <>{hsaBlock}</>
return (
<FormProvider {...methods}>
<form
tabIndex={-1}
onSubmit={intentionallyFloat(handleSubmit(onAdvance))}
>
<Helmet>
<title>
Health Savings Accounts (HSA) | Savings Accounts | UsTaxes.org
</title>
</Helmet>
<h2>Health Savings Accounts (HSA)</h2>
{form}
{navButtons}
</form>
</FormProvider>
)
}
Example #19
Source File: NftForm.tsx From atlas with GNU General Public License v3.0 | 4 votes |
NftForm: React.FC<NftFormProps> = ({ setFormStatus, onSubmit, videoId }) => {
const { activeMembership } = useUser()
const scrollableWrapperRef = useRef<HTMLDivElement>(null)
const {
state: { activeInputs, setActiveInputs, listingType, setListingType, currentStep, previousStep, nextStep },
} = useNftForm()
const { chainState } = useNftFormUtils()
const { convertMsTimestampToBlock, convertBlocksToDuration } = useBlockTimeEstimation()
const isOnFirstStep = currentStep === 0
const isOnLastStep = currentStep === 2
const maxStartDate = addMilliseconds(new Date(), convertBlocksToDuration(chainState.nftAuctionStartsAtMaxDelta))
const maxEndDate = addMilliseconds(new Date(), convertBlocksToDuration(chainState.nftMaxAuctionDuration))
const formMethods = useForm<NftFormFields>({
mode: 'onChange',
resolver: (data, ctx, options) => {
const resolver = zodResolver(
createValidationSchema(data, maxStartDate, maxEndDate, listingType, chainState.nftMinStartingPrice)
)
return resolver(data, ctx, options)
},
reValidateMode: 'onChange',
defaultValues: {
startDate: null,
endDate: null,
startingPrice: chainState.nftMinStartingPrice || undefined,
},
})
const {
handleSubmit: createSubmitHandler,
reset,
getValues,
setValue,
watch,
trigger,
formState: { isValid },
} = formMethods
const { video, loading: loadingVideo } = useVideo(videoId, { fetchPolicy: 'cache-only' })
const { url: channelAvatarUrl } = useAsset(video?.channel.avatarPhoto)
const { url: thumbnailPhotoUrl } = useAsset(video?.thumbnailPhoto)
const { url: memberAvatarUri } = useMemberAvatar(activeMembership)
const [openModal, closeModal] = useConfirmationModal()
const handleSubmit = useCallback(() => {
const startDateValue = getValues('startDate')
const startDate = startDateValue?.type === 'date' && startDateValue.date
if (startDate && new Date() > startDate) {
trigger('startDate')
// the start date is in the past, abort the submit and show a modal
openModal({
title: 'Start sale now?',
children: (
<Text variant="t200" secondary>
The start date <Text variant="t200">{formatDateTime(startDate)} </Text> you selected has already passed. Do
you want to put your NFT on sale now?
</Text>
),
primaryButton: {
variant: 'primary',
size: 'large',
text: 'Start sale now',
onClick: () => {
setValue('startDate', null)
closeModal()
handleSubmit()
},
},
secondaryButton: {
variant: 'secondary',
size: 'large',
text: 'Cancel',
onClick: () => {
previousStep()
closeModal()
},
},
})
return
}
const handler = createSubmitHandler((data) => {
if (listingType === 'Fixed price') {
if (!data.buyNowPrice) {
SentryLogger.error('Missing buy now price for fixed price NFT', 'NftForm', null, {
form: { data, listingType },
})
return
}
onSubmit({
type: 'buyNow',
buyNowPrice: data.buyNowPrice,
})
} else if (listingType === 'Auction') {
const startsAtBlock = startDate ? convertMsTimestampToBlock(startDate.getTime()) : undefined
const startingPrice = data.startingPrice || chainState.nftMinStartingPrice
const minimalBidStep = Math.ceil(startingPrice * NFT_MIN_BID_STEP_MULTIPLIER)
if (data.auctionDurationBlocks) {
// auction has duration, assume english
onSubmit({
type: 'english',
startsAtBlock,
startingPrice,
minimalBidStep,
buyNowPrice: data.buyNowPrice || undefined,
auctionDurationBlocks: data.auctionDurationBlocks,
whitelistedMembersIds: data.whitelistedMembers?.map((member) => member.id),
})
} else {
// auction has no duration, assume open
onSubmit({
type: 'open',
startsAtBlock,
startingPrice,
minimalBidStep,
buyNowPrice: data.buyNowPrice || undefined,
whitelistedMembersIds: data.whitelistedMembers?.map((member) => member.id),
})
}
} else {
SentryLogger.error('Unknown listing type', 'NftForm', null, {
form: { data, listingType },
})
}
})
return handler()
}, [
chainState.nftMinStartingPrice,
closeModal,
convertMsTimestampToBlock,
createSubmitHandler,
getValues,
listingType,
onSubmit,
openModal,
previousStep,
setValue,
trigger,
])
const handleGoForward = useCallback(() => {
scrollableWrapperRef.current?.scrollIntoView()
if (isOnLastStep) return
nextStep()
}, [isOnLastStep, nextStep])
const handleGoBack = useCallback(() => {
if (isOnFirstStep) return
scrollableWrapperRef.current?.scrollIntoView()
previousStep()
}, [isOnFirstStep, previousStep])
const formDisabled = useMemo(() => {
if (currentStep === 0) {
return !listingType
}
if (currentStep === 1) {
return !isValid
}
return false
}, [currentStep, isValid, listingType])
const formStatus: NftFormStatus = useMemo(
() => ({
isValid,
isDisabled: formDisabled,
canGoBack: !isOnFirstStep,
canGoForward: !isOnLastStep,
triggerGoBack: handleGoBack,
triggerGoForward: handleGoForward,
triggerSubmit: handleSubmit,
}),
[isValid, formDisabled, isOnFirstStep, isOnLastStep, handleGoBack, handleGoForward, handleSubmit]
)
// sent updates on form status to VideoWorkspace
useEffect(() => {
setFormStatus(formStatus)
}, [formStatus, setFormStatus])
// Clear form on listing type change
useEffect(() => {
reset()
if (listingType === 'Fixed price') {
setTimeout(() => {
setValue('buyNowPrice', 1)
})
}
setActiveInputs([])
}, [listingType, reset, setActiveInputs, setValue])
const getNftStatus = () => {
switch (listingType) {
case 'Fixed price':
return 'buy-now'
case 'Auction':
return 'auction'
default:
return 'idle'
}
}
const nftTileProps: NftTileProps = {
status: getNftStatus(),
thumbnail: { thumbnailUrl: thumbnailPhotoUrl },
title: video?.title,
owner: { assetUrl: memberAvatarUri, name: activeMembership?.handle },
creator: { assetUrl: channelAvatarUrl, name: video?.channel.title ?? '' },
loading: loadingVideo,
duration: video?.duration,
views: video?.views,
buyNowPrice: watch('buyNowPrice') || 0,
startingPrice: watch('startingPrice') || 0,
}
const stepsContent = [
<ListingType key="step-content-1" selectedType={listingType} onSelectType={setListingType} />,
<SetUp
maxStartDate={maxStartDate}
maxEndDate={maxEndDate}
key="step-content-2"
selectedType={listingType}
activeInputs={activeInputs}
setActiveInputs={setActiveInputs}
handleGoForward={handleGoForward}
/>,
<AcceptTerms key="step-content-3" selectedType={listingType} formData={getValues()} />,
]
return (
<ScrollableWrapper ref={scrollableWrapperRef}>
<NftWorkspaceFormWrapper>
<NftPreview>
<NftTile interactable={false} {...nftTileProps} />
<Text margin={{ top: 4 }} variant="h100" secondary>
Your nft preview
</Text>
</NftPreview>
<NftFormScrolling>
<FormProvider {...formMethods}>
<NftFormWrapper lastStep={currentStep === 2}>
<StepperWrapper>
<StepperInnerWrapper>
{issueNftSteps.map((step, idx) => {
const stepVariant = getStepVariant(currentStep, idx)
const isLast = idx === issueNftSteps.length - 1
return (
<StepWrapper key={idx}>
<Step showOtherStepsOnMobile number={idx + 1} variant={stepVariant} title={step.title} />
{!isLast && <SvgActionChevronR />}
</StepWrapper>
)
})}
</StepperInnerWrapper>
</StepperWrapper>
{stepsContent[currentStep]}
</NftFormWrapper>
</FormProvider>
</NftFormScrolling>
</NftWorkspaceFormWrapper>
</ScrollableWrapper>
)
}
Example #20
Source File: CollectionFilter.tsx From firebase-tools-ui with Apache License 2.0 | 4 votes |
CollectionFilter: React.FC<
React.PropsWithChildren<{
className?: string;
path: string;
onClose?: () => void;
}>
> = ({ className, path, onClose }) => {
const collectionFilter = useCollectionFilter(path);
const dispatch = useDispatch();
const formMethods = useForm<CollectionFilterType>({
mode: 'onChange',
defaultValues: collectionFilter,
});
const cf = formMethods.watch();
const onSubmit = (data: CollectionFilterType) => {
dispatch(
actions.addCollectionFilter({
path,
...data,
})
);
onClose?.();
};
return (
<CollectionFilterTheme>
<FormProvider {...formMethods}>
<form
onSubmit={formMethods.handleSubmit(onSubmit)}
className={className}
>
{/* Field entry */}
<FilterItem title="Filter by field" preview={cf.field} defaultOpen>
<Controller
name="field"
rules={{ required: 'Required' }}
defaultValue=""
render={({ field: { ref, ...field } }) => (
<Field
label="Enter field"
{...field}
error={
formMethods.formState.touchedFields['field'] &&
formMethods.formState.errors['field']?.message
}
/>
)}
/>
</FilterItem>
{/* Condition entry */}
<FilterItem
title="Add condition"
preview={<ConditionPreview cf={cf} />}
defaultOpen
>
<ConditionSelect>
{cf && isSingleValueCollectionFilter(cf) && (
<ConditionEntry
name="value"
error={
(formMethods.formState.touchedFields as any)['value'] &&
(formMethods.formState.errors as any)['value']?.message
}
/>
)}
{cf && isMultiValueCollectionFilter(cf) && (
<ConditionEntries name="values" />
)}
</ConditionSelect>
</FilterItem>
{/* Sort entry */}
<FilterItem
title="Sort results"
preview={isSortableCollectionFilter(cf) && cf.sort}
defaultOpen
>
<SortRadioGroup
name="sort"
disabled={!isSortableCollectionFilter(cf)}
/>
</FilterItem>
<Preview path={path} cf={cf} />
<CardActions className={styles.actions}>
<CardActionButtons>
<CardActionButton
type="button"
onClick={() => {
dispatch(
actions.removeCollectionFilter({
path,
})
);
onClose?.();
}}
>
Clear
</CardActionButton>
</CardActionButtons>
<CardActionButtons>
<CardActionButton type="button" onClick={() => onClose?.()}>
Cancel
</CardActionButton>
<CardActionButton
unelevated
type="submit"
disabled={!formMethods.formState.isValid}
>
Apply
</CardActionButton>
</CardActionButtons>
</CardActions>
</form>
</FormProvider>
</CollectionFilterTheme>
);
}
Example #21
Source File: FilteredAssetsTable.tsx From UsTaxes with GNU Affero General Public License v3.0 | 4 votes |
FilteredAssetsTable = (): ReactElement => {
const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)')
const activeYear: TaxYear = useSelector(
(state: YearsTaxesState) => state.activeYear
)
const dispatch = useDispatch()
const assets = useSelector((state: YearsTaxesState) => state.assets)
const allAssets: WithIndex<Asset<Date>>[] = assets.map((a, i) => ({
...a,
idx: i
}))
const methods = useForm<AssetFilter>({
defaultValues: { ...blankFilter, closeYear: activeYear }
})
const closeYear = methods.watch('closeYear')
const securityName = methods.watch('securityName')
const yearFilter = (a: Asset<Date>): boolean =>
closeYear === 'all' ||
(a.closeDate === undefined && closeYear === 'none') ||
(a.closeDate !== undefined &&
closeYear !== 'none' &&
a.closeDate.getFullYear() === TaxYears[closeYear as TaxYear])
const securityFilter = (a: Asset<Date>): boolean =>
securityName === '' || a.name.toLowerCase() === securityName.toLowerCase()
const displayAssets = allAssets.filter(
(a) => yearFilter(a) && securityFilter(a)
)
const title = [
['Summary of'],
securityName !== '' ? [securityName] : [],
closeYear === 'all'
? ['all time']
: closeYear === 'none'
? ['current holdings']
: [`sales in ${TaxYears[closeYear as TaxYear]}`]
]
.flat()
.join(' ')
const filterForm = (() => {
// Show filter if there are any assets to filter from
if (assets.length > 0) {
return (
<FormProvider {...methods}>
<h3>Filter by</h3>
<Grid container direction="row" spacing={2}>
<LabeledInput
sizes={{ xs: 6 }}
label="Asset Name"
name="securityName"
/>
<GenericLabeledDropdown<[string, CloseYear], AssetFilter>
noUndefined
sizes={{ xs: 6 }}
label="Sale Year"
name="closeYear"
dropDownData={[
['All', 'all'],
['Still open', 'none'],
...enumKeys(TaxYears).map<[string, TaxYear]>((x) => [
TaxYears[x].toString(),
x
])
]}
keyMapping={(x) => x[1].toString()}
valueMapping={(x) => x[1]}
textMapping={(x) => x[0]}
/>
</Grid>
</FormProvider>
)
}
})()
const assetSummary = (() => {
if (assets.length > 0) {
return <AssetSummary title={title} assets={displayAssets} />
}
})()
const asCsv = (): string[] =>
[
[
'Security',
'Quantity',
'Open Date',
'Open Price',
'Open Fee',
'Cost basis',
'Close Date',
'Close Price',
'Close Fee',
'Proceeds',
'Gain / Loss'
],
...displayAssets.map((a) => [
a.name,
a.quantity,
a.openDate.toISOString().slice(0, 10),
a.openPrice,
a.openFee,
a.openPrice * a.quantity + a.openFee,
a.closeDate?.toISOString().slice(0, 10) ?? '',
a.closePrice,
a.closeFee,
a.closePrice === undefined
? ''
: a.closePrice * a.quantity - (a.closeFee ?? 0),
a.closePrice === undefined
? ''
: (a.closePrice - a.openPrice) * a.quantity -
(a.closeFee ?? 0) -
a.openFee
])
].map((line) => line.join(','))
const exportView = (() => {
if (displayAssets.length > 0) {
return (
<Grid item>
<Button
color={prefersDarkMode ? 'default' : 'secondary'}
variant="contained"
onClick={() => {
const csv = asCsv().join('\n')
const blob = new Blob([csv], { type: 'text/csv;charset=utf-8' })
const url = URL.createObjectURL(blob)
const link = document.createElement('a')
link.setAttribute('href', url)
link.setAttribute('download', 'assets.csv')
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
}}
>
Export {displayAssets.length} Assets
</Button>
</Grid>
)
}
})()
return (
<Grid container spacing={1} direction="column">
<Grid item>{filterForm}</Grid>
<Grid item>{assetSummary}</Grid>
{exportView}
<Grid item>
<DisplayAssets
assets={displayAssets}
deleteRows={(rows) =>
dispatch(actions.removeAssets(rows)(activeYear))
}
/>
</Grid>
</Grid>
)
}
Example #22
Source File: GuardianChangeProposalForm.tsx From homebase-app with MIT License | 4 votes |
GuardianChangeProposalForm: React.FC<Props> = ({ open, handleClose, defaultValues }) => {
const daoId = useDAOID();
const { data: dao } = useDAO(daoId);
const { data: daoHoldings } = useDAOHoldings(daoId);
const methods = useForm<Values>({
defaultValues: useMemo(
() => ({
newGuardianAddress: "",
...defaultValues,
}),
[defaultValues]
),
// resolver: yupResolver(validationSchema as any),
});
const newGuardianAddress = methods.watch("newGuardianAddress");
useEffect(() => {
methods.reset(defaultValues);
}, [defaultValues, methods]);
const { mutate } = useProposeGuardianChange();
const onSubmit = useCallback(
(values: Values) => {
if (dao) {
mutate({ dao, newGuardianAddress: values.newGuardianAddress });
handleClose();
}
},
[dao, handleClose, mutate]
);
return (
<FormProvider {...methods}>
<ResponsiveDialog
open={open}
onClose={handleClose}
aria-labelledby='alert-dialog-title'
aria-describedby='alert-dialog-description'
title={"Update Guardian"}>
<Content container direction={"column"} style={{ gap: 18 }}>
{dao && (
<Grid item>
<Typography variant={"body2"} color={"inherit"}>
Current Guardian: {dao.data.guardian}
</Typography>
</Grid>
)}
<Grid item>
<ProposalFormInput label={"New Guardian Address"}>
<Controller
control={methods.control}
name={`newGuardianAddress`}
render={({ field }) => (
<TextField
{...field}
type='text'
placeholder=' tz1...'
InputProps={{ disableUnderline: true }}
/>
)}
/>
</ProposalFormInput>
</Grid>
<Grid item>
<Typography align='left' variant='subtitle2' color='textPrimary' display={"inline"}>
Proposal Fee:{" "}
</Typography>
<Typography align='left' variant='subtitle2' color='secondary' display={"inline"}>
{dao && dao.data.extra.frozen_extra_value.toString()} {dao ? dao.data.token.symbol : ""}
</Typography>
</Grid>
<SendButton
onClick={methods.handleSubmit(onSubmit as any)}
disabled={!dao || !daoHoldings || !newGuardianAddress}>
Submit
</SendButton>
</Content>
</ResponsiveDialog>
</FormProvider>
);
}
Example #23
Source File: RealEstate.tsx From UsTaxes with GNU Affero General Public License v3.0 | 4 votes |
export default function RealEstate(): ReactElement {
const defaultValues = blankAddForm
const methods = useForm<PropertyAddForm>({ defaultValues })
const { handleSubmit, control, getValues } = methods
const dispatch = useDispatch()
const { onAdvance, navButtons } = usePager()
const activeYear: TaxYear = useSelector(
(state: YearsTaxesState) => state.activeYear
)
const properties: Property[] = useYearSelector(
(state) => state.information.realEstate
)
const propertyType = useWatch({
control,
name: 'propertyType'
})
const otherExpensesEntered: number | undefined = useWatch({
control,
name: 'expenses.other'
})
const validateDays = (n: number, other: number): Message | true => {
const days = daysInYear(TaxYears[activeYear])
return n + other <= days ? true : `Total use days must be less than ${days}`
}
const validatePersonal = (n: number): Message | true =>
validateDays(n, Number(getValues().rentalDays ?? 0))
const validateRental = (n: number): Message | true =>
validateDays(n, Number(getValues().personalUseDays ?? 0))
const deleteProperty = (n: number): void => {
dispatch(removeProperty(n))
}
const onAddProperty = (formData: PropertyAddForm): void => {
dispatch(addProperty(toProperty(formData)))
}
const onEditProperty =
(index: number) =>
(formData: PropertyAddForm): void => {
dispatch(editProperty({ value: toProperty(formData), index }))
}
const expenseFields: ReactElement[] = enumKeys(PropertyExpenseType).map(
(k, i) => (
<LabeledInput
key={i}
label={displayExpense(PropertyExpenseType[k])}
name={`expenses.${k.toString()}`}
patternConfig={Patterns.currency}
required={false}
/>
)
)
const otherExpenseDescription = (() => {
if ((otherExpensesEntered ?? 0) !== 0) {
return (
<LabeledInput
key={enumKeys(PropertyExpenseType).length}
label="Other description"
name="otherExpenseType"
required={true}
/>
)
}
})()
const form = (
<FormListContainer
defaultValues={defaultValues}
items={properties.map((a) => toUserInput(a))}
icon={() => <HouseOutlined />}
primary={(p) => toProperty(p).address.address}
secondary={(p) => <Currency value={toProperty(p).rentReceived} />}
onSubmitAdd={onAddProperty}
onSubmitEdit={onEditProperty}
removeItem={(i) => deleteProperty(i)}
>
<h3>Property Location</h3>
<Grid container spacing={2}>
<AddressFields
autofocus={true}
checkboxText="Does the property have a foreign address"
allowForeignCountry={false}
/>
<GenericLabeledDropdown
dropDownData={enumKeys(PropertyType)}
label="Property type"
textMapping={(t) => displayPropertyType(PropertyType[t])}
keyMapping={(_, n) => n}
name="propertyType"
valueMapping={(n) => n}
/>
{(() => {
if (propertyType === 'other') {
return (
<LabeledInput
name="otherPropertyType"
label="Short property type description"
required={true}
/>
)
}
})()}
</Grid>
<h3>Use</h3>
<Grid container spacing={2}>
<LabeledInput
name="rentalDays"
rules={{ validate: (n: string) => validateRental(Number(n)) }}
label="Number of days in the year used for rental"
patternConfig={Patterns.numDays(activeYear)}
/>
<LabeledInput
name="personalUseDays"
rules={{ validate: (n: string) => validatePersonal(Number(n)) }}
label="Number of days in the year for personal use"
patternConfig={Patterns.numDays(activeYear)}
/>
<LabeledCheckbox
name="qualifiedJointVenture"
label="Is this a qualified joint venture"
/>
</Grid>
<h3>Property Financials</h3>
<h4>Income</h4>
<Grid container spacing={2}>
<LabeledInput
name="rentReceived"
label="Rent received"
patternConfig={Patterns.currency}
/>
</Grid>
<h4>Expenses</h4>
<Grid container spacing={2}>
{_.chain([...expenseFields, otherExpenseDescription])
.chunk(2)
.map((segment, i) =>
segment.map((item, k) => (
<Grid item key={`${i}-${k}`} xs={12} sm={6}>
{item}
</Grid>
))
)
.value()}
</Grid>
</FormListContainer>
)
return (
<FormProvider {...methods}>
<form
tabIndex={-1}
onSubmit={intentionallyFloat(handleSubmit(onAdvance))}
>
<Helmet>
<title>Real Estate | Income | UsTaxes.org</title>
</Helmet>
<h2>Properties</h2>
{form}
{navButtons}
</form>
</FormProvider>
)
}
Example #24
Source File: ProposalForm.tsx From homebase-app with MIT License | 4 votes |
ProposalFormContainer: React.FC<Props> = ({
open,
handleClose,
defaultValues,
defaultTab,
}) => {
const daoId = useDAOID();
const {data: dao} = useDAO(daoId);
const {data: daoHoldings} = useDAOHoldings(daoId);
const [selectedTab, setSelectedTab] = useState(defaultTab || 0);
const methods = useForm<Values>({
defaultValues: useMemo(
() => ({
agoraPostId: "0",
...treasuryProposalFormInitialState,
...nftTransferFormInitialState,
...registryProposalFormInitialState,
...defaultValues,
}),
[defaultValues]
),
// resolver: yupResolver(validationSchema as any),
});
useEffect(() => {
methods.reset({
agoraPostId: "0",
...treasuryProposalFormInitialState,
...nftTransferFormInitialState,
...registryProposalFormInitialState,
...defaultValues,
});
}, [defaultValues, methods]);
const forms = enabledForms[dao?.data.type || "treasury"];
const {mutate: treasuryMutate} = useTreasuryPropose();
const {mutate: registryMutate} = useRegistryPropose();
const onSubmit = useCallback(
(values: Values) => {
const agoraPostId = Number(values.agoraPostId);
const mappedTransfers = [
...values.transferForm.transfers,
...values.nftTransferForm.transfers,
]
.filter(
(transfer) =>
!!transfer.amount && !!transfer.asset && !!transfer.recipient
)
.map((transfer) =>
(transfer.asset as Asset).symbol === "XTZ"
? {...transfer, amount: transfer.amount, type: "XTZ" as const}
: {
...transfer,
amount: transfer.amount,
asset: transfer.asset as Token,
type: "FA2" as const,
}
);
const mappedList = values.registryUpdateForm.list.filter(
(item) => !!item.key && !!item.value
);
if ((dao as BaseDAO).data.type === "treasury") {
treasuryMutate({
dao: dao as TreasuryDAO,
args: {
agoraPostId,
transfers: mappedTransfers,
},
});
} else if ((dao as BaseDAO).data.type === "registry") {
registryMutate({
dao: dao as RegistryDAO,
args: {
agoraPostId,
transfer_proposal: {
transfers: mappedTransfers,
registry_diff: mappedList,
},
},
});
}
methods.reset()
handleClose();
},
[dao, handleClose, methods, registryMutate, treasuryMutate]
);
return (
<FormProvider {...methods}>
<ProposalFormResponsiveDialog open={open} onClose={handleClose}>
{dao && daoHoldings && (
<>
<AppTabBar
value={selectedTab}
setValue={setSelectedTab}
labels={forms.map((form) => form.label)}
/>
{forms.map((form, i) => (
<TabPanel key={`tab-${i}`} value={selectedTab} index={i}>
<form.component/>
</TabPanel>
))}
<Content container direction={"column"} style={{gap: 18}}>
<Grid item>
<ProposalFormInput label={"Agora Post ID"}>
<Controller
control={methods.control}
name={`agoraPostId`}
render={({field}) => (
<TextField
{...field}
type="number"
placeholder="Type an Agora Post ID"
InputProps={{disableUnderline: true}}
/>
)}
/>
</ProposalFormInput>
</Grid>
<Grid item>
<Typography
align="left"
variant="subtitle2"
color="textPrimary"
display={"inline"}
>
Proposal Fee:{" "}
</Typography>
<Typography
align="left"
variant="subtitle2"
color="secondary"
display={"inline"}
>
{dao && dao.data.extra.frozen_extra_value.toString()}{" "}
{dao ? dao.data.token.symbol : ""}
</Typography>
</Grid>
<SendButton
onClick={methods.handleSubmit(onSubmit as any)}
disabled={!dao || !daoHoldings}
>
Submit
</SendButton>
</Content>
</>
)}
</ProposalFormResponsiveDialog>
</FormProvider>
);
}
Example #25
Source File: Questions.tsx From UsTaxes with GNU Affero General Public License v3.0 | 4 votes |
Questions = (): ReactElement => {
const information = useSelector((state: TaxesState) => state.information)
const stateAnswers: Responses = {
...emptyQuestions,
...information.questions
}
const methods = useForm<Responses>({ defaultValues: stateAnswers })
const {
handleSubmit,
getValues,
reset,
formState: { isDirty }
} = methods
const currentValues = getValues()
const { navButtons, onAdvance } = usePager()
const questions = getRequiredQuestions({
...information,
questions: {
...information.questions,
...currentValues
}
})
const currentAnswers: Responses = { ...emptyQuestions, ...currentValues }
// This form can be rerendered because the global state was modified by
// another control.
useEffect(() => {
if (!isDirty && !_.isEqual(currentAnswers, stateAnswers)) {
reset(stateAnswers)
}
}, [])
const dispatch = useDispatch()
const onSubmit = (responses: Responses): void => {
// fix to remove unrequired answers:
const qtags = questions.map((q) => q.tag)
const unrequired = Object.keys(responses).filter(
(rtag) => qtags.find((t) => t === (rtag as QuestionTagName)) === undefined
)
const newResponses = {
...responses,
...Object.fromEntries(unrequired.map((k) => [k, undefined]))
}
dispatch(answerQuestion(newResponses))
onAdvance()
}
const page = (
<form tabIndex={-1} onSubmit={intentionallyFloat(handleSubmit(onSubmit))}>
<Helmet>
<title>Informational Questions | Results | UsTaxes.org</title>
</Helmet>
<h2>Informational Questions</h2>
<p>
Based on your prior responses, responses to these questions are
required.
</p>
<Grid container spacing={2}>
<List>
{questions.map((q, i) => (
<ListItem key={i}>
{(() => {
if (q.valueTag === 'boolean') {
return <LabeledCheckbox name={q.tag} label={q.text} />
}
return <LabeledInput name={q.tag} label={q.text} />
})()}
</ListItem>
))}
</List>
</Grid>
{navButtons}
</form>
)
return <FormProvider {...methods}>{page}</FormProvider>
}
Example #26
Source File: APICUDPage.tsx From one-platform with MIT License | 4 votes |
APICUDPage = () => {
const [wizardStep, setWizardStep] = useState(1);
const { handleDynamicCrumbs } = useBreadcrumb();
const userInfo = opcBase.auth?.getUserInfo();
const navigate = useNavigate();
const { slug } = useParams();
const gqlClient = useURQL();
const isUpdate = Boolean(slug);
const [isDeleteConfirmationOpen, setIsDeleteConfirmationOpen] = useToggle();
// gql queries
const [createNsState, createNamespace] = useCreateNamespace();
const [, updateANamespace] = useUpdateNamespace();
const [deleteNamespaceState, deleteANamespace] = useDeleteANamespace();
const { isLoading: isNamespaceLoading, data: nsData } = useGetANamespaceBySlug({ slug });
const namespace = nsData?.getNamespaceBySlug;
const id = namespace?.id;
const formMethod = useForm<FormData>({
defaultValues: FORM_DEFAULT_VALUE,
mode: 'onBlur',
reValidateMode: 'onBlur',
resolver: yupResolver(
wizardValidationSchemas[wizardStep as keyof typeof wizardValidationSchemas]
),
});
const { handleSubmit, reset } = formMethod;
// effect for breadcrumb data
useEffect(() => {
if (!isNamespaceLoading && isUpdate && namespace?.name && namespace?.slug) {
handleDynamicCrumbs({
'api-name': { label: namespace.name, url: `/apis/${namespace.slug}` },
});
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isNamespaceLoading, namespace?.name, namespace?.slug, isUpdate]);
/**
* This effect validated whether user has edit access if not move them to explore
* Checks user is in owner list or in createdBy
*/
useEffect(() => {
if (!isNamespaceLoading && isUpdate && namespace) {
const userUuid = userInfo?.rhatUUID;
const isApiCreatedUser = userUuid === (namespace?.createdBy as UserRoverDetails)?.rhatUUID;
const isOwner =
namespace?.owners.findIndex(
(owner) => owner.group === ApiEmailGroup.USER && owner.user.rhatUUID === userUuid
) !== -1;
const hasEditAccess = isApiCreatedUser || isOwner;
if (!hasEditAccess) navigate('/apis');
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isNamespaceLoading, namespace, isUpdate, userInfo?.rhatUUID]);
/**
* In update mode the form is prefilled with API config data
*/
useEffect(() => {
if (!isNamespaceLoading && isUpdate && namespace) {
const owners = namespace.owners.map((owner) => ({
group: owner.group,
mid: owner.group === ApiEmailGroup.USER ? owner?.user?.rhatUUID : owner?.email,
email: owner.group === ApiEmailGroup.USER ? owner?.user?.mail : owner?.email,
}));
reset({
...namespace,
owners,
});
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isNamespaceLoading, namespace, isUpdate]);
const isLastStep = wizardStep === MAX_WIZARD_STEPS;
const formatFormData = ({ id: nsId, slug: nSlug, ...data }: FormData) => {
return {
...data,
owners: data.owners.map(({ group, mid }) => ({ group, mid })),
schemas: data.schemas.map((schema) => ({
...schema,
environments: schema.environments.map(({ slug: eSlug, ...env }) => ({
...env,
headers: (env?.headers || [])
.filter(({ id: hId, key, value }) => (hId && key) || (key && value))
.map(({ id: hID, key, value }) => (hID ? { id: hID, key } : { key, value })),
})),
})),
};
};
const handleCreateNamespace = async (data: FormData) => {
const payload = formatFormData(data) as CreateNamespaceType;
try {
const res = await createNamespace({ payload });
if (res.error) {
window.OpNotification.danger({
subject: 'Failed to create API',
body: res.error?.message,
});
return;
}
navigate(`/apis/${res.data?.createNamespace.slug}`);
} catch (error) {
window.OpNotification.danger({
subject: 'Failed to create API',
});
}
};
const handleUpdateNamespace = async (data: FormData) => {
const payload = formatFormData(data) as CreateNamespaceType;
delete (payload as any).createdBy;
try {
const res = await updateANamespace({ payload, id: id as string });
if (res.error) {
window.OpNotification.danger({
subject: 'Failed to update API',
body: res.error?.message,
});
return;
}
navigate(`/apis/${res.data?.updateNamespace.slug}`);
} catch (error) {
window.OpNotification.danger({
subject: 'Failed to update API',
});
}
};
const handleSchemaValidation = async ({ envSlug, ...config }: HandleSchemaValidationArg) => {
try {
const res = await gqlClient
.query<UseGetAPISchemaFileQuery, UseGetAPISchemaFileVariable>(GET_API_SCHEMA_FILE, {
config,
envSlug,
})
.toPromise();
return res.data?.fetchAPISchema;
} catch (error) {
window.OpNotification.danger({
subject: 'Failed to fetch schema',
});
}
return undefined;
};
const handleApiDelete = async (): Promise<void> => {
if (deleteNamespaceState.fetching) return;
const res = await deleteANamespace({ id: id as string });
if (res.error) {
window.OpNotification.danger({
subject: `Failed to delete API`,
body: res.error?.message,
});
} else {
navigate('/apis');
}
};
const onFormSubmit = (data: FormData) => {
if (wizardStep < MAX_WIZARD_STEPS) {
setWizardStep((state) => state + 1);
return;
}
if (isUpdate) {
handleUpdateNamespace(data);
} else {
handleCreateNamespace(data);
}
};
const onPrev = () => {
if (wizardStep > 1) {
setWizardStep((state) => state - 1);
}
};
const onSearchOwners = async (search: string): Promise<JSX.Element[]> => {
if (!search || search.length < 3) {
return [
<SelectOption key="no-result" value="Please type atleast 3 characters" isPlaceholder />,
];
}
try {
const res = await gqlClient.query<UserSearchQuery>(GET_USERS_QUERY, { search }).toPromise();
const options = (res.data?.searchRoverUsers || []).map((owner) => (
<SelectOption
key={`user:${owner.mail}-owner-${owner.rhatUUID}`}
value={{
...owner,
toString: () => owner.cn,
}}
description={owner.mail}
/>
));
return options;
} catch (error) {
window.OpNotification.danger({
subject: 'Failed to search for users',
});
}
return [];
};
if (isUpdate && isNamespaceLoading) {
return (
<Bullseye>
<Spinner size="xl" />
</Bullseye>
);
}
return (
<PageSection
isCenterAligned
isWidthLimited
style={{ backgroundColor: 'var(--pf-global--BackgroundColor--light-300)' }}
className="pf-u-h-100 pf-u-pb-4xl"
>
<Form
onSubmit={handleSubmit(onFormSubmit)}
style={{ maxWidth: '1080px', margin: 'auto' }}
autoComplete="off"
>
<FormProvider {...formMethod}>
<Stack hasGutter>
{/* Top Stepper */}
<StackItem>
<Card>
<CardBody>
<ProgressStepper isCenterAligned>
{wizardStepDetails.map(({ title }, index) => (
<ProgressStep
variant={wizardStep <= index ? 'pending' : 'success'}
id={`wizard-step-icon-${index}`}
key={`wizard-step-icon-${index + 1}`}
titleId={`wizard-step-icon-${index}`}
aria-label={title}
isCurrent={wizardStep === index + 1}
>
{title}
</ProgressStep>
))}
</ProgressStepper>
</CardBody>
</Card>
</StackItem>
{/* Form Steps */}
<StackItem>
<CSSTransition in={wizardStep === 1} timeout={200} classNames="fade-in" unmountOnExit>
<APIBasicDetailsForm onSearchOwners={onSearchOwners} />
</CSSTransition>
</StackItem>
<StackItem>
<CSSTransition in={wizardStep === 2} timeout={200} classNames="fade-in" unmountOnExit>
<APISchemaForm
handleSchemaValidation={handleSchemaValidation}
isUpdate={isUpdate}
/>
</CSSTransition>
</StackItem>
<StackItem>
<CSSTransition in={wizardStep === 3} timeout={200} classNames="fade-in" unmountOnExit>
<APIReview />
</CSSTransition>
</StackItem>
{/* Form Action Buttons */}
<StackItem>
<Card>
<CardBody>
<Split hasGutter>
<SplitItem>
<Button type="submit" isLoading={createNsState.fetching}>
{isLastStep ? (isUpdate ? 'Update' : 'Create') : 'Next'}
</Button>
</SplitItem>
<SplitItem>
<Button variant="secondary" onClick={onPrev} isDisabled={wizardStep === 1}>
Back
</Button>
</SplitItem>
<SplitItem isFilled>
<Link to={isUpdate ? `/apis/${namespace?.slug}` : '/apis'}>
<Button variant="link">Cancel</Button>
</Link>
</SplitItem>
{isUpdate && (
<SplitItem>
<Button variant="link" isDanger onClick={setIsDeleteConfirmationOpen.on}>
Delete
</Button>
</SplitItem>
)}
</Split>
</CardBody>
</Card>
</StackItem>
</Stack>
</FormProvider>
</Form>
<Modal
variant={ModalVariant.medium}
title={`Delete ${namespace?.name} API`}
titleIconVariant="danger"
isOpen={isDeleteConfirmationOpen}
onClose={setIsDeleteConfirmationOpen.off}
actions={[
<Button
key="confirm"
variant="primary"
onClick={handleApiDelete}
isLoading={deleteNamespaceState.fetching}
>
Confirm
</Button>,
<Button key="cancel" variant="link" onClick={setIsDeleteConfirmationOpen.off}>
Cancel
</Button>,
]}
>
This action is irreversible. Are you sure you want to delete this API?
</Modal>
</PageSection>
);
}
Example #27
Source File: product-form-context.tsx From admin with MIT License | 4 votes |
ProductFormProvider = ({
product = defaultProduct,
onSubmit,
children,
}) => {
const [viewType, setViewType] = React.useState<PRODUCT_VIEW>(
product.variants?.length > 0 ? VARIANTS_VIEW : SINGLE_PRODUCT_VIEW
)
const [images, setImages] = React.useState<any[]>([])
const [variants, setVariants] = React.useState<any[]>([])
const [productOptions, setProductOptions] = React.useState<any[]>([])
const [hasImagesChanged, setHasImagesChanged] = React.useState(false)
const appendImage = (image) => {
setHasImagesChanged(true)
setImages([...images, image])
}
const removeImage = (image) => {
setHasImagesChanged(true)
const tmp = images.filter((img) => img.url !== image.url)
setImages(tmp)
}
const methods = useForm()
const resetForm = () => {
methods.reset({
...product,
})
setHasImagesChanged(false)
setImages(product.images)
setProductOptions(product.options)
if (product?.variants) {
const variants = product?.variants?.map((v) => ({
...v,
options: v.options.map((o) => ({
...o,
title: product.options.find((po) => po.id === o.option_id)?.title,
})),
}))
setVariants(variants)
}
if (product?.options) {
const options = product?.options?.map((po) => ({
name: po.title,
values: po.values ? po.values.map((v) => v.value) : [],
}))
setProductOptions(options)
}
}
React.useEffect(() => {
resetForm()
}, [product])
const handleSubmit = (values) => {
onSubmit(
{ ...trimValues(values), images, variants, options: productOptions },
viewType
)
}
return (
<FormProvider {...methods}>
<ProductFormContext.Provider
value={{
productOptions,
setProductOptions,
variants,
setVariants,
images,
setImages,
appendImage,
removeImage,
setViewType,
viewType,
isVariantsView: viewType === VARIANTS_VIEW,
onSubmit: handleSubmit,
resetForm,
additionalDirtyState: {
images: hasImagesChanged,
},
}}
>
{children}
</ProductFormContext.Provider>
</FormProvider>
)
}
Example #28
Source File: ConfigProposalForm.tsx From homebase-app with MIT License | 4 votes |
ConfigProposalForm: React.FC<Props> = ({
open,
handleClose,
}) => {
const daoId = useDAOID();
const {data: dao} = useDAO(daoId);
const {data: daoHoldings} = useDAOHoldings(daoId);
const methods = useForm<Values>();
const {mutate} = useProposeConfigChange();
const onSubmit = useCallback(
(values: Values) => {
if (dao) {
console.log(values);
const mutateValues = {
frozen_extra_value: parseInt(values.frozen_extra_value),
slash_scale_value: !Number.isNaN(parseInt(values.returnedPercentage)) ?
100 - Number(values.returnedPercentage) : NaN
}
Object.entries(mutateValues).map(([key, value]) => {
if(Number.isNaN(value)) {
delete mutateValues[key as keyof typeof mutateValues];
}
});
console.log(mutateValues)
mutate({
dao, args: mutateValues
})
methods.reset()
handleClose();
}
},
[dao, handleClose, methods, mutate]
);
return (
<FormProvider {...methods}>
<ResponsiveDialog
open={open}
onClose={handleClose}
title={"Change DAO configuration"}
>
<Grid container direction={"column"} style={{gap: 18}}>
<Grid item>
<Typography variant={'body1'} color={"secondary"}>All fields are optional. Leave empty what you wish to leave unchanged</Typography>
</Grid>
<Grid item>
<ProposalFormInput label={`Proposal fee (Current: ${dao?.data.extra.frozen_extra_value.toString()})`}>
<Controller
control={methods.control}
name={`frozen_extra_value`}
render={({field}) => (
<TextField
{...field}
type="number"
placeholder="Proposal fee"
InputProps={{disableUnderline: true}}
/>
)}
/>
</ProposalFormInput>
</Grid>
<Grid item>
<ProposalFormInput label={`Percentage of tokens returned after rejection (Current: ${dao?.data.extra.returnedPercentage.toString()}%)`}>
<Controller
control={methods.control}
name={`returnedPercentage`}
render={({field}) => (
<TextField
{...field}
type="number"
placeholder="Returned tokens percentage"
InputProps={{disableUnderline: true}}
/>
)}
/>
</ProposalFormInput>
</Grid>
<Grid item>
<Typography align="left" variant="subtitle2" color="textPrimary" display={"inline"}>Proposal
Fee: </Typography>
<Typography
align="left" variant="subtitle2" color="secondary" display={"inline"}>
{dao && dao.data.extra.frozen_extra_value.toString()}{" "}
{dao ? dao.data.token.symbol : ""}
</Typography>
</Grid>
<SendButton
onClick={methods.handleSubmit(onSubmit as any)}
disabled={!dao || !daoHoldings}
>
Submit
</SendButton>
</Grid>
</ResponsiveDialog>
</FormProvider>
)
;
}