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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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>
  )
    ;
}