react-hook-form#useWatch TypeScript Examples

The following examples show how to use react-hook-form#useWatch. 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: FormAutosave.tsx    From firetable with Apache License 2.0 6 votes vote down vote up
export default function FormAutosave({ control, handleSave }: IAutosaveProps) {
  const values = useWatch({ control });

  const [debouncedValue] = useDebounce(values, 1000, {
    equalityFn: _isEqual,
  });

  useEffect(() => {
    handleSave(debouncedValue);
  }, [debouncedValue]);

  return null;
}
Example #2
Source File: useVariantFormEvents.ts    From calories-in with MIT License 6 votes vote down vote up
function useVariantFormEvents({ dietFormStatsTree }: Params) {
  const { reset } = useFormContext()
  const variantFormFieldId = useWatch({ name: 'variantFormFieldId' })

  function getVariantStatsForForFieldId(value: string) {
    if (value) {
      const stats = dietFormStatsTree.subtrees.find(({ id }) => id === value)

      if (!stats) {
        throw new Error()
      }

      return stats.stats
    }

    return dietFormStatsTree.avg as Stats
  }

  const variantStats = getVariantStatsForForFieldId(variantFormFieldId)

  function onVariantFormFieldIdChange(value: string) {
    const variantStats = getVariantStatsForForFieldId(value)
    const newVaraintsDetailsForm = getVariantsDetailsForm(value, variantStats)
    reset(newVaraintsDetailsForm)
  }

  return { onVariantFormFieldIdChange, variantStats }
}
Example #3
Source File: deploy-form.tsx    From sdk with MIT License 6 votes vote down vote up
export function DeployForm({ form }: IDeployFormProps) {
	const connection = useContext(ConnectorContext)
	const blockchain = useWatch({
		control: form.control,
		name: "blockchain",
	}) ?? connection.sdk?.wallet?.blockchain

	switch (blockchain) {
		case BlockchainGroup.ETHEREUM:
			return <EthereumDeployForm form={form}/>
		case BlockchainGroup.TEZOS:
			return <TezosDeployForm form={form}/>
		case BlockchainGroup.SOLANA:
			return <SolanaDeployForm form={form}/>
		default:
			return <Box sx={{my: 2}}>Deploy not available for selected blockchain</Box>
	}
}
Example #4
Source File: ShowFractional.tsx    From rabet-extension with GNU General Public License v3.0 6 votes vote down vote up
ShowFractional = ({
  control,
  isRotateActive,
}: ShowFractionalProps) => {
  const formValues = useWatch({ control });

  const values = {
    value1: formValues.from,
    asset1: formValues.asset1,
    value2: formValues.to,
    asset2: formValues.asset2,
  };

  if (isRotateActive) {
    values.value1 = formValues.to;
    values.asset1 = formValues.asset2;
    values.value2 = formValues.from;
    values.asset2 = formValues.asset1;
  }

  const denominator = new BN(values.value2)
    .div(new BN(values.value1))
    .toFixed(5);

  return (
    <span className="text-sm text-primary-dark">
      1 {values.asset1.asset_code || 'XLM'} = {denominator.toString()}{' '}
      {values.asset2.asset_code || 'XLM'}
    </span>
  );
}
Example #5
Source File: FormContainer.stories.tsx    From react-hook-form-mui with MIT License 6 votes vote down vote up
SubComponent = () => {
  const [name, email] = useWatch({
    name: ['name', 'email']
  })
  console.log(name, email)
  return (
    <>
      <Button type={'submit'} color={'primary'} disabled={!(name && email)}>Submit</Button>
    </>
  )
}
Example #6
Source File: SideDrawerField.tsx    From firetable with Apache License 2.0 6 votes vote down vote up
export default function SubTable({
  column,
  control,
  docRef,
}: ISideDrawerFieldProps) {
  const fieldClasses = useFieldStyles();

  const row = useWatch({ control });
  const { documentCount, label, subTablePath } = useSubTableData(
    column,
    row,
    docRef
  );

  return (
    <Grid container wrap="nowrap">
      <div className={fieldClasses.root}>
        {documentCount} {column.name}: {label}
      </div>

      <IconButton
        component={Link}
        to={subTablePath}
        style={{ width: 56, marginLeft: 16 }}
        disabled={!subTablePath}
      >
        <LaunchIcon />
      </IconButton>
    </Grid>
  );
}
Example #7
Source File: PasswordRepeatElement.tsx    From react-hook-form-mui with MIT License 6 votes vote down vote up
export default function PasswordRepeatElement(props: PasswordRepeatElementProps) {
  const pwValue = useWatch({
    name: props.passwordFieldName,
    control: props.control,
    defaultValue: ''
  })
  return (
    <PasswordElement {...props}
                     validation={{
                       validate: (value: string) => {
                         return value === pwValue || 'Password should match'
                       }
                     }}
    />
  )
}
Example #8
Source File: Autosave.tsx    From firetable with Apache License 2.0 5 votes vote down vote up
export default function Autosave({
  control,
  docRef,
  row,
  reset,
  dirtyFields,
}: IAutosaveProps) {
  const { tableState, updateCell } = useFiretableContext();

  const values = useWatch({ control });
  const [debouncedValue] = useDebounce(getEditables(values, tableState), 1000, {
    equalityFn: _isEqual,
  });

  useEffect(() => {
    if (!row || !row.ref) return;
    if (row.ref.id !== docRef.id) return;
    if (!updateCell) return;

    // Get only fields that have had their value updated by the user
    const updatedValues = _pickBy(
      _pickBy(debouncedValue, (_, key) => dirtyFields[key]),
      (value, key) => !_isEqual(value, row[key])
    );
    console.log(debouncedValue, row);
    console.log(updatedValues, dirtyFields);
    if (Object.keys(updatedValues).length === 0) return;

    // Update the document
    Object.entries(updatedValues).forEach(([key, value]) =>
      updateCell(
        row.ref,
        key,
        value,
        // After the cell is updated, set this field to be not dirty
        // so it doesn’t get updated again when a different field in the form
        // is updated + make sure the new value is kept after reset
        () => reset({ ...values, [key]: value })
      )
    );
  }, [debouncedValue]);

  return null;
}
Example #9
Source File: Address.tsx    From UsTaxes with GNU Affero General Public License v3.0 5 votes vote down vote up
export default function AddressFields(props: AddressProps): ReactElement {
  const {
    autofocus,
    checkboxText = 'Check if you have a foreign address',
    allowForeignCountry = true
  } = props

  const { control } = useFormContext<{ isForeignCountry: boolean }>()

  const isForeignCountry = useWatch({
    name: 'isForeignCountry',
    control
  })

  const csz: ReactElement = (() => {
    if (!allowForeignCountry || !isForeignCountry) {
      return (
        <>
          <USStateDropDown
            label="State"
            name="address.state"
            required={!isForeignCountry}
          />
          <LabeledInput
            label="Zip"
            name="address.zip"
            patternConfig={Patterns.zip}
            required={!isForeignCountry}
          />
        </>
      )
    }
    return (
      <>
        <LabeledInput
          label="Province"
          name="address.province"
          required={isForeignCountry}
        />
        <LabeledInput
          name="address.postalCode"
          label="Postal Code"
          required={isForeignCountry}
        />
        <CountryDropDown
          name="address.foreignCountry"
          label="Country"
          required={isForeignCountry}
        />
      </>
    )
  })()

  return (
    <>
      <LabeledInput
        autofocus={autofocus}
        label="Address"
        name="address.address"
        required={true}
      />
      <LabeledInput label="Unit No" name="address.aptNo" required={false} />
      <LabeledInput
        label="City"
        name="address.city"
        patternConfig={Patterns.name}
      />
      {(() => {
        if (allowForeignCountry) {
          return (
            <LabeledCheckbox label={checkboxText} name="isForeignCountry" />
          )
        }
      })()}
      {csz}
    </>
  )
}
Example #10
Source File: SideDrawerField.tsx    From firetable with Apache License 2.0 5 votes vote down vote up
export default function Action({
  column,
  control,
  docRef,
  disabled,
}: ISideDrawerFieldProps) {
  const classes = useStyles();
  const fieldClasses = useFieldStyles();

  const row = useWatch({ control });

  return (
    <Controller
      control={control}
      name={column.key}
      render={({ onChange, value }) => {
        const hasRan = value && value.status;

        return (
          <Grid container alignItems="center" wrap="nowrap" spacing={2}>
            <Grid item xs className={classes.labelGridItem}>
              <div className={fieldClasses.root}>
                <Typography variant="body1" className={classes.label}>
                  {hasRan && isUrl(value.status) ? (
                    <Link
                      href={value.status}
                      target="_blank"
                      rel="noopener noreferrer"
                      variant="body2"
                      underline="always"
                    >
                      {value.status}
                    </Link>
                  ) : hasRan ? (
                    value.status
                  ) : (
                    sanitiseCallableName(column.key)
                  )}
                </Typography>
              </div>
            </Grid>

            <Grid item>
              <ActionFab
                row={{ ...row, ref: docRef }}
                column={{ config: column.config, key: column.key }}
                onSubmit={onChange}
                value={value}
                disabled={disabled}
              />
            </Grid>
          </Grid>
        );
      }}
    />
  );
}
Example #11
Source File: SideDrawerField.tsx    From firetable with Apache License 2.0 5 votes vote down vote up
export default function Percentage({
  control,
  column,
  disabled,
}: ISideDrawerFieldProps) {
  const value: number | undefined = useWatch({ control, name: column.key });
  const classes = useStyles({ value });

  return (
    <Controller
      control={control}
      name={column.key}
      render={({ onChange, onBlur, value }) => {
        const handleChange = (e) => onChange(Number(e.target.value) / 100);

        return (
          <TextField
            variant="filled"
            fullWidth
            margin="none"
            placeholder={column.name}
            onChange={handleChange}
            onBlur={onBlur}
            value={typeof value === "number" ? value * 100 : value}
            id={`sidedrawer-field-${column.key}`}
            label=""
            hiddenLabel
            disabled={disabled}
            type="number"
            InputProps={{
              endAdornment: "%",
              classes: {
                root: classes.resultColor,
                underline: classes.underline,
              },
            }}
          />
        );
      }}
    />
  );
}
Example #12
Source File: StoreForward.tsx    From meshtastic-web with GNU General Public License v3.0 5 votes vote down vote up
StoreForwardSettingsPanel = (): JSX.Element => {
  const [loading, setLoading] = useState(false);

  const storeForwardConfig = useAppSelector(
    (state) => state.meshtastic.radio.moduleConfig.storeForward,
  );

  const { register, handleSubmit, formState, reset, control } =
    useForm<Protobuf.ModuleConfig_StoreForwardConfig>({
      defaultValues: storeForwardConfig,
    });

  useEffect(() => {
    reset(storeForwardConfig);
  }, [reset, storeForwardConfig]);

  const onSubmit = handleSubmit(async (data) => {
    setLoading(true);
    await connection.setModuleConfig(
      {
        payloadVariant: {
          oneofKind: 'storeForward',
          storeForward: data,
        },
      },
      async (): Promise<void> => {
        reset({ ...data });
        setLoading(false);
        await Promise.resolve();
      },
    );
  });

  const moduleEnabled = useWatch({
    control,
    name: 'enabled',
    defaultValue: false,
  });

  return (
    <Form loading={loading} dirty={!formState.isDirty} submit={onSubmit}>
      <Checkbox label="Module Enabled" {...register('enabled')} />
      <Checkbox
        label="Heartbeat Enabled"
        disabled={!moduleEnabled}
        {...register('heartbeat')}
      />
      <Input
        type="number"
        label="Number of records"
        suffix="Records"
        disabled={!moduleEnabled}
        {...register('records', {
          valueAsNumber: true,
        })}
      />
      <Input
        type="number"
        label="History return max"
        disabled={!moduleEnabled}
        {...register('historyReturnMax', {
          valueAsNumber: true,
        })}
      />
      <Input
        type="number"
        label="History return window"
        disabled={!moduleEnabled}
        {...register('historyReturnWindow', {
          valueAsNumber: true,
        })}
      />
    </Form>
  );
}
Example #13
Source File: RangeTest.tsx    From meshtastic-web with GNU General Public License v3.0 5 votes vote down vote up
RangeTestSettingsPanel = (): JSX.Element => {
  const [loading, setLoading] = useState(false);

  const rangeTestConfig = useAppSelector(
    (state) => state.meshtastic.radio.moduleConfig.rangeTest,
  );

  const { register, handleSubmit, formState, reset, control } =
    useForm<Protobuf.ModuleConfig_RangeTestConfig>({
      defaultValues: rangeTestConfig,
    });

  useEffect(() => {
    reset(rangeTestConfig);
  }, [reset, rangeTestConfig]);

  const onSubmit = handleSubmit(async (data) => {
    setLoading(true);
    await connection.setModuleConfig(
      {
        payloadVariant: {
          oneofKind: 'rangeTest',
          rangeTest: data,
        },
      },
      async (): Promise<void> => {
        reset({ ...data });
        setLoading(false);
        await Promise.resolve();
      },
    );
  });

  const moduleEnabled = useWatch({
    control,
    name: 'enabled',
    defaultValue: false,
  });

  return (
    <Form loading={loading} dirty={!formState.isDirty} submit={onSubmit}>
      <Checkbox label="Module Enabled" {...register('enabled')} />
      <Input
        type="number"
        label="Message Interval"
        disabled={!moduleEnabled}
        suffix="Seconds"
        {...register('sender', {
          valueAsNumber: true,
        })}
      />
      <Checkbox
        label="Save CSV to storage"
        disabled={!moduleEnabled}
        {...register('save')}
      />
    </Form>
  );
}
Example #14
Source File: MQTT.tsx    From meshtastic-web with GNU General Public License v3.0 5 votes vote down vote up
MQTT = (): JSX.Element => {
  const mqttConfig = useAppSelector(
    (state) => state.meshtastic.radio.moduleConfig.mqtt,
  );
  const [loading, setLoading] = useState(false);
  const { register, handleSubmit, formState, reset, control } =
    useForm<Protobuf.ModuleConfig_MQTTConfig>({
      defaultValues: mqttConfig,
    });

  const moduleEnabled = useWatch({
    control,
    name: 'disabled',
    defaultValue: false,
  });

  useEffect(() => {
    reset(mqttConfig);
  }, [reset, mqttConfig]);

  const onSubmit = handleSubmit((data) => {
    setLoading(true);
    void connection.setModuleConfig(
      {
        payloadVariant: {
          oneofKind: 'mqtt',
          mqtt: data,
        },
      },
      async () => {
        reset({ ...data });
        setLoading(false);
        await Promise.resolve();
      },
    );
  });
  return (
    <Form loading={loading} dirty={!formState.isDirty} submit={onSubmit}>
      <Checkbox label="Module Disabled" {...register('disabled')} />
      <Input
        label="MQTT Server Address"
        disabled={moduleEnabled}
        {...register('address')}
      />
      <Input
        label="MQTT Username"
        disabled={moduleEnabled}
        {...register('username')}
      />
      <Input
        label="MQTT Password"
        type="password"
        autoComplete="off"
        disabled={moduleEnabled}
        {...register('password')}
      />
      <Checkbox
        label="Encryption Enabled"
        disabled={moduleEnabled}
        {...register('encryptionEnabled')}
      />
    </Form>
  );
}
Example #15
Source File: ExternalNotifications.tsx    From meshtastic-web with GNU General Public License v3.0 5 votes vote down vote up
ExternalNotificationsSettingsPlanel = (): JSX.Element => {
  const [loading, setLoading] = useState(false);

  const extNotificationConfig = useAppSelector(
    (state) => state.meshtastic.radio.moduleConfig.extNotification,
  );

  const { register, handleSubmit, formState, reset, control } =
    useForm<Protobuf.ModuleConfig_ExternalNotificationConfig>({
      defaultValues: extNotificationConfig,
    });

  useEffect(() => {
    reset(extNotificationConfig);
  }, [reset, extNotificationConfig]);

  const onSubmit = handleSubmit(async (data) => {
    setLoading(true);
    await connection.setModuleConfig(
      {
        payloadVariant: {
          oneofKind: 'externalNotification',
          externalNotification: data,
        },
      },
      async (): Promise<void> => {
        reset({ ...data });
        setLoading(false);
        await Promise.resolve();
      },
    );
  });

  const moduleEnabled = useWatch({
    control,
    name: 'enabled',
    defaultValue: false,
  });

  return (
    <Form loading={loading} dirty={!formState.isDirty} submit={onSubmit}>
      <Checkbox label="Module Enabled" {...register('enabled')} />
      <Input
        type="number"
        label="Output MS"
        suffix="ms"
        disabled={!moduleEnabled}
        {...register('outputMs', {
          valueAsNumber: true,
        })}
      />
      <Input
        type="number"
        label="Output"
        disabled={!moduleEnabled}
        {...register('output', {
          valueAsNumber: true,
        })}
      />
      <Checkbox
        label="Active"
        disabled={!moduleEnabled}
        {...register('active')}
      />
      <Checkbox
        label="Message"
        disabled={!moduleEnabled}
        {...register('alertMessage')}
      />
      <Checkbox
        label="Bell"
        disabled={!moduleEnabled}
        {...register('alertBell')}
      />
    </Form>
  );
}
Example #16
Source File: WiFi.tsx    From meshtastic-web with GNU General Public License v3.0 5 votes vote down vote up
WiFi = (): JSX.Element => {
  const wifiConfig = useAppSelector(
    (state) => state.meshtastic.radio.config.wifi,
  );
  const [loading, setLoading] = useState(false);
  const { register, handleSubmit, formState, reset, control } =
    useForm<Protobuf.Config_WiFiConfig>({
      defaultValues: wifiConfig,
    });

  const WifiApMode = useWatch({
    control,
    name: 'apMode',
    defaultValue: false,
  });

  useEffect(() => {
    reset(wifiConfig);
  }, [reset, wifiConfig]);

  const onSubmit = handleSubmit((data) => {
    setLoading(true);
    void connection.setConfig(
      {
        payloadVariant: {
          oneofKind: 'wifi',
          wifi: data,
        },
      },
      async () => {
        reset({ ...data });
        setLoading(false);
        await Promise.resolve();
      },
    );
  });
  return (
    <Form loading={loading} dirty={!formState.isDirty} submit={onSubmit}>
      <Checkbox label="Enable WiFi AP" {...register('apMode')} />
      <Input label="WiFi SSID" disabled={WifiApMode} {...register('ssid')} />
      <Input
        type="password"
        autoComplete="off"
        label="WiFi PSK"
        disabled={WifiApMode}
        {...register('psk')}
      />
    </Form>
  );
}
Example #17
Source File: HTTP.tsx    From meshtastic-web with GNU General Public License v3.0 5 votes vote down vote up
HTTP = ({ connecting }: HTTPProps): JSX.Element => {
  const dispatch = useAppDispatch();

  const { register, handleSubmit, control } = useForm<{
    ipSource: 'local' | 'remote';
    ip?: string;
    tls: boolean;
  }>({
    defaultValues: {
      ipSource: 'local',
      ip: connectionUrl,
      tls: false,
    },
  });

  const watchIpSource = useWatch({
    control,
    name: 'ipSource',
    defaultValue: 'local',
  });

  const onSubmit = handleSubmit(async (data) => {
    if (data.ip) {
      localStorage.setItem('connectionUrl', data.ip);
    }
    dispatch(
      setConnectionParams({
        type: connType.HTTP,
        params: {
          address: data.ip ?? connectionUrl,
          tls: data.tls,
          fetchInterval: 2000,
        },
      }),
    );
    await setConnection(connType.HTTP);
  });

  return (
    <form onSubmit={onSubmit}>
      <Select
        label="Host Source"
        options={[
          {
            name: 'Local',
            value: 'local',
          },
          {
            name: 'Remote',
            value: 'remote',
          },
        ]}
        disabled={connecting}
        {...register('ipSource')}
      />
      {watchIpSource === 'local' ? (
        <Input label="Host" value={connectionUrl} disabled />
      ) : (
        <Input label="Host" disabled={connecting} {...register('ip')} />
      )}
      <Checkbox label="Use TLS?" disabled={connecting} {...register('tls')} />
      <Button
        className="mt-2 ml-auto"
        onClick={async (): Promise<void> => {
          if (connecting) {
            await connection.disconnect();
          } else {
            await onSubmit();
          }
        }}
        border
      >
        {connecting ? 'Disconnect' : 'Connect'}
      </Button>
    </form>
  );
}
Example #18
Source File: usePricesFieldArray.ts    From admin with MIT License 5 votes vote down vote up
usePricesFieldArray = <TKeyName extends string = "id">(
  currencyCodes: string[],
  { control, name, keyName }: UseFieldArrayOptions<TKeyName>,
  options: UsePricesFieldArrayOptions = {
    defaultAmount: 1000,
    defaultCurrencyCode: "usd",
  }
) => {
  const { defaultAmount, defaultCurrencyCode } = options
  const { fields, append, remove } = useFieldArray<PriceFormValue, TKeyName>({
    control,
    name,
    keyName,
  })
  const watchedFields = useWatch({
    control,
    name,
    defaultValue: fields,
  })

  const selectedCurrencies = watchedFields.map(
    (field) => field?.price?.currency_code
  )
  const availableCurrencies = currencyCodes?.filter(
    (currency) => !selectedCurrencies.includes(currency)
  )

  const controlledFields = fields.map((field, index) => {
    return {
      ...field,
      ...watchedFields[index],
    }
  })

  const appendPrice = () => {
    let newCurrency = availableCurrencies[0]
    if (!selectedCurrencies.includes(defaultCurrencyCode)) {
      newCurrency = defaultCurrencyCode
    }
    append({
      price: { currency_code: newCurrency, amount: defaultAmount },
    })
  }

  const deletePrice = (index) => {
    return () => {
      remove(index)
    }
  }

  return {
    fields: controlledFields,
    appendPrice,
    deletePrice,
    availableCurrencies,
    selectedCurrencies,
  } as const
}
Example #19
Source File: promotion-type.tsx    From admin with MIT License 5 votes vote down vote up
PromotionType = () => {
  const { control } = useDiscountForm()

  const regions = useWatch({
    control,
    name: "regions",
  })

  return (
    <Controller
      name="rule.type"
      control={control}
      rules={{ required: true }}
      render={({ onChange, value }) => {
        return (
          <RadioGroup.Root
            value={value}
            onValueChange={onChange}
            className={clsx("flex items-center gap-base mt-base px-1")}
          >
            <RadioGroup.Item
              value={DiscountRuleType.PERCENTAGE}
              className="flex-1"
              label="Percentage"
              description={"Discount applied in %"}
            />
            <RadioGroup.Item
              value={DiscountRuleType.FIXED}
              className="flex-1"
              label="Fixed amount"
              description={"Discount in whole numbers"}
              disabled={Array.isArray(regions) && regions.length > 1}
              disabledTooltip="You can only select one valid region if you want to use the fixed amount type"
            />
            <RadioGroup.Item
              value={DiscountRuleType.FREE_SHIPPING}
              className="flex-1"
              label="Free shipping"
              description={"Override delivery amount"}
            />
          </RadioGroup.Root>
        )
      }}
    />
  )
}
Example #20
Source File: index.tsx    From admin with MIT License 5 votes vote down vote up
ControlledCheckbox = ({
  control,
  name,
  id,
  index,
  value,
  ...props
}: ControlledCheckboxProps) => {
  const variants = useWatch<string[]>({
    control,
    name,
  })

  return (
    <Controller
      control={control}
      name={name}
      render={(field) => {
        return (
          <Checkbox
            className="shrink-0 inter-small-regular"
            {...props}
            {...field}
            checked={variants?.some((variant) => variant === value)}
            onChange={(e) => {
              // copy field value
              const valueCopy = [...(variants || [])] as any[]

              // update checkbox value
              valueCopy[index] = e.target.checked ? id : null

              // update field value
              field.onChange(valueCopy)
            }}
          />
        )
      }}
    />
  )
}
Example #21
Source File: CannedMessage.tsx    From meshtastic-web with GNU General Public License v3.0 4 votes vote down vote up
CannedMessage = (): JSX.Element => {
  const cannedMessageConfig = useAppSelector(
    (state) => state.meshtastic.radio.moduleConfig.cannedMessage,
  );
  const [loading, setLoading] = useState(false);
  const { register, handleSubmit, formState, reset, control } =
    useForm<Protobuf.ModuleConfig_CannedMessageConfig>({
      defaultValues: cannedMessageConfig,
    });

  const moduleEnabled = useWatch({
    control,
    name: 'rotary1Enabled',
    defaultValue: false,
  });

  useEffect(() => {
    reset(cannedMessageConfig);
  }, [reset, cannedMessageConfig]);

  const onSubmit = handleSubmit((data) => {
    setLoading(true);
    void connection.setModuleConfig(
      {
        payloadVariant: {
          oneofKind: 'cannedMessage',
          cannedMessage: data,
        },
      },
      async () => {
        reset({ ...data });
        setLoading(false);
        await Promise.resolve();
      },
    );
  });
  return (
    <Form loading={loading} dirty={!formState.isDirty} submit={onSubmit}>
      <Checkbox label="Module Enabled" {...register('enabled')} />
      <Checkbox
        label="Rotary Encoder #1 Enabled"
        {...register('rotary1Enabled')}
      />
      <Input
        label="Encoder Pin A"
        type="number"
        disabled={moduleEnabled}
        {...register('inputbrokerPinA', { valueAsNumber: true })}
      />
      <Input
        label="Encoder Pin B"
        type="number"
        disabled={moduleEnabled}
        {...register('inputbrokerPinB', { valueAsNumber: true })}
      />
      <Input
        label="Endoer Pin Press"
        type="number"
        disabled={moduleEnabled}
        {...register('inputbrokerPinPress', { valueAsNumber: true })}
      />
      <Select
        label="Clockwise event"
        disabled={moduleEnabled}
        optionsEnum={Protobuf.ModuleConfig_CannedMessageConfig_InputEventChar}
        {...register('inputbrokerEventCw', { valueAsNumber: true })}
      />
      <Select
        label="Counter Clockwise event"
        disabled={moduleEnabled}
        optionsEnum={Protobuf.ModuleConfig_CannedMessageConfig_InputEventChar}
        {...register('inputbrokerEventCcw', { valueAsNumber: true })}
      />
      <Select
        label="Press event"
        disabled={moduleEnabled}
        optionsEnum={Protobuf.ModuleConfig_CannedMessageConfig_InputEventChar}
        {...register('inputbrokerEventPress', { valueAsNumber: true })}
      />
      <Checkbox label="Up Down enabled" {...register('updown1Enabled')} />
      <Input
        label="Allow Input Source"
        disabled={moduleEnabled}
        {...register('allowInputSource')}
      />
      <Checkbox label="Send Bell" {...register('sendBell')} />
    </Form>
  );
}
Example #22
Source File: pricing-form-context.tsx    From admin with MIT License 4 votes vote down vote up
PriceListFormProvider: React.FC<FormProviderProps> = ({
  priceList = defaultState,
  children,
}) => {
  const [configFields, setConfigFields] = useState<
    Record<ConfigurationField, unknown>
  >({
    customer_groups: priceList.customer_groups,
    ends_at: priceList.ends_at,
    starts_at: priceList.starts_at,
  })
  const methods = useForm<PriceListFormValues>({
    defaultValues: priceList,
  })

  const [prices, setPrices] = useState<CreatePriceListPricesFormValues | null>(
    null
  )

  const currentStartsAt = useWatch({
    name: "starts_at",
    control: methods.control,
  })
  const currentEndsAt = useWatch({
    name: "ends_at",
    control: methods.control,
  })
  const currentCustomerGroups = useWatch({
    name: "customer_groups",
    control: methods.control,
  })

  const disableConfiguration = (configField: ConfigurationField) => {
    let configToSave: unknown | null = null

    switch (configField) {
      case ConfigurationField.CUSTOMER_GROUPS:
        configToSave = currentCustomerGroups
        break
      case ConfigurationField.STARTS_AT:
        configToSave = currentStartsAt
        break
      case ConfigurationField.ENDS_AT:
        configToSave = currentEndsAt
        break
    }

    // we save the configuration field value to the state, so that if the user re-enables the field we can populate it with the previous value
    setConfigFields({
      ...configFields,
      [configField]: configToSave,
    })

    // use timeout to avoid flashing default value
    setTimeout(() => methods.setValue(configField, null), 300)
  }

  const enableConfiguration = (configField: ConfigurationField) => {
    // we get the configuration field value from the state, so that if the user re-enables the field we can populate it with the previous value
    if (configFields[configField]) {
      methods.setValue(configField, configFields[configField])
    } else {
      // if the configuration field value is null, we set a default value
      switch (configField) {
        case ConfigurationField.STARTS_AT:
          methods.setValue(configField, new Date())
          break
        case ConfigurationField.ENDS_AT:
          methods.setValue(configField, weekFromNow())
          break
        case ConfigurationField.CUSTOMER_GROUPS:
          methods.setValue(configField, [])
          break
      }
    }
  }

  const handleConfigurationSwitch = (values: string[]) => {
    for (const key of Object.keys(configFields)) {
      if (values.includes(key)) {
        enableConfiguration(key as ConfigurationField)
      } else {
        disableConfiguration(key as ConfigurationField)
      }
    }
  }

  const handleSubmit = (submitHandler) => {
    return methods.handleSubmit((values) => {
      submitHandler({ ...values, prices })
    })
  }

  return (
    <FormProvider {...methods}>
      <PriceListFormContext.Provider
        value={{
          configFields,
          handleConfigurationSwitch,
          prices,
          handleSubmit,
          setPrices,
        }}
      >
        {children}
      </PriceListFormContext.Provider>
    </FormProvider>
  )
}
Example #23
Source File: Serial.tsx    From meshtastic-web with GNU General Public License v3.0 4 votes vote down vote up
SerialSettingsPanel = (): JSX.Element => {
  const [loading, setLoading] = useState(false);

  const serialConfig = useAppSelector(
    (state) => state.meshtastic.radio.moduleConfig.serial,
  );

  const { register, handleSubmit, formState, reset, control } =
    useForm<Protobuf.ModuleConfig_SerialConfig>({
      defaultValues: serialConfig,
    });

  useEffect(() => {
    reset(serialConfig);
  }, [reset, serialConfig]);

  const onSubmit = handleSubmit(async (data) => {
    setLoading(true);
    await connection.setModuleConfig(
      {
        payloadVariant: {
          oneofKind: 'serial',
          serial: data,
        },
      },
      async (): Promise<void> => {
        reset({ ...data });
        setLoading(false);
        await Promise.resolve();
      },
    );
  });

  const moduleEnabled = useWatch({
    control,
    name: 'enabled',
    defaultValue: false,
  });

  return (
    <Form loading={loading} dirty={!formState.isDirty} submit={onSubmit}>
      <Checkbox label="Module Enabled" {...register('enabled')} />
      <Checkbox label="Echo" disabled={!moduleEnabled} {...register('echo')} />

      <Input
        type="number"
        label="RX"
        disabled={!moduleEnabled}
        {...register('rxd', {
          valueAsNumber: true,
        })}
      />
      <Input
        type="number"
        label="TX Pin"
        disabled={!moduleEnabled}
        {...register('txd', {
          valueAsNumber: true,
        })}
      />
      <Input
        type="number"
        label="Baud Rate"
        disabled={!moduleEnabled}
        {...register('baud', {
          valueAsNumber: true,
        })}
      />
      <Input
        type="number"
        label="Timeout"
        disabled={!moduleEnabled}
        {...register('timeout', {
          valueAsNumber: true,
        })}
      />
      <Input
        type="number"
        label="Mode"
        disabled={!moduleEnabled}
        {...register('mode', {
          valueAsNumber: true,
        })}
      />
    </Form>
  );
}
Example #24
Source File: index.tsx    From rabet-extension with GNU General Public License v3.0 4 votes vote down vote up
SwapDetails = ({
  path,
  values,
  control,
  minimumReceived,
}: SwapDetailsProps) => {
  const [marketPrice, setMarketPrice] = useState<BigNumber | string>(
    '',
  );

  let formValues = values;

  if (control) {
    formValues = useWatch({ control });
  }

  useEffect(() => {
    calculatePriceImpact(formValues.asset1, formValues.asset2).then(
      (result) => {
        setMarketPrice(result);
      },
    );
  }, [formValues]);

  const priceImpact = new BN(1)
    .minus(
      new BN(minimumReceived).div(
        new BN(marketPrice).times(formValues.from),
      ),
    )
    .times(100);

  let finalPriceImpact = priceImpact.toFixed(2);

  if (priceImpact.isNaN() || !priceImpact.isFinite()) {
    finalPriceImpact = '0';
  }

  if (priceImpact.isLessThan(0)) {
    finalPriceImpact = '0';
  }

  if (priceImpact.isLessThan(0.1)) {
    finalPriceImpact = '<0.01';
  }

  const assetCode = (asset) =>
    asset.asset_type === 'native' ? 'XLM' : asset.asset_code;

  return (
    <>
      <S.Box>
        <S.BoxTitle>Path</S.BoxTitle>
        <S.Path>
          {path.map((p, index) => (
            <div key={assetCode(p)} className="flex items-center">
              {assetCode(p)}
              {index !== path.length - 1 && (
                <div className="mx-[5px]">
                  <AngleRight />
                </div>
              )}
            </div>
          ))}
        </S.Path>
      </S.Box>

      <S.Box>
        <S.BoxTitle>Price impact</S.BoxTitle>
        <S.BoxValue
          className="up"
          color={priceImpactColor(finalPriceImpact)}
        >
          {finalPriceImpact.toString()}%
        </S.BoxValue>
      </S.Box>

      <S.Box>
        <S.BoxTitle>Minimum received</S.BoxTitle>
        <div>
          {minimumReceived ? (
            <>
              {formatBalance(minimumReceived.toString())}{' '}
              {formValues.asset2.asset_code || 'XLM'}
            </>
          ) : (
            <p>Asset</p>
          )}
        </div>
      </S.Box>
    </>
  );
}
Example #25
Source File: general.tsx    From admin with MIT License 4 votes vote down vote up
General: React.FC<GeneralProps> = ({ discount }) => {
  const initialCurrency = discount?.regions?.[0].currency_code || undefined

  const [fixedRegionCurrency, setFixedRegionCurrency] = useState<
    string | undefined
  >(initialCurrency)

  const { regions: opts } = useAdminRegions()
  const { register, control, type } = useDiscountForm()

  const regions = useWatch({
    control,
    name: "regions",
  })

  useEffect(() => {
    if (type === "fixed" && regions) {
      let id: string

      if (Array.isArray(regions) && regions.length) {
        id = regions[0].value
      } else {
        id = ((regions as unknown) as { label: string; value: string }).value // if you change from fixed to percentage, unselect and select a region, and then change back to fixed it is possible to make useForm set regions to an object instead of an array
      }

      const reg = opts?.find((r) => r.id === id)

      if (reg) {
        setFixedRegionCurrency(reg.currency_code)
      }
    }
  }, [type, opts, regions])

  const regionOptions = useMemo(() => {
    return opts?.map((r) => ({ value: r.id, label: r.name })) || []
  }, [opts])

  const [render, setRender] = useState(false)
  useEffect(() => {
    setTimeout(() => setRender(true), 100)
  }, [])

  return (
    <div className="pt-5">
      {render && (
        <>
          <Controller
            name="regions"
            control={control}
            rules={{
              required: "Atleast one region is required",
              validate: (value) =>
                Array.isArray(value) ? value.length > 0 : !!value,
            }}
            render={({ onChange, value }) => {
              return (
                <Select
                  value={value || null}
                  onChange={(value) => {
                    onChange(type === "fixed" ? [value] : value)
                  }}
                  label="Choose valid regions"
                  isMultiSelect={type !== "fixed"}
                  hasSelectAll={type !== "fixed"}
                  enableSearch
                  required
                  options={regionOptions}
                />
              )
            }}
          />
          <div className="flex gap-x-base gap-y-base my-base">
            <InputField
              label="Code"
              className="flex-1"
              placeholder="SUMMERSALE10"
              required
              name="code"
              ref={register({ required: "Code is required" })}
            />

            {type !== "free_shipping" && (
              <>
                {type === "fixed" ? (
                  <div className="flex-1">
                    <CurrencyInput
                      size="small"
                      currentCurrency={fixedRegionCurrency}
                      readOnly
                      hideCurrency
                    >
                      <Controller
                        name="rule.value"
                        control={control}
                        rules={{
                          required: "Amount is required",
                          min: 1,
                        }}
                        render={({ value, onChange }) => {
                          return (
                            <CurrencyInput.AmountInput
                              label={"Amount"}
                              required
                              amount={value}
                              onChange={onChange}
                            />
                          )
                        }}
                      />
                    </CurrencyInput>
                  </div>
                ) : (
                  <div className="flex-1">
                    <InputField
                      label="Percentage"
                      min={0}
                      required
                      type="number"
                      placeholder="10"
                      prefix={"%"}
                      name="rule.value"
                      ref={register({
                        required: true,
                        valueAsNumber: true,
                      })}
                    />
                  </div>
                )}
              </>
            )}
          </div>

          <div className="text-grey-50 inter-small-regular flex flex-col mb-6">
            <span>
              The code your customers will enter during checkout. This will
              appear on your customer’s invoice.
            </span>
            <span>Uppercase letters and numbers only.</span>
          </div>
          <Textarea
            label="Description"
            required
            placeholder="Summer Sale 2022"
            rows={1}
            name="rule.description"
            ref={register({
              required: true,
            })}
          />
          <div className="mt-xlarge flex items-center">
            <Controller
              name="is_dynamic"
              control={control}
              render={({ onChange, value }) => {
                return (
                  <Checkbox
                    label="This is a template discount"
                    name="is_dynamic"
                    id="is_dynamic"
                    checked={value}
                    onChange={(e) => onChange(e.target.checked)}
                  />
                )
              }}
            />
            <IconTooltip
              content={
                "Template discounts allow you to define a set of rules that can be used across a group of discounts. This is useful in campaigns that should generate unique codes for each user, but where the rules for all unique codes should be the same."
              }
            />
          </div>
        </>
      )}
    </div>
  )
}
Example #26
Source File: index.tsx    From rabet-extension with GNU General Public License v3.0 4 votes vote down vote up
BasicSwap = ({ usage }: AppProps) => {
  const navigate = useNavigate();
  const account = useActiveAccount();
  const [assets] = useState(() => {
    const accountAssets = account.assets || [];

    const assetsPlusDefaultAssets = combineAssets(
      accountAssets,
      defaultAssets,
    );

    return assetsPlusDefaultAssets;
  });

  const [path, setPath] = useState([]);
  const [loading, setLoading] = useState(false);
  const [showSwapInfo, setShowSwapInfo] = useState(false);
  const [minimumReceived, setMinimumReceived] = useState(0);
  const [isRotateActive, setIsRotateActive] = useState(false);
  const [shouldRotate, setShouldRotate] = useState(false);

  const {
    reset,
    trigger,
    control,
    setValue,
    getValues,
    handleSubmit,
    clearErrors,
    formState: { errors, isValid, isValidating },
  } = useForm({
    mode: 'onChange',
    defaultValues: {
      to: '',
      from: '',
      asset1: assets[0],
      asset2: assets[1],
      lastField: 'from',
    },
    resolver: (formValues) =>
      validateForm(
        formValues,
        account,
        setValue,
        setLoading,
        setRealData,
        setShowSwapInfo,
      ),
  });

  function setRealData(
    values: FormValues,
    calculatedResult: ServerApi.PaymentPathRecord,
  ) {
    clearErrors('from');
    clearErrors('to');

    setIsRotateActive(false);

    const calculatePath = [
      values.asset1,
      ...calculatedResult.path,
      values.asset2,
    ];
    setPath(calculatePath);

    if (values.lastField === 'from') {
      setValue('to', calculatedResult.destination_amount);

      const minReceived = new BN(calculatedResult.destination_amount)
        .div(100)
        .times(config.MIN_RECEIVED);

      setMinimumReceived(parseFloat(minReceived.toString()));
    } else {
      setValue('from', calculatedResult.source_amount);

      if (
        !isInsufficientAsset(
          values.asset1,
          account.subentry_count,
          values.from,
        )
      ) {
        errors.from = {
          type: 'error',
          message: 'Insufficient amount.',
        };
      }

      const minReceived = new BN(values.to)
        .div(100)
        .times(config.MIN_RECEIVED);

      setMinimumReceived(parseFloat(minReceived.toString()));
    }

    setShowSwapInfo(true);
  }

  useEffect(() => {
    trigger();
  }, [useWatch({ name: ['asset1', 'asset2'], control })]);

  const setFromMax = () => {
    const formValues = getValues();
    const maxValue = getMaxBalance(formValues.asset1, account);

    setValue('from', maxValue, {
      shouldValidate: true,
    });
    setValue('to', '0');
  };

  const handleSwapPlaces = () => {
    const { to, from, asset1, asset2 } = getValues();

    setValue('asset1', asset2, {
      shouldValidate: true,
    });
    setValue('asset2', asset1, {
      shouldValidate: true,
    });

    setValue('to', from);
    setValue('from', to);
  };

  const onSubmit = (v: FormValues) => {
    setShowSwapInfo(false);

    const values = {
      ...v,
      path,
      minimumReceived,
    };

    if (usage === 'extension') {
      navigate(RouteName.BasicSwapConfirm, {
        state: {
          values,
        },
      });
    } else {
      openModalAction({
        title: '',
        isStyled: false,
        size: 'medium',
        padding: 'medium',
        minHeight: 0,
        children: (
          <div className="px-8 pt-8 pb-[14px] min-h-[534px]">
            <BasicConfirmSwap usage="desktop" values={values} />
          </div>
        ),
      });
    }

    reset();
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <label className="label-primary block mt-[12px]">From</label>
      <S.ModalInput>
        <Controller
          name="lastField"
          control={control}
          render={({ field }) => (
            <Input
              invisible
              type="text"
              size="medium"
              value={field.value}
              onChange={field.onChange}
              errorMsg={errors.lastField && errors.lastField.message}
            />
          )}
        />

        <Controller
          name="from"
          control={control}
          render={({ field }) => (
            <Input
              type="number"
              placeholder="0.0"
              size="medium"
              variant="max"
              value={field.value}
              onChange={(e) => {
                setValue('lastField', 'from');
                field.onChange(e);
              }}
              setMax={setFromMax}
              errorMsg={errors.from && errors.from.message}
              onKeyPress={controlNumberInput}
              styleType="light"
              className="grow"
            />
          )}
        />

        <Controller
          name="asset1"
          control={control}
          render={({ field }) => (
            <SelectAssetModal
              usage={usage}
              valueName="asset1"
              asset={field.value}
              onChange={field.onChange}
              assets={assets}
            />
          )}
        />
      </S.ModalInput>

      <div className="flex justify-center">
        <div
          style={{
            width: 32,
            height: 32,
            borderRadius: '50%',
            overflow: 'hidden',
            cursor: 'pointer',
          }}
          onClick={handleSwapPlaces}
        >
          <Swap />
        </div>
      </div>

      <label className="label-primary block mt-[-28px]">To</label>
      <S.ModalInput>
        <Controller
          name="to"
          control={control}
          render={({ field }) => (
            <Input
              type="number"
              placeholder="0.0"
              size="medium"
              value={field.value}
              onChange={(e) => {
                setValue('lastField', 'to');
                field.onChange(e);
              }}
              errorMsg={errors.to && errors.to.message}
              onKeyPress={controlNumberInput}
              styleType="light"
              className="grow"
            />
          )}
        />

        <Controller
          name="asset2"
          control={control}
          render={({ field }) => (
            <SelectAssetModal
              usage={usage}
              asset={field.value}
              valueName="asset2"
              assets={assets}
              onChange={field.onChange}
            />
          )}
        />
      </S.ModalInput>

      {loading ? (
        <div className="flex justify-center">
          <Loading size={40} className="!p-0" />
        </div>
      ) : (
        ''
      )}

      {showSwapInfo ? (
        <>
          <div className="flex items-center justify-end">
            <div className="mr-1">
              <ShowFractional
                control={control}
                isRotateActive={isRotateActive}
              />
            </div>

            <S.Rotate isRotate={shouldRotate}>
              <span
                onClick={() => {
                  setIsRotateActive(!isRotateActive);
                  setShouldRotate(true);

                  setTimeout(() => {
                    setShouldRotate(false);
                  }, 500);
                }}
              >
                <Rotate />
              </span>
            </S.Rotate>
          </div>

          <S.Hr />

          <SwapDetail
            path={path}
            control={control}
            minimumReceived={minimumReceived}
          />
        </>
      ) : (
        ''
      )}

      <ButtonContainer
        btnSize={usage === 'extension' ? 100 : 104}
        justify="end"
        positionStyles={{
          bottom: '32px',
        }}
        mt={40}
      >
        {usage !== 'desktop' ? (
          <Button
            type="button"
            variant="default"
            size="medium"
            content="Cancel"
            onClick={() => {
              navigate('/', {
                state: {
                  alreadyLoaded: true,
                },
              });
            }}
            style={{ marginRight: '7px' }}
          />
        ) : (
          ''
        )}

        <Button
          type="submit"
          variant="primary"
          size="medium"
          content="Swap"
          style={{ marginRight: '-12px' }}
          disabled={!isValid || isValidating || !showSwapInfo}
        />
      </ButtonContainer>
    </form>
  );
}
Example #27
Source File: edit-general.tsx    From admin with MIT License 4 votes vote down vote up
EditGeneral: React.FC<EditGeneralProps> = ({ discount, onClose }) => {
  const { mutate, isLoading } = useAdminUpdateDiscount(discount.id)
  const notification = useNotification()

  const { control, handleSubmit, reset, register } = useForm<GeneralForm>({
    defaultValues: mapGeneral(discount),
  })

  const onSubmit = (data: GeneralForm) => {
    mutate(
      {
        regions: data.regions.map((r) => r.value),
        code: data.code,
        rule: {
          id: discount.rule.id,
          description: data.description,
          value: data.value,
          allocation: discount.rule.allocation,
        },
      },
      {
        onSuccess: ({ discount }) => {
          notification("Success", "Discount updated successfully", "success")
          reset(mapGeneral(discount))
          onClose()
        },
        onError: (error) => {
          notification("Error", getErrorMessage(error), "error")
        },
      }
    )
  }

  useEffect(() => {
    reset(mapGeneral(discount))
  }, [discount])

  const type = discount.rule.type

  const { regions } = useAdminRegions()

  const regionOptions = useMemo(() => {
    return regions
      ? regions.map((r) => ({
          label: r.name,
          value: r.id,
        }))
      : []
  }, [regions])

  const selectedRegions = useWatch<Option[]>({
    control,
    name: "regions",
  })

  const fixedCurrency = useMemo(() => {
    if (type === "fixed" && selectedRegions?.length) {
      return regions?.find((r) => r.id === selectedRegions[0].value)
        ?.currency_code
    }
  }, [selectedRegions, type, regions])

  return (
    <Modal handleClose={onClose}>
      <form onSubmit={handleSubmit(onSubmit)}>
        <Modal.Body>
          <Modal.Header handleClose={onClose}>
            <h1 className="inter-xlarge-semibold">Edit general information</h1>
          </Modal.Header>
          <Modal.Content isLargeModal>
            <Controller
              name="regions"
              control={control}
              rules={{
                required: "Atleast one region is required",
                validate: (value) =>
                  Array.isArray(value) ? value.length > 0 : !!value,
              }}
              render={({ value, onChange }) => {
                return (
                  <Select
                    value={value}
                    onChange={(value) => {
                      onChange(type === "fixed" ? [value] : value)
                    }}
                    label="Choose valid regions"
                    isMultiSelect={type !== "fixed"}
                    hasSelectAll={type !== "fixed"}
                    enableSearch
                    required
                    options={regionOptions}
                  />
                )
              }}
            />
            <div className="flex gap-x-base gap-y-base my-base">
              <InputField
                label="Code"
                className="flex-1"
                placeholder="SUMMERSALE10"
                required
                name="code"
                ref={register({ required: "Code is required" })}
              />

              {type !== "free_shipping" && (
                <>
                  {type === "fixed" ? (
                    <div className="flex-1">
                      <CurrencyInput
                        size="small"
                        currentCurrency={fixedCurrency ?? "USD"}
                        readOnly
                        hideCurrency
                      >
                        <Controller
                          name="value"
                          control={control}
                          rules={{
                            required: "Amount is required",
                            min: 1,
                          }}
                          render={({ value, onChange }) => {
                            return (
                              <CurrencyInput.AmountInput
                                label={"Amount"}
                                required
                                amount={value}
                                onChange={onChange}
                              />
                            )
                          }}
                        />
                      </CurrencyInput>
                    </div>
                  ) : (
                    <div className="flex-1">
                      <InputField
                        label="Percentage"
                        min={0}
                        required
                        type="number"
                        placeholder="10"
                        prefix={"%"}
                        name="value"
                        ref={register({
                          required: "Percentage is required",
                          valueAsNumber: true,
                        })}
                      />
                    </div>
                  )}
                </>
              )}
            </div>

            <div className="text-grey-50 inter-small-regular flex flex-col mb-6">
              <span>
                The code your customers will enter during checkout. This will
                appear on your customer’s invoice.
              </span>
              <span>Uppercase letters and numbers only.</span>
            </div>
            <Textarea
              label="Description"
              required
              placeholder="Summer Sale 2022"
              rows={1}
              name="description"
              ref={register({
                required: "Description is required",
              })}
            />
          </Modal.Content>
          <Modal.Footer>
            <div className="gap-x-xsmall flex items-center justify-end w-full">
              <Button
                variant="ghost"
                size="small"
                className="min-w-[128px]"
                type="button"
                onClick={onClose}
              >
                Cancel
              </Button>
              <Button
                variant="primary"
                size="small"
                className="min-w-[128px]"
                type="submit"
                loading={isLoading}
              >
                Save
              </Button>
            </div>
          </Modal.Footer>
        </Modal.Body>
      </form>
    </Modal>
  )
}
Example #28
Source File: PaymentMethodsInput.tsx    From coindrop with GNU General Public License v3.0 4 votes vote down vote up
PaymentMethodsInput: FC<Props> = ({ fieldArrayName, fields, control, register, remove, append }) => {
    const { colors } = useTheme();
    const paymentMethodsDataWatch: PaymentMethod[] = useWatch({
        control,
        name: fieldArrayName,
    });
    const [openAccordionItemIndex, setOpenAccordionItemIndex] = useState(-1);
    useEffect(() => {
        if (
            paymentMethodsDataWatch[paymentMethodsDataWatch.length - 1]?.paymentMethodId === "default-blank"
            || paymentMethodsDataWatch[paymentMethodsDataWatch.length - 1]?.address === ""
        ) {
            setOpenAccordionItemIndex(paymentMethodsDataWatch.length - 1);
        }
    }, [paymentMethodsDataWatch]);
    const containsInvalidAddress = paymentMethodsDataWatch.some(paymentMethod => !paymentMethod.address);
    const { isAddressTouched, setIsAddressTouched } = useContext(AdditionalValidation);
    // optgroup not compatible with Chakra dark mode: https://github.com/chakra-ui/chakra-ui/issues/2853
        // const optionsGroup = (category: Category) => {
        //     const optgroupLabels: Record<Category, string> = {
        //         "digital-wallet": 'Digital Wallets',
        //         "digital-asset": "Digital Assets",
        //         "subscription-platform": "Subscription Platforms",
        //     };
        //     return (
        //         <optgroup label={optgroupLabels[category]}>
        //             {paymentMethods
        //             .filter(paymentMethod => paymentMethod.category === category)
        //             .sort((a, b) => (a.id < b.id ? -1 : 1))
        //             .map(({ id, displayName }) => (
        //                 <option
        //                     key={id}
        //                     value={id}
        //                     style={{display: paymentMethodsDataWatch.map(paymentMethodDataWatch => paymentMethodDataWatch.paymentMethodId).includes(id) ? "none" : undefined }}
        //                 >
        //                     {displayName}
        //                 </option>
        //             ))}
        //         </optgroup>
        //     );
        // };
    return (
        <>
        {fields.length < 1
            ? 'No payment methods defined yet.'
        : (
            <Accordion
                allowToggle
                defaultIndex={-1}
                index={openAccordionItemIndex}
            >
                {
                fields
                    .map((item, index) => {
                    const watchedData = paymentMethodsDataWatch.find(watchedPaymentMethod => watchedPaymentMethod.id === item.id);
                    const PaymentMethodIcon = paymentMethodIcons[watchedData?.paymentMethodId];
                    return (
                        <AccordionItem
                            key={item.id}
                        >
                            <AccordionButton
                                onClick={() => {
                                    setIsAddressTouched(true);
                                    if (openAccordionItemIndex !== index && !paymentMethodsDataWatch.find(paymentMethod => paymentMethod.address === "")) {
                                        setOpenAccordionItemIndex(index);
                                    } else {
                                        setOpenAccordionItemIndex(undefined);
                                    }
                                }}
                            >
                                <Flex flex="1" textAlign="left" align="center">
                                    <Flex mr={1} align="center">
                                        {PaymentMethodIcon ? <PaymentMethodIcon mr={2} /> : <QuestionOutlineIcon mr={2} />}
                                        {paymentMethodNames[watchedData?.paymentMethodId] ?? 'Add payment method'}
                                    </Flex>
                                    {watchedData?.isPreferred && (
                                        <Flex>
                                            <StarIcon
                                                ml={2}
                                                size="16px"
                                                color={colors.yellow['400']}
                                            />
                                            <Text
                                                as="span"
                                                fontSize="xs"
                                                ml={1}
                                            >
                                                <i>Preferred</i>
                                            </Text>
                                        </Flex>
                                    )}
                                </Flex>
                                <AccordionIcon />
                            </AccordionButton>
                            <AccordionPanel pb={4} id={`accordion-panel-${watchedData.paymentMethodId}`}>
                                <input
                                    ref={register()}
                                    name={`${fieldArrayName}[${index}].id`}
                                    defaultValue={item.id}
                                    style={{display: 'none'}}
                                />
                                <Box
                                    display={paymentMethodNames[watchedData?.paymentMethodId] ? "none" : "block"}
                                    data-cy={`select-payment-method-container-${watchedData.paymentMethodId}`}
                                >
                                    <Select
                                        name={`${fieldArrayName}[${index}].paymentMethodId`}
                                        ref={register()}
                                        defaultValue={paymentMethodNames[item.paymentMethodId] ? item.paymentMethodId : 'default-blank'}
                                        isInvalid={containsInvalidAddress && isAddressTouched}
                                        onChange={() => setIsAddressTouched(false)}
                                    >
                                        <option hidden disabled value="default-blank">Select...</option>
                                        {/* optgroup not compatible with Chakra dark mode: https://github.com/chakra-ui/chakra-ui/issues/2853 */}
                                        {Object.entries(paymentMethodNames)
                                            .sort((a, b) => {
                                                const [aId] = a;
                                                const [bId] = b;
                                                return aId < bId ? -1 : 1;
                                            })
                                            .map(([paymentMethodId, paymentMethodDisplayName]) => (
                                                <option
                                                    key={paymentMethodId}
                                                    value={paymentMethodId}
                                                    style={{display: paymentMethodsDataWatch.map(paymentMethod => paymentMethod.paymentMethodId).includes(paymentMethodId) ? "none" : undefined }}
                                                >
                                                    {paymentMethodDisplayName}
                                                </option>
                                            ))}
                                    </Select>
                                </Box>
                                <Box
                                    mx={3}
                                    display={paymentMethodNames[watchedData?.paymentMethodId] ? "block" : "none"}
                                >
                                    <FormControl isRequired>
                                        <FormLabel htmlFor={`${fieldArrayName}[${index}].address`}>Address</FormLabel>
                                        <Input
                                            id={`address-input-${watchedData.paymentMethodId}`}
                                            name={`${fieldArrayName}[${index}].address`}
                                            ref={register()}
                                            defaultValue={item.address}
                                            isInvalid={containsInvalidAddress && isAddressTouched}
                                        />
                                        <Box>
                                            <Checkbox
                                                name={`${fieldArrayName}[${index}].isPreferred`}
                                                ref={register()}
                                                defaultValue={item?.isPreferred ? 1 : 0}
                                                defaultIsChecked={item?.isPreferred}
                                                mt={1}
                                                colorScheme="yellow"
                                            >
                                                Preferred
                                            </Checkbox>
                                        </Box>
                                    </FormControl>
                                </Box>
                                <Flex
                                    justify={watchedData?.paymentMethodId === 'default-blank' ? 'space-between' : 'flex-end'}
                                    mt={3}
                                    wrap="wrap"
                                    align="center"
                                >
                                    {watchedData?.paymentMethodId === 'default-blank' && (
                                        <Text fontSize="xs" ml={1}>
                                            <Link href="/blog/payment-method-request" isExternal>
                                                Payment method not listed?
                                            </Link>
                                        </Text>
                                    )}
                                    <Button
                                        onClick={() => {
                                            remove(index);
                                            setIsAddressTouched(false);
                                        }}
                                        leftIcon={<MinusIcon />}
                                        size="sm"
                                    >
                                        {'Remove '}
                                        {/* {paymentMethodNames[watchedData?.paymentMethodId]} */}
                                    </Button>
                                </Flex>
                            </AccordionPanel>
                        </AccordionItem>
                    );
                })
}
            </Accordion>
        )}
        <Flex
            justify="center"
            mt={2}
        >
            <Button
                id="add-payment-method-button"
                onClick={() => {
                    append({});
                    setIsAddressTouched(false);
                }}
                leftIcon={<AddIcon />}
                variant="ghost"
                size="sm"
                isDisabled={
                    (
                        fields.length > 0
                        && !paymentMethodNames[paymentMethodsDataWatch[paymentMethodsDataWatch.length - 1]?.paymentMethodId]
                    )
                    || containsInvalidAddress
                }
            >
                Add payment method
            </Button>
        </Flex>
        </>
    );
}
Example #29
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>
  )
}