@chakra-ui/react#FormLabel TypeScript Examples

The following examples show how to use @chakra-ui/react#FormLabel. 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: FormControl.tsx    From openchakra with MIT License 6 votes vote down vote up
FormControl: React.FC<FormControlPropType> = ({
  label,
  htmlFor,
  children,
  hasColumn,
}) => (
  <ChakraFormControl
    mb={3}
    as={Grid}
    display="flex"
    alignItems="center"
    justifyItems="center"
  >
    <FormLabel
      p={0}
      mr={2}
      color="gray.500"
      lineHeight="1rem"
      width={hasColumn ? '2.5rem' : '90px'}
      fontSize="xs"
      htmlFor={htmlFor}
    >
      {label}
    </FormLabel>
    <Box
      display="flex"
      alignItems="center"
      justifyItems="center"
      width={hasColumn ? '30px' : '130px'}
    >
      {children}
    </Box>
  </ChakraFormControl>
)
Example #2
Source File: NgrokRegionField.tsx    From bluebubbles-server with Apache License 2.0 6 votes vote down vote up
NgrokRegionField = ({ helpText }: NgrokRegionFieldProps): JSX.Element => {
    const ngrokRegion: string = (useAppSelector(state => state.config.ngrok_region) ?? '');
    return (
        <FormControl>
            <FormLabel htmlFor='ngrok_region'>Ngrok Region</FormLabel>
            <Flex flexDirection='row' justifyContent='flex-start' alignItems='center'>
                <Select
                    id='ngrok_region'
                    placeholder='Select your Ngrok Region'
                    maxWidth="15em"
                    mr={3}
                    value={ngrokRegion}
                    onChange={(e) => {
                        if (!e.target.value || e.target.value.length === 0) return;
                        onSelectChange(e);
                    }}
                >
                    <option value='us'>North America</option>
                    <option value='eu'>Europe</option>
                    <option value='ap'>Asia/Pacific</option>
                    <option value='au'>Australia</option>
                    <option value='sa'>South America</option>
                    <option value='jp'>Japan</option>
                    <option value='in'>India</option>
                </Select>
            </Flex>
            <FormHelperText>
                {helpText ?? 'Select the region closest to you. This will ensure latency is at its lowest when connecting to the server.'}
            </FormHelperText>
        </FormControl>
    );
}
Example #3
Source File: UserForm.tsx    From next-crud with MIT License 6 votes vote down vote up
UserForm = ({ initialValues, onSubmit }: IProps) => {
  const { register, formState, handleSubmit } = useForm<IFormValues>({
    defaultValues: initialValues,
    resolver: yupResolver(schema),
    mode: 'onChange',
  })

  return (
    <VStack
      as="form"
      onSubmit={handleSubmit(onSubmit)}
      spacing={4}
      width="100%"
    >
      <FormControl
        id="username"
        isInvalid={!!formState.errors.username?.message}
      >
        <FormLabel>Username</FormLabel>
        <Input name="username" ref={register} />
        <FormErrorMessage>
          {formState.errors.username?.message}
        </FormErrorMessage>
      </FormControl>
      <Button
        type="submit"
        colorScheme="blue"
        isLoading={formState.isSubmitting}
        disabled={!formState.isValid}
      >
        Submit
      </Button>
    </VStack>
  )
}
Example #4
Source File: UrlField.tsx    From calories-in with MIT License 6 votes vote down vote up
function UrlField({ canEdit, food }: Props) {
  const { register } = useFormContext<FoodForm>()

  return (
    <Flex minHeight={canEdit ? '200px' : undefined} flexDirection="column">
      {canEdit && (
        <Alert status="info" mb={3}>
          <AlertIcon color="teal.400" />
          Add a link will open a web page when the food is clicked. This is
          useful if you want to show a specific product.
        </Alert>
      )}

      <FormControl id="email">
        <Flex alignItems="center">
          <FormLabel mb={0} flexShrink={0}>
            Link:
          </FormLabel>
          {canEdit ? (
            <Input
              {...register('url')}
              placeholder="http://example.com"
              type="email"
            />
          ) : (
            <Link
              href={food?.url}
              target="_blank"
              noOfLines={1}
              color="teal.500"
            >
              {food?.url}
            </Link>
          )}
        </Flex>
      </FormControl>
    </Flex>
  )
}
Example #5
Source File: index.tsx    From formik-chakra-ui with MIT License 6 votes vote down vote up
FormControl: FC<BaseProps> = (props: BaseProps) => {
  const {
    children,
    name,
    label,
    labelProps,
    helperText,
    helperTextProps,
    errorMessageProps,
    ...rest
  } = props;
  const [, { error, touched }] = useField(name);

  return (
    <ChakraFormControl isInvalid={!!error && touched} {...rest}>
      {label && (
        <FormLabel htmlFor={name} {...labelProps}>
          {label}
        </FormLabel>
      )}
      {children}
      {error && (
        <FormErrorMessage {...errorMessageProps}>{error}</FormErrorMessage>
      )}
      {helperText && (
        <FormHelperText {...helperTextProps}>{helperText}</FormHelperText>
      )}
    </ChakraFormControl>
  );
}
Example #6
Source File: AddNewFeedForm.tsx    From nextjs-hasura-boilerplate with MIT License 5 votes vote down vote up
AddNewFeedForm = () => {
  const [body, setBody] = useState("");
  const [session] = useSession();
  const [
    insertFeed,
    { loading: insertFeedFetching, error: insertFeedError },
  ] = useInsertFeedMutation();

  if (!session) {
    return (
      <AccessDeniedIndicator message="You need to be signed in to add a new feed!" />
    );
  }

  const handleSubmit = async () => {
    await insertFeed({
      variables: {
        author_id: session.id,
        body,
      },
    });

    setBody("");
  };

  const errorNode = () => {
    if (!insertFeedError) {
      return false;
    }

    return (
      <Alert status="error">
        <AlertIcon />
        <AlertTitle>{insertFeedError}</AlertTitle>
        <CloseButton position="absolute" right="8px" top="8px" />
      </Alert>
    );
  };

  return (
    <Stack spacing={4}>
      {errorNode()}
      <Box p={4} shadow="lg" rounded="lg">
        <Stack spacing={4}>
          <FormControl isRequired>
            <FormLabel htmlFor="body">What's on your mind?</FormLabel>
            <Textarea
              id="body"
              value={body}
              onChange={(e: ChangeEvent<HTMLTextAreaElement>) =>
                setBody(e.currentTarget.value)
              }
              isDisabled={insertFeedFetching}
            />
          </FormControl>
          <FormControl>
            <Button
              loadingText="Posting..."
              onClick={handleSubmit}
              isLoading={insertFeedFetching}
              isDisabled={!body.trim()}
            >
              Post
            </Button>
          </FormControl>
        </Stack>
      </Box>
    </Stack>
  );
}
Example #7
Source File: LocalPortField.tsx    From bluebubbles-server with Apache License 2.0 5 votes vote down vote up
LocalPortField = ({ helpText }: LocalPortFieldProps): JSX.Element => {
    const dispatch = useAppDispatch();

    const port: number = useAppSelector(state => state.config.socket_port) ?? 1234;
    const [newPort, setNewPort] = useState(port);
    const [portError, setPortError] = useState('');
    const hasPortError: boolean = (portError?? '').length > 0;

    useEffect(() => { setNewPort(port); }, [port]);

    /**
     * A handler & validator for saving a new port.
     *
     * @param theNewPort - The new port to save
     */
    const savePort = (theNewPort: number): void => {
        // Validate the port
        if (theNewPort < 1024 || theNewPort > 65635) {
            setPortError('Port must be between 1,024 and 65,635');
            return;
        } else if (theNewPort === port) {
            setPortError('You have not changed the port since your last save!');
            return;
        }

        dispatch(setConfig({ name: 'socket_port', value: theNewPort }));
        if (hasPortError) setPortError('');
        showSuccessToast({
            id: 'settings',
            duration: 4000,
            description: 'Successfully saved new port! Restarting Proxy & HTTP services...'
        });
    };

    return (
        <FormControl isInvalid={hasPortError}>
            <FormLabel htmlFor='socket_port'>Local Port</FormLabel>
            <Flex flexDirection='row' justifyContent='flex-start' alignItems='center'>
                <Input
                    id='socket_port'
                    type='number'
                    maxWidth="5em"
                    value={newPort}
                    onChange={(e) => {
                        if (hasPortError) setPortError('');
                        setNewPort(Number.parseInt(e.target.value));
                    }}
                />
                <IconButton
                    ml={3}
                    verticalAlign='top'
                    aria-label='Save port'
                    icon={<AiOutlineSave />}
                    onClick={() => savePort(newPort)}
                />
            </Flex>
            {!hasPortError ? (
                <FormHelperText>
                    {helpText ?? 'Enter the local port for the socket server to run on'}
                </FormHelperText>
            ) : (
                <FormErrorMessage>{portError}</FormErrorMessage>
            )}
        </FormControl>
    );
}
Example #8
Source File: IconCustomizerDrawer.tsx    From lucide with ISC License 5 votes vote down vote up
export function IconCustomizerDrawer() {
  const [showCustomize, setShowCustomize] = useState(false);
  const { color, setColor, size, setSize, strokeWidth, setStroke, resetStyle } = useContext(IconStyleContext);

  return (
    <>
      <Button as="a" leftIcon={<Edit />} size="lg" onClick={() => setShowCustomize(true)}>
        Customize
      </Button>
      <Drawer isOpen={showCustomize} placement="right" onClose={() => setShowCustomize(false)}>
        <DrawerOverlay />
        <DrawerContent>
          <DrawerCloseButton />
          <DrawerHeader>Customize Icons</DrawerHeader>

          <DrawerBody>
            <Grid gridGap={'1em'}>
              <FormControl>
                <ColorPicker
                  color={color}
                  value={color}
                  onChangeComplete={(col) => setColor(col.hex)}
                />
              </FormControl>
              <FormControl>
                <FormLabel htmlFor="stroke">
                  <Flex>
                    <Text flexGrow={1} fontWeight={'bold'}>
                      Stroke
                    </Text>
                    <Text>{strokeWidth}px</Text>
                  </Flex>
                </FormLabel>
                <Slider
                  value={strokeWidth}
                  onChange={setStroke}
                  min={0.5}
                  max={3}
                  step={0.5}
                  name={'stroke'}
                >
                  <SliderTrack>
                    <SliderFilledTrack bg={color} />
                  </SliderTrack>
                  <SliderThumb />
                </Slider>
              </FormControl>
              <FormControl>
                <FormLabel htmlFor="size">
                  <Flex>
                    <Text flexGrow={1} fontWeight={'bold'}>
                      Size
                    </Text>
                    <Text>{size}px</Text>
                  </Flex>
                </FormLabel>
                <Slider value={size} onChange={setSize} min={12} max={64} step={1} name={'size'}>
                  <SliderTrack>
                    <SliderFilledTrack bg={color} />
                  </SliderTrack>
                  <SliderThumb />
                </Slider>
              </FormControl>
              <FormControl>
                <Button onClick={resetStyle}>Reset</Button>
              </FormControl>
            </Grid>
          </DrawerBody>
        </DrawerContent>
      </Drawer>
    </>
  );
}
Example #9
Source File: ColorPicker.tsx    From lucide with ISC License 5 votes vote down vote up
function ColorPicker({ hsv, hsl, onChange, value: color }: ColorPickerProps) {
  const [value, setValue] = useState(color);
  const input = useRef<HTMLInputElement>(null);

  useEffect(() => {
    if (color !== value && input.current !== document.activeElement) {
      setValue(color === 'currentColor' ? color : String(color).toUpperCase());
    }
  }, [color]);

  const handleChange = (e) => {
    let value = e.target.value;
    setValue(value);
    onChange(value, e);
  };

  return (
    <div>
      <FormLabel htmlFor="color" fontWeight={'bold'}>
        Color
      </FormLabel>
      <InputGroup>
        <InputLeftElement
          children={
            <Icon>
              <rect x={0} width={24} y={0} height={24} fill={value} rx={2} />
            </Icon>
          }
        />
        <Input value={value} name="color" onChange={handleChange} ref={input} />
      </InputGroup>
      <div
        style={{
          width: '100%',
          paddingBottom: '75%',
          position: 'relative',
          overflow: 'hidden',
          marginTop: '0.5rem',
          borderRadius: '0.375rem',
          border: '1px solid',
          borderColor: 'inherit',
        }}
      >
        <Saturation hsl={hsl} hsv={hsv} onChange={onChange} />
      </div>
      <div
        style={{
          minHeight: '2em',
          position: 'relative',
          margin: '0.5rem 0 0 0',
          borderRadius: '0.375rem',
          border: '1px solid',
          borderColor: 'inherit',
          overflow: 'hidden',
        }}
      >
        <Hue hsl={hsl} onChange={onChange} direction={'horizontal'} />
      </div>
    </div>
  );
}
Example #10
Source File: index.tsx    From coindrop with GNU General Public License v3.0 5 votes vote down vote up
UserSettingsPage: FC = () => {
    const { user } = useUser();
    const userId = user?.id;
    const fetcher = () => getUserData(userId);
    const { data: userData, error: fetchError, mutate } = useSWR(
        userId ? 'user-data' : null,
        fetcher,
    );
    const email = user?.email;
    const Settings = () => {
        if (fetchError) {
            return (
                <Text>
                    ⚠️ Error fetching user data. Please refresh the page or contact support.
                </Text>
            );
        }
        if (userData) {
            return (
                <UserDataForm
                    userData={userData}
                    mutate={mutate}
                    userId={userId}
                />
            );
        }
        return (
            <Center>
                <Spinner data-testid="no-user-data-spinner" />
            </Center>
        );
    };
    if (!user) {
        return <Spinner data-testid="no-user-spinner" />;
    }
    return (
        <Box>
            <Heading as="h1" textAlign="center" my={4}>
                My Account
            </Heading>
            <SectionHeading size="lg">
                Profile
            </SectionHeading>
            <SectionContainer>
                <FormLabel>Email address</FormLabel>
                <Text ml={2}>{email}</Text>
            </SectionContainer>
            <SectionHeading size="lg">
                Settings
            </SectionHeading>
            <Settings />
            <SectionHeading size="lg">
                Danger Zone
            </SectionHeading>
            <DeleteAccount />
        </Box>
    );
}
Example #11
Source File: index.tsx    From nextjs-hasura-boilerplate with MIT License 5 votes vote down vote up
MyAccountPageComponent = ({ user }) => {
  const [name, setName] = useState(user.name);
  const [session] = useSession();
  const [updateUser, { loading: updateUserFetching, error: updateUserError }] =
    useUpdateUserMutation();

  const handleSubmit = () => {
    updateUser({
      variables: {
        userId: session.id,
        name,
      },
    });
  };

  const errorNode = () => {
    if (!updateUserError) {
      return false;
    }

    return (
      <Alert status="error">
        <AlertIcon />
        <AlertTitle>{updateUserError}</AlertTitle>
        <CloseButton position="absolute" right="8px" top="8px" />
      </Alert>
    );
  };

  return (
    <Stack spacing={8}>
      <Heading>My Account</Heading>
      {errorNode()}
      <Box shadow="lg" rounded="lg" p={4}>
        <Stack spacing={4}>
          <FormControl isRequired>
            <FormLabel htmlFor="name">Name</FormLabel>
            <Input
              type="text"
              id="name"
              value={name}
              onChange={(e: FormEvent<HTMLInputElement>) =>
                setName(e.currentTarget.value)
              }
              isDisabled={updateUserFetching}
            />
          </FormControl>
          <FormControl>
            <Button
              loadingText="Saving..."
              onClick={handleSubmit}
              isLoading={updateUserFetching}
              isDisabled={!name.trim()}
            >
              Save
            </Button>
          </FormControl>
        </Stack>
      </Box>
    </Stack>
  );
}
Example #12
Source File: ControlAttendeeEdit.tsx    From takeout-app with MIT License 5 votes vote down vote up
ControlAttendeeEdit: React.FC<Props> = () => {
  const match = useRouteMatch<{ id: string }>();
  const id = parseInt(match.params.id, 10);
  const { data } = ControlApi.useAttendee(id);
  const [errorAlert, setErrorAlert] = React.useState<JSX.Element | null>(null);
  const [isRequesting, setIsRequesting] = React.useState<boolean>(false);
  const { register, handleSubmit, reset } = useForm<ControlUpdateAttendeeRequestAttendee>({
    defaultValues: {
      name: React.useMemo(() => data?.attendee?.name, [data]),
      is_staff: React.useMemo(() => data?.attendee?.is_staff, [data]),
      is_speaker: React.useMemo(() => data?.attendee?.is_speaker, [data]),
      is_committer: React.useMemo(() => data?.attendee?.is_committer, [data]),
      presentation_slugs: React.useMemo(() => data?.attendee?.presentation_slugs || [], [data]),
    },
  });

  const onSubmit = handleSubmit(async (data) => {
    if (isRequesting) return;
    setIsRequesting(true);
    try {
      await ControlApi.updateAttendee(id, data);
      setErrorAlert(null);
    } catch (e) {
      setErrorAlert(
        <Box my={2}>
          <ErrorAlert error={e} />
        </Box>,
      );
    }
    setIsRequesting(false);
  });

  React.useEffect(() => {
    if (data) reset(data.attendee);
  }, [data]);

  if (!data) return <p>Loading</p>;

  // TODO: link to registration page and support email
  return (
    <>
      {errorAlert}
      <Container mt="20px">
        <form onSubmit={onSubmit}>
          <Text>
            <Link href={data.ticket.admin_url} isExternal textDecoration="underline">
              {data.ticket.reference}
            </Link>
          </Text>
          <FormControl mt={4} id="attendee__name" isRequired>
            <FormLabel>Name</FormLabel>
            <Input {...register("name")} />
          </FormControl>
          <FormControl mt={4} id="attendee__staff" isRequired>
            <FormLabel>Staff</FormLabel>
            <Checkbox {...register("is_staff")} />
          </FormControl>
          <FormControl mt={4} id="attendee__speaker" isRequired>
            <FormLabel>Speaker</FormLabel>
            <Checkbox {...register("is_speaker")} />
          </FormControl>
          <FormControl mt={4} id="attendee__committer" isRequired>
            <FormLabel>Committer</FormLabel>
            <Checkbox {...register("is_committer")} />
          </FormControl>
          <FormControl mt={4} id="attendee__presentation_slugs">
            <FormLabel>Presentation Slugs</FormLabel>
            <Input {...register("presentation_slugs.0")} />
          </FormControl>

          <Button mt={4} size="lg" type="submit" isLoading={isRequesting}>
            Save
          </Button>
        </form>
      </Container>
    </>
  );
}
Example #13
Source File: ControlTrackCardForm.tsx    From takeout-app with MIT License 5 votes vote down vote up
ControlTrackCardForm: React.FC<Props> = ({ track }) => {
  const { data: controlConferenceData } = ControlApi.useConference();
  // const history = useHistory();
  const [errorAlert, setErrorAlert] = React.useState<JSX.Element | null>(null);
  const [isRequesting, setIsRequesting] = React.useState<boolean>(false);

  const { register, handleSubmit, reset } = useForm<{
    slug: ConferencePresentationSlug;
    inSeconds: number;
  }>({
    defaultValues: {
      slug: "",
      inSeconds: 0,
    },
  });

  const onSubmit = handleSubmit(async (data) => {
    if (!controlConferenceData) return;
    if (isRequesting) return;
    setIsRequesting(true);

    try {
      const card: TrackCard = {
        track: track.slug,
        at: dayjs().add(data.inSeconds, "seconds").unix(),
        ut: 0,
        ...generateTrackCardFromPresentation(controlConferenceData, data.slug),
      };
      console.log(card);
      await ControlApi.createTrackCard(card);
      setErrorAlert(null);
    } catch (e) {
      setErrorAlert(
        <Box my={2}>
          <ErrorAlert error={e} />
        </Box>,
      );
    }
    setIsRequesting(false);
    reset();
  });

  if (!controlConferenceData) {
    return <p>Loading</p>;
  }

  //<Input {...register("at", { valueAsDate: true })} type="datetime-local" />

  return (
    <Box>
      {errorAlert}
      <form onSubmit={onSubmit}>
        <FormControl mt={4} id="cardform_slug" isRequired>
          <FormLabel>Name</FormLabel>
          <Input {...register("slug")} />
        </FormControl>

        <FormControl mt={4} id="cardform_at">
          <FormLabel>Activation In</FormLabel>
          <Input {...register("inSeconds", { valueAsNumber: true })} type="number" />
        </FormControl>
        <Button mt={4} size="lg" type="submit" isLoading={isRequesting}>
          Save
        </Button>
      </form>
    </Box>
  );
}
Example #14
Source File: Content.tsx    From calories-in with MIT License 5 votes vote down vote up
function Content({ title, onClose, initialRef, variantFormIndex }: Props) {
  const { register } = useFormContext()
  const nameRegister = register('name')
  const nameInputRef = useMergeRefs(nameRegister.ref, initialRef)

  const onSubmit = useSubmitVariantNameForm({
    variantFormIndex,
    onComplete: onClose,
  })

  const { errorMessage, isInvalid } = useFormError('name')

  useSelectInputText(initialRef)

  return (
    <form onSubmit={onSubmit}>
      <ModalContent>
        <ModalHeader>{title}</ModalHeader>
        <ModalCloseButton />

        <ModalBody>
          <FormControl isInvalid={isInvalid}>
            <FormLabel>Name</FormLabel>
            <Input
              autoComplete="off"
              {...nameRegister}
              ref={nameInputRef}
              focusBorderColor={isInvalid ? 'red.500' : undefined}
              placeholder="Enter name"
            />
            <Collapse animateOpacity={true} in={Boolean(errorMessage)}>
              <Box minHeight="21px">
                <FormErrorMessage>{errorMessage}</FormErrorMessage>
              </Box>
            </Collapse>
          </FormControl>
        </ModalBody>

        <ModalFooter>
          <Button mr={3} onClick={onClose}>
            Close
          </Button>
          <Button
            type="submit"
            colorScheme="teal"
            variant="solid"
            onClick={onSubmit}
          >
            Rename
          </Button>
        </ModalFooter>
      </ModalContent>
    </form>
  )
}
Example #15
Source File: PollIntervalField.tsx    From bluebubbles-server with Apache License 2.0 5 votes vote down vote up
PollIntervalField = ({ helpText }: PollIntervalFieldProps): JSX.Element => {
    const dispatch = useAppDispatch();

    const pollInterval: number = useAppSelector(state => state.config.db_poll_interval) ?? 1000;
    const [newInterval, setNewInterval] = useState(pollInterval);
    const [intervalError, setIntervalError] = useState('');
    const hasIntervalError: boolean = (intervalError?? '').length > 0;

    useEffect(() => { setNewInterval(pollInterval); }, [pollInterval]);

    /**
     * A handler & validator for saving a new poll interval
     *
     * @param theNewInterval - The new interval to save
     */
    const saveInterval = (theNewInterval: number): void => {
        // Validate the interval
        if (theNewInterval < 500) {
            setIntervalError('The interval must be at least 500ms or else database locks can occur');
            return;
        }

        dispatch(setConfig({ name: 'db_poll_interval', value: theNewInterval }));
        if (hasIntervalError) setIntervalError('');
        showSuccessToast({
            id: 'settings',
            duration: 4000,
            description: 'Successfully saved new poll interval! Restarting DB listeners...'
        });
    };

    return (
        <FormControl isInvalid={hasIntervalError}>
            <FormLabel htmlFor='db_poll_interval'>Database Poll Interval (ms)</FormLabel>
            <Flex flexDirection='row' justifyContent='flex-start' alignItems='center'>
                <Input
                    id='db_poll_interval'
                    type='number'
                    maxWidth="5em"
                    value={newInterval}
                    onChange={(e) => {
                        if (hasIntervalError) setIntervalError('');
                        setNewInterval(Number.parseInt(e.target.value));
                    }}
                />
                <IconButton
                    ml={3}
                    verticalAlign='top'
                    aria-label='Save poll interval'
                    icon={<AiOutlineSave />}
                    onClick={() => saveInterval(newInterval)}
                />
            </Flex>
            {!hasIntervalError ? (
                <FormHelperText>
                    {helpText ?? 'Enter how often (in milliseconds) you want the server to check for new messages in the database'}
                </FormHelperText>
            ) : (
                <FormErrorMessage>{intervalError}</FormErrorMessage>
            )}
        </FormControl>
    );
}
Example #16
Source File: ServerPasswordField.tsx    From bluebubbles-server with Apache License 2.0 5 votes vote down vote up
ServerPasswordField = ({ helpText }: ServerPasswordFieldProps): JSX.Element => {
    const dispatch = useAppDispatch();

    const password: string = (useAppSelector(state => state.config.password) ?? '');
    const [showPassword, setShowPassword] = useBoolean();
    const [newPassword, setNewPassword] = useState(password);
    const [passwordError, setPasswordError] = useState('');
    const hasPasswordError: boolean = (passwordError?? '').length > 0;

    useEffect(() => { setNewPassword(password); }, [password]);

    /**
     * A handler & validator for saving a new password.
     *
     * @param theNewPassword - The new password to save
     */
    const savePassword = (theNewPassword: string): void => {
        // Validate the port
        if (theNewPassword.length < 8) {
            setPasswordError('Your password must be at least 8 characters!');
            return;
        } else if (theNewPassword === password) {
            setPasswordError('You have not changed the password since your last save!');
            return;
        }

        dispatch(setConfig({ name: 'password', value: theNewPassword }));
        if (hasPasswordError) setPasswordError('');
        showSuccessToast({
            id: 'settings',
            description: 'Successfully saved new password!'
        });
    };

    return (
        <FormControl isInvalid={hasPasswordError}>
            <FormLabel htmlFor='password'>Server Password</FormLabel>
            <Input
                id='password'
                type={showPassword ? 'text' : 'password'}
                maxWidth="20em"
                value={newPassword}
                onChange={(e) => {
                    if (hasPasswordError) setPasswordError('');
                    setNewPassword(e.target.value);
                }}
            />
            <IconButton
                ml={3}
                verticalAlign='top'
                aria-label='View password'
                icon={showPassword ? <AiFillEye /> : <AiFillEyeInvisible />}
                onClick={() => setShowPassword.toggle()}
            />
            <IconButton
                ml={3}
                verticalAlign='top'
                aria-label='Save password'
                icon={<AiOutlineSave />}
                onClick={() => savePassword(newPassword)}
            />
            {!hasPasswordError ? (
                <FormHelperText>
                    {helpText ?? 'Enter a password to use for clients to authenticate with the server'}
                </FormHelperText>
            ) : (
                <FormErrorMessage>{passwordError}</FormErrorMessage>
            )}
        </FormControl>
    );
}
Example #17
Source File: index.tsx    From calories-in with MIT License 5 votes vote down vote up
function Form({
  ownerName,
  notes,
  onClose,
  initialRef,
  onEditNotes,
  fieldId,
  textAreaHeight,
}: Props) {
  const { register, handleSubmit } = useFormContext()
  const notesRegister = register('notes')
  const notesInputRef = useMergeRefs(notesRegister.ref, initialRef)
  const oneTimeCheckActions = useOneTimeCheckActions()

  const onSubmit = handleSubmit((form: NotesForm) => {
    oneTimeCheckActions.set(`notes-${fieldId}`)
    onEditNotes(form.notes || undefined)
    onClose()
  })

  const { errorMessage, isInvalid } = useFormError('name')

  return (
    <form onSubmit={onSubmit}>
      <ModalContent>
        <Header ownerName={ownerName} notes={notes} />
        <ModalCloseButton />

        <ModalBody>
          <FormControl isInvalid={isInvalid}>
            <FormLabel>Notes</FormLabel>
            <Textarea
              autoComplete="off"
              {...notesRegister}
              ref={notesInputRef}
              focusBorderColor={isInvalid ? 'red.500' : undefined}
              placeholder="Enter notes"
              height={textAreaHeight}
            />
            <Collapse animateOpacity={true} in={Boolean(errorMessage)}>
              <Box minHeight="21px">
                <FormErrorMessage>{errorMessage}</FormErrorMessage>
              </Box>
            </Collapse>
          </FormControl>
        </ModalBody>

        <ModalFooter>
          <Button mr={3} onClick={onClose}>
            Close
          </Button>
          <Button
            type="submit"
            colorScheme="teal"
            variant="solid"
            onClick={onSubmit}
          >
            Save
          </Button>
        </ModalFooter>
      </ModalContent>
    </form>
  )
}
Example #18
Source File: TrackStreamOptionsSelector.tsx    From takeout-app with MIT License 5 votes vote down vote up
TrackStreamOptionsSelector: React.FC<Props> = ({
  track,
  streamOptionsState: [options, setOptions],
  instance,
}) => {
  const handleOnChange = (key: "caption" | "interpretation") => {
    return (e: React.ChangeEvent<HTMLInputElement>) => {
      const newOptions = { ...options, [`${key}`]: e.target.checked };
      setOptions(newOptions);
    };
  };
  const enAndJa = (track.card?.topic?.labels?.indexOf("EN & JA") ?? -1) !== -1;
  const interpretationLabel = enAndJa ? "English track" : "Japanese to English interpretation";
  return (
    <Box>
      <Stack direction={["row", "row", "row", "column"]} spacing={0}>
        <Tooltip label="Caption (English only)" aria-label="">
          <FormControl display="flex" alignItems="center" h="30px">
            <FormLabel htmlFor={`TrackStreamOptions_${instance}__CC`} aria-hidden="true" m={0} mr={1}>
              <ClosedCaptionIcon w="24px" h="24px" />
            </FormLabel>
            <Switch
              aria-label="Closed Caption"
              id={`TrackStreamOptions_${instance}__CC`}
              isChecked={options.caption}
              onChange={handleOnChange("caption")}
            />
          </FormControl>
        </Tooltip>

        {track.interpretation && track.card?.interpretation ? (
          <Tooltip label={interpretationLabel} aria-label="">
            <FormControl display="flex" alignItems="center" h="30px">
              <FormLabel htmlFor={`TrackStreamOptions_${instance}__interpret`} aria-hidden="true" m={0} mr={1}>
                <TranslateIcon w="24px" h="24px" />
              </FormLabel>
              <Switch
                aria-label={interpretationLabel}
                id={`TrackStreamOptions_${instance}__interpret`}
                isChecked={options.interpretation}
                onChange={handleOnChange("interpretation")}
              />
            </FormControl>
          </Tooltip>
        ) : null}
      </Stack>
    </Box>
  );
}
Example #19
Source File: Login.tsx    From takeout-app with MIT License 5 votes vote down vote up
LoginForm: React.FC = () => {
  const history = useHistory();
  const { data: conferenceData } = Api.useConference();
  const [errorAlert, setErrorAlert] = React.useState<JSX.Element | null>(null);
  const [isRequesting, setIsRequesting] = React.useState<boolean>(false);
  const { register, handleSubmit } = useForm<{
    email: string;
    reference: string;
  }>({ defaultValues: { email: "", reference: "" } });

  const onSubmit = handleSubmit(async (data) => {
    if (isRequesting) return;
    setIsRequesting(true);
    try {
      const resp = await Api.createSession(data.email, data.reference);
      setErrorAlert(null);

      if (resp.attendee.is_ready) {
        if (conferenceData) {
          history.push(`/tracks/${encodeURIComponent(conferenceData.conference.default_track)}`);
        } else {
          location.href = "/";
        }
      } else {
        history.push("/attendee");
      }
    } catch (e) {
      setErrorAlert(
        <Box my={2}>
          <ErrorAlert error={e} />
        </Box>,
      );
    }
    setIsRequesting(false);
  });

  // TODO: link to registration page and support email
  return (
    <Box maxW="360px" w="100%">
      <form onSubmit={onSubmit}>
        <FormControl mt={4} id="login_email" isRequired>
          <FormLabel>Email Address</FormLabel>
          <FormHelperText color={Colors.textMuted} my={1}>
            Must be identital to the one registered to your ticket
          </FormHelperText>
          <Input {...register("email")} type="email" autoFocus backgroundColor="white" />
        </FormControl>
        <FormControl mt={4} id="login_reference" isRequired>
          <FormLabel>Ticket ID (Reference Code)</FormLabel>
          <FormHelperText color={Colors.textMuted} my={1}>
            Code should be shown at{" "}
            <Link href="/assets/ticket-email.png" isExternal target="_blank" textDecoration="underline">
              the upper right of the confirmation email
            </Link>{" "}
            you received.
          </FormHelperText>
          <Input
            {...register("reference")}
            type="password"
            placeholder="e.g. ABCD-1, XY1N-10, ..."
            backgroundColor="white"
          />
        </FormControl>
        <Flex direction="row" justifyContent="space-around" w="100%" mt="30px">
          <Button type="submit" w="160px" h="46px" colorScheme="rk" isLoading={isRequesting}>
            Log in
          </Button>
        </Flex>
      </form>
      {errorAlert}
    </Box>
  );
}
Example #20
Source File: index.tsx    From jsonschema-editor-react with Apache License 2.0 5 votes vote down vote up
AdvancedBoolean: React.FunctionComponent<AdvancedItemStateProps> = (
	props: React.PropsWithChildren<AdvancedItemStateProps>
) => {
	const { itemStateProp } = props;

	const item = useState(itemStateProp);

	return (
		<Flex direction="column" wrap="nowrap">
			<Stack
				isInline
				alignItems="center"
				justifyContent="center"
				alignContent="center"
				m={1}
			>
				<FormLabel mr={2} htmlFor="default">
					Default:{" "}
				</FormLabel>
				<Select
					variant="outline"
					value={(item.default.value as string) ?? ""}
					size="sm"
					margin={2}
					placeholder="Choose data type"
					onChange={(evt: React.ChangeEvent<HTMLSelectElement>) => {
						item.default.set(evt.target.value);
					}}
				>
					<option key="true" value="true">
						true
					</option>
					<option key="false" value="false">
						false
					</option>
				</Select>
			</Stack>
		</Flex>
	);
}
Example #21
Source File: ControlLogin.tsx    From takeout-app with MIT License 5 votes vote down vote up
ControlLogin: React.FC<Props> = () => {
  const history = useHistory();
  const [errorAlert, setErrorAlert] = React.useState<JSX.Element | null>(null);
  const [isRequesting, setIsRequesting] = React.useState<boolean>(false);
  const { register, handleSubmit } = useForm<{
    password: string;
  }>({ defaultValues: { password: "" } });

  const onSubmit = handleSubmit(async (data) => {
    if (isRequesting) return;
    setIsRequesting(true);
    try {
      await ControlApi.createControlSession(data.password);
      setErrorAlert(null);

      history.push("/control");
    } catch (e) {
      setErrorAlert(
        <Box my={2}>
          <ErrorAlert error={e} />
        </Box>,
      );
    }
    setIsRequesting(false);
  });

  // TODO: link to registration page and support email
  return (
    <>
      {errorAlert}
      <Container mt="20px">
        <p>Beep beep, beep boop?</p>
        <form onSubmit={onSubmit}>
          <FormControl mt={4} id="login_password" isRequired>
            <FormLabel>Control Password</FormLabel>
            <Input {...register("password")} type="password" />
          </FormControl>
          <Button mt={4} size="lg" type="submit" isLoading={isRequesting}>
            Take control
          </Button>
        </form>
      </Container>
    </>
  );
}
Example #22
Source File: AttendeeEdit.tsx    From takeout-app with MIT License 4 votes vote down vote up
AttendeeEdit: React.FC = () => {
  const { data: conferenceData } = Api.useConference();
  const { data: session, error: sessionError } = Api.useSession();
  const history = useHistory();
  const [errorAlert, setErrorAlert] = React.useState<JSX.Element | null>(null);
  const [isRequesting, setIsRequesting] = React.useState<boolean>(false);

  const { register, handleSubmit, reset } = useForm<{
    name: string;
    gravatar_email: string;
  }>({
    defaultValues: {
      name: React.useMemo(() => session?.attendee?.name, [session]),
      gravatar_email: "",
    },
  });

  React.useEffect(() => {
    if (session?.attendee) reset({ name: session.attendee.name, gravatar_email: "" });
  }, [session?.attendee]);

  const onSubmit = handleSubmit(async (data) => {
    //const wasReady = session!.attendee?.is_ready;

    if (isRequesting) return;
    setIsRequesting(true);
    try {
      await Api.updateAttendee(data.name, data.gravatar_email);
      setErrorAlert(null);

      if (conferenceData) {
        history.push(`/tracks/${encodeURIComponent(conferenceData.conference.default_track)}`);
      } else {
        location.href = "/";
      }
    } catch (e) {
      setErrorAlert(
        <Box my={2}>
          <ErrorAlert error={e} />
        </Box>,
      );
    }
    setIsRequesting(false);
  });

  if (!session?.attendee) {
    return (
      <Container maxW={["auto", "auto", "auto", "1000px"]} px="15px" py="22px">
        <VStack>
          {sessionError ? (
            <Box my={2}>
              <ErrorAlert error={sessionError} />
            </Box>
          ) : null}
          <Spinner size="xl" />
        </VStack>
      </Container>
    );
  }

  return (
    <Container maxW={["auto", "auto", "auto", "1000px"]} px="15px" py="22px">
      <VStack justify="start" alignItems="start" spacing="30px">
        <Heading as="h2" color={Colors.main}>
          Settings
        </Heading>
        <HStack spacing="30px">
          <Avatar size="xl" bg={Colors.defaultAvatarBg} src={session.attendee.avatar_url} loading="lazy" />
          <Box maxW="750px">
            <Text mb={2}>
              Confirm your name and avatar used at live chat. These informations may be shared with other attendees once
              submitted.
            </Text>
            <Text>
              Be remember to abide by{" "}
              <Link href="https://rubykaigi.org/2021-takeout/policies" isExternal textDecoration="underline">
                our policies
              </Link>
              .
            </Text>
          </Box>
        </HStack>
        <form onSubmit={onSubmit}>
          <VStack justify="start" alignItems="start" spacing="30px">
            <FormControl id="login_reference" isRequired>
              <FormLabel>Name</FormLabel>
              <FormHelperText my={1}>Feel free to use nicknames, usernames, or handles :)</FormHelperText>
              <Input {...register("name")} maxW="460px" autoFocus />
            </FormControl>

            <FormControl id="login_email">
              <FormLabel>Gravatar Email Address</FormLabel>
              <FormHelperText my={1}>
                We use avatar images registered on{" "}
                <Link href="https://www.gravatar.com" isExternal textDecoration="underline">
                  Gravatar
                </Link>
                . Fill the following field if you desire to choose different email address for your Gravatar image.
              </FormHelperText>
              <Input
                {...register("gravatar_email")}
                type="email"
                maxW="460px"
                placeholder="(leave empty to remain unchanged)"
              />
            </FormControl>

            <Button type="submit" w="160px" h="46px" colorScheme="rk" isLoading={isRequesting}>
              {session.attendee.is_ready ? "Save" : "Continue"}
            </Button>
          </VStack>
        </form>

        {errorAlert}
      </VStack>
    </Container>
  );
}
Example #23
Source File: index.tsx    From coindrop with GNU General Public License v3.0 4 votes vote down vote up
UserDataForm: FC<UserDataFormProps> = ({ userData, mutate, userId }) => {
    const [isSubmitting, setIsSubmitting] = useState(false);
    const toast = useToast();
    const { register, handleSubmit, formState: { isDirty }, reset } = useForm();
    const email_lists = userData?.email_lists;
    const onSubmit = async (rawFormData) => {
        setIsSubmitting(true);
        const userDataForDb = {
            email_lists: [],
        };
        Object.keys(optionalEmailLists).forEach(emailListId => {
            if (rawFormData.email_lists[emailListId]) {
                userDataForDb.email_lists.push(emailListId);
            }
        });
        try {
            await updateUserData({ data: userDataForDb, userId });
            mutate(userDataForDb);
            reset();
            toast({
                title: "Account updated",
                status: "success",
                duration: 6000,
                isClosable: true,
            });
        } catch (err) {
            toast({
                title: "Error updating account",
                description: "Please try again or contact support",
                status: "error",
                duration: 9000,
                isClosable: true,
            });
        } finally {
            setIsSubmitting(false);
        }
    };
    return (
        <SectionContainer>
            <form onSubmit={handleSubmit(onSubmit)} data-testid="settings-form">
                <SectionHeading size="md">
                    E-mail
                </SectionHeading>
                <Box
                    id="email-preferences-content"
                    m={4}
                >
                    <FormLabel>Newsletters</FormLabel>
                    <Flex wrap="wrap">
                        {Object.entries(optionalEmailLists).map(([emailListId, emailListDisplayName]: [EmailListIds, string]) => {
                            return (
                                <Checkbox
                                    key={emailListId}
                                    mr={6}
                                    name={`email_lists.${emailListId}`}
                                    colorScheme="orange"
                                    defaultChecked={email_lists?.includes(emailListId)}
                                    ref={register()}
                                >
                                    {emailListDisplayName}
                                </Checkbox>
                            );
                        })}
                        {alwaysEnabledEmailLists.map(listName => (
                            <Checkbox
                                key={listName}
                                mr={6}
                                colorScheme="orange"
                                defaultChecked
                                isDisabled
                            >
                                {listName}
                            </Checkbox>
                        ))}
                    </Flex>
                </Box>
                <Box>
                    <Button
                        colorScheme="green"
                        type="submit"
                        isDisabled={!isDirty || isSubmitting}
                        leftIcon={isSubmitting ? <Spinner size="sm" /> : undefined}
                    >
                        {isSubmitting ? 'Saving' : 'Save'}
                    </Button>
                </Box>
            </form>
        </SectionContainer>
    );
}
Example #24
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 #25
Source File: Form.tsx    From ksana.in with Apache License 2.0 4 votes vote down vote up
export function Form() {
  const router = useRouter()
  const { showAlert, hideAlert } = useAlertContext()
  const bgBox = useColorModeValue('white', 'gray.700')

  const [loading, setLoading] = useState<boolean>(false)
  const [errorForm, setErrorForm] = useState<string>('')
  const [email, setEmail] = useState<string>('')

  const handleChangeEmail = (e: ChangeEvent<HTMLInputElement>) => {
    const value = e.target.value
    setEmail(value)
  }

  const checkIsEmpty = () => {
    if (email === '') {
      setErrorForm('Email tidak boleh dikosongkan.')
      return true
    }

    setErrorForm('')
    return false
  }

  const handleResetPassword = async () => {
    const { error } = await forgetPassword({ email })

    if (!error) {
      showAlert({
        title: 'Lupa password',
        message: 'Tautan untuk melakukan setel ulang kata sandi telah dikirim ke email kamu.',
        onClose: () => {
          hideAlert()
          router.push('/')
        }
      })
    }
  }

  const handleSubmit = async () => {
    setLoading(true)

    const isEmpty = checkIsEmpty()

    if (!isEmpty) {
      await handleResetPassword()
    }

    setLoading(false)
  }

  return (
    <Stack spacing={8} mx={'auto'} mt="20" maxW={'lg'} py={12} px={6}>
      <Stack align={'center'}>
        <Heading fontSize={'4xl'} color="orange.400">
          Lupa password
        </Heading>
      </Stack>
      <Box rounded={'lg'} bg={bgBox} boxShadow={'lg'} p={8}>
        <Stack spacing={4}>
          <FormControl id="email" isRequired>
            <FormLabel>Email</FormLabel>
            <Input
              isInvalid={Boolean(errorForm)}
              type="email"
              name="email"
              value={email}
              onChange={handleChangeEmail}
              autoComplete="username"
            />
          </FormControl>

          <Button
            isLoading={loading}
            loadingText="Memproses"
            w="full"
            bg="orange.400"
            _hover={{
              bg: 'orange.500'
            }}
            onClick={handleSubmit}
          >
            Minta reset password
          </Button>
        </Stack>
      </Box>
    </Stack>
  )
}
Example #26
Source File: index.tsx    From calories-in with MIT License 4 votes vote down vote up
function StatFormField(props: Props) {
  const {
    name,
    label,
    inputType,
    isIdented = false,
    nutritionValueUnit = 'g',
    textInputRef,
    isReadOnly = false,
    isEmphasized = false,
    isValueBold = false,
    isCaption = false,
    isRequired,
    children,
    labelDetail,
    dividerProps = {},
    hasDivider = true,
    dailyValuePercent,
    labelElement,
    formLabelProps,
    ...rest
  } = props
  const { errorMessage, isInvalid } = useFormError(name)

  const inputElement = useGetInputElement({
    isInvalid,
    name,
    inputType,
    textInputRef,
    isReadOnly,
    nutritionValueUnit,
    isBold: isValueBold,
  })

  const labelDetailElement = labelDetail ? (
    <Text
      as={isReadOnly ? 'span' : undefined}
      fontSize="sm"
      fontWeight="thin"
      ml={1}
    >
      {labelDetail}
    </Text>
  ) : null

  const isValueNextToLabel = isReadOnly && !(isCaption || isEmphasized)

  return (
    <FormControl
      isInvalid={isInvalid}
      id={name}
      pl={isIdented ? 10 : 0}
      isRequired={!isReadOnly && isRequired}
      {...rest}
    >
      <VStack spacing={2} alignItems="stretch">
        {hasDivider && <Divider {...dividerProps} />}
        <Flex justifyContent={'space-between'} alignItems="center">
          <Flex>
            <FormLabel
              fontWeight={
                isIdented ? 'normal' : isEmphasized ? 'semibold' : 'medium'
              }
              flexShrink={0}
              fontSize={isCaption ? 'lg' : 'md'}
              m={0}
              {...formLabelProps}
            >
              {label || labelElement}
              {isReadOnly && labelDetailElement}
            </FormLabel>
            {!isReadOnly && labelDetailElement}
            {isValueNextToLabel && <Box ml={2}>{inputElement}</Box>}
          </Flex>

          <Flex ml={2} justifyContent="flex-end">
            {!isValueNextToLabel && inputElement}

            {dailyValuePercent !== undefined && isValueNextToLabel && (
              <Text fontWeight="medium">{`${dailyValuePercent}%`}</Text>
            )}

            {!isReadOnly && inputType === 'nutritionValue' && (
              <Flex
                width={9}
                flexShrink={0}
                justifyContent="flex-start"
                alignItems="center"
                ml={1}
              >
                <Text textColor="gray.500">{nutritionValueUnit}</Text>
              </Flex>
            )}
          </Flex>
        </Flex>
      </VStack>

      <Collapse animateOpacity={true} in={Boolean(errorMessage)}>
        <Box minHeight="21px">
          <FormErrorMessage>{errorMessage}</FormErrorMessage>
        </Box>
      </Collapse>

      {children}
    </FormControl>
  )
}
Example #27
Source File: EditPiggybankModal.tsx    From coindrop with GNU General Public License v3.0 4 votes vote down vote up
EditPiggybankModal: FunctionComponent<Props> = ({ isOpen, onClose }) => {
    const [isSubmitting, setIsSubmitting] = useState(false);
    const { colors } = useTheme();
    const { user } = useUser();
    const { colorMode } = useColorMode();
    const accentColorLevelInitial = getAccentColorLevelInitial(colorMode);
    const accentColorLevelHover = getAccentColorLevelHover(colorMode);
    const { push: routerPush, query: { piggybankName } } = useRouter();
    const initialPiggybankId = Array.isArray(piggybankName) ? piggybankName[0] : piggybankName;
    const { piggybankDbData } = useContext(PublicPiggybankDataContext);
    const { avatar_storage_id: currentAvatarStorageId } = piggybankDbData;
    const initialPaymentMethodsDataFieldArray = convertPaymentMethodsDataToFieldArray(piggybankDbData.paymentMethods);
    const initialAccentColor = piggybankDbData.accentColor ?? 'orange';
    const {
        register,
        handleSubmit,
        setValue,
        watch,
        control,
        formState: { isDirty },
    } = useForm({
        defaultValues: {
            piggybankId: initialPiggybankId,
            accentColor: initialAccentColor,
            website: piggybankDbData.website ?? '',
            name: piggybankDbData.name ?? '',
            verb: piggybankDbData.verb ?? 'pay',
            paymentMethods: sortByIsPreferredThenAlphabetical(initialPaymentMethodsDataFieldArray),
        },
    });
    const paymentMethodsFieldArrayName = "paymentMethods";
    const { fields, append, remove } = useFieldArray({
        control,
        name: paymentMethodsFieldArrayName,
    });
    const {
        accentColor: watchedAccentColor,
        piggybankId: watchedPiggybankId,
    } = watch(["accentColor", "piggybankId"]);
    const isAccentColorDirty = initialAccentColor !== watchedAccentColor;
    const isUrlUnchanged = initialPiggybankId === watchedPiggybankId;
    const { isPiggybankIdAvailable, setIsAddressTouched } = useContext(AdditionalValidation);
    const onSubmit = async (formData) => {
        try {
            setIsSubmitting(true);
            const dataToSubmit = {
                ...formData,
                paymentMethods: convertPaymentMethodsFieldArrayToDbMap(formData.paymentMethods ?? []),
                owner_uid: user.id,
                avatar_storage_id: currentAvatarStorageId ?? null,
            };
            if (isUrlUnchanged) {
                await db.collection('piggybanks').doc(initialPiggybankId).set(dataToSubmit);
                mutate(['publicPiggybankData', initialPiggybankId], dataToSubmit);
            } else {
                await axios.post(
                    '/api/createPiggybank',
                    {
                        oldPiggybankName: initialPiggybankId,
                        newPiggybankName: formData.piggybankId,
                        piggybankData: dataToSubmit,
                    },
                    {
                        headers: {
                            token: user.token,
                        },
                    },
                );
                try {
                    await db.collection('piggybanks').doc(initialPiggybankId).delete();
                } catch (err) {
                    console.log('error deleting old Coindrop page');
                }
                routerPush(`/${formData.piggybankId}`);
            }
            fetch(`/${initialPiggybankId}`, { headers: { isToForceStaticRegeneration: "true" }});
            onClose();
        } catch (error) {
            setIsSubmitting(false);
            // TODO: handle errors
            throw error;
        }
    };
    const handleAccentColorChange = (e) => {
        e.preventDefault();
        setValue("accentColor", e.target.dataset.colorname);
    };
    useEffect(() => {
        register("accentColor");
    }, [register]);
    const formControlTopMargin = 2;
    return (
        <Modal
            isOpen={isOpen}
            onClose={onClose}
            size="xl"
            closeOnOverlayClick={false}
        >
            <ModalOverlay />
            <ModalContent>
                <ModalHeader>Configure</ModalHeader>
                <ModalCloseButton />
                <form id="configure-coindrop-form" onSubmit={handleSubmit(onSubmit)}>
                    <ModalBody>
                        <AvatarInput />
                        <FormControl
                            isRequired
                            mt={formControlTopMargin}
                        >
                            <FormLabel htmlFor="input-piggybankId">URL</FormLabel>
                            <EditUrlInput
                                register={register}
                                value={watchedPiggybankId}
                            />
                        </FormControl>
                        <FormControl
                            mt={formControlTopMargin}
                        >
                            <FormLabel
                                htmlFor="input-accentColor"
                            >
                                Theme
                            </FormLabel>
                            <Flex wrap="wrap" justify="center">
                                {themeColorOptions.map(colorName => {
                                    const isColorSelected = watchedAccentColor === colorName;
                                    const accentColorInitial = colors[colorName][accentColorLevelInitial];
                                    const accentColorHover = colors[colorName][accentColorLevelHover];
                                    return (
                                    <Box
                                        key={colorName}
                                        as="button"
                                        bg={isColorSelected ? accentColorHover : accentColorInitial}
                                        _hover={{
                                            bg: accentColorHover,
                                        }}
                                        w="36px"
                                        h="36px"
                                        borderRadius="50%"
                                        mx={1}
                                        my={1}
                                        onClick={handleAccentColorChange}
                                        data-colorname={colorName}
                                    >
                                        {isColorSelected && (
                                            <CheckIcon color="#FFF" />
                                        )}
                                    </Box>
                                    );
                                })}
                            </Flex>
                        </FormControl>
                        <FormControl
                            isRequired
                            mt={formControlTopMargin}
                        >
                            <FormLabel
                                htmlFor="input-name"
                            >
                                Name
                            </FormLabel>
                            <Input
                                id="input-name"
                                name="name"
                                ref={register}
                            />
                        </FormControl>
                        <FormControl
                            isRequired
                            mt={formControlTopMargin}
                        >
                            <FormLabel
                                htmlFor="input-verb"
                            >
                                Payment action name
                            </FormLabel>
                            <Select
                                id="input-verb"
                                name="verb"
                                ref={register}
                            >
                                <option value="pay">Pay</option>
                                <option value="donate to">Donate to</option>
                                <option value="support">Support</option>
                                <option value="tip">Tip</option>
                            </Select>
                        </FormControl>
                        <FormControl
                            mt={formControlTopMargin}
                        >
                            <FormLabel
                                htmlFor="input-website"
                            >
                                Website
                            </FormLabel>
                            <Input
                                id="input-website"
                                name="website"
                                ref={register}
                                placeholder="http://"
                                type="url"
                            />
                        </FormControl>
                        <FormControl
                            mt={formControlTopMargin}
                        >
                            <FormLabel
                                htmlFor="input-paymentmethods"
                            >
                                Payment Methods
                            </FormLabel>
                            <PaymentMethodsInput
                                fields={fields}
                                control={control}
                                register={register}
                                remove={remove}
                                append={append}
                                fieldArrayName={paymentMethodsFieldArrayName}
                            />
                        </FormControl>
                    </ModalBody>
                    <Flex
                        id="modal-footer"
                        justify="space-between"
                        m={6}
                    >
                        <DeleteButton
                            piggybankName={initialPiggybankId}
                        />
                        <Flex>
                            <Button
                                variant="ghost"
                                onClick={onClose}
                            >
                                Cancel
                            </Button>
                            <Button
                                id="save-configuration-btn"
                                colorScheme="green"
                                mx={1}
                                type="submit"
                                isLoading={isSubmitting}
                                loadingText="Saving"
                                isDisabled={
                                    (
                                        !isDirty
                                        && !isAccentColorDirty // controlled accentColor field is not showing up in formState.dirtyFields
                                    )
                                    || !isPiggybankIdAvailable
                                    || !initialPiggybankId
                                }
                                onClick={() => setIsAddressTouched(true)}
                            >
                                Save
                            </Button>
                        </Flex>
                    </Flex>
                </form>
            </ModalContent>
        </Modal>
    );
}
Example #28
Source File: AvatarInput.tsx    From coindrop with GNU General Public License v3.0 4 votes vote down vote up
AvatarInput: FunctionComponent = () => {
    const inputRef = useRef<FileInputRef>(null);
    const { piggybankDbData } = useContext(PublicPiggybankDataContext);
    const currentAvatarStorageId = piggybankDbData.avatar_storage_id;
    const { query: { piggybankName: piggybankNameQuery } } = useRouter();
    const piggybankName = typeof piggybankNameQuery === 'string' ? piggybankNameQuery : piggybankNameQuery[0];
    const { user } = useUser();
    const uid = user?.id;
    const piggybankRef = db.collection('piggybanks').doc(piggybankName);
    const fileSizeError = "Image too large";
    const contentTypeError = "Only images are accepted";
    const imageDimensionsError = "Image height and width must be >= 250px";
    const [fileSelectErrorMessage, setFileSelectErrorMessage] = useState("");
    function clearInput() { inputRef.current.value = null; }
    const setAvatar = async (newAvatarStorageId) => {
      Promise.all([
        piggybankRef.set({ avatar_storage_id: newAvatarStorageId }, { merge: true }),
        deleteImage({
          storageId: currentAvatarStorageId,
          ownerUid: uid,
          piggybankName,
        }),
      ]);
      mutate(['publicPiggybankData', piggybankName], { ...piggybankDbData, avatar_storage_id: newAvatarStorageId });
    };
    const onInputChange = async (event: React.ChangeEvent<HTMLInputElement>) => {
      setFileSelectErrorMessage(null);
      const file = event?.target?.files?.[0];
      const storageRef = storage.ref();
      const newAvatarStorageId = uuidV4();
      const newAvatarPath = piggybankImageStoragePath({ ownerUid: uid, piggybankName, imageAs: "avatar", imageStorageId: newAvatarStorageId });
      const newAvatarRef = storageRef.child(newAvatarPath);
      if (file) {
        try {
          const contentType = file.type;
          if (!contentType.startsWith("image/")) {
            throw new Error(contentTypeError);
          }
          if (file.size > 1000000) {
            throw new Error(fileSizeError);
          }
          const { width, height } = await getImageDimensions(file);
          if (width < 250 || height < 250) {
            throw new Error(imageDimensionsError);
          }
          await newAvatarRef.put(file);
          clearInput();
          setAvatar(newAvatarStorageId);
        } catch (err) {
          const { message } = err;
          if (message === fileSizeError) {
            setFileSelectErrorMessage("Image too large. Please resize image to < 1MB.");
          } else if (message === contentTypeError) {
            setFileSelectErrorMessage("Only .jpg, .png, and .webp images are accepted.");
          } else if (message === imageDimensionsError) {
            setFileSelectErrorMessage("Width and height must be at least 250px");
          } else {
            setFileSelectErrorMessage("Error during upload. Please try again.");
          }
          clearInput();
        }
      }
    };
    return (
      <>
        <FormLabel htmlFor="avatar-input">Image</FormLabel>
        <Stack id="avatar-input-container">
          <Box mx="auto">
            {
              currentAvatarStorageId
              ? <Avatar />
              : (
                <NextImage
                  id="avatar-img"
                  width={200}
                  height={200}
                  src="/avatar-placeholder.png"
                  alt="avatar placeholder"
                  data-cy="avatar-placeholder"
                />
              )
            }
          </Box>
          <Center>
            <Flex wrap="wrap" justify="center">
              <Box mt={1}>
                <FileInput
                  text={currentAvatarStorageId ? "Upload new image" : "Upload image"}
                  id="avatar-input"
                  ref={inputRef}
                  accept="image/png, image/jpeg, image/webp"
                  onChange={onInputChange}
                />
              </Box>
              {currentAvatarStorageId && (
                <Box ml={2} mt={1}>
                  <Button
                    onClick={() => {
                      setAvatar(null);
                    }}
                    colorScheme="red"
                    size="sm"
                    leftIcon={<DeleteIcon />}
                    data-cy="remove-image-btn"
                  >
                    Remove image
                  </Button>
                </Box>
              )}
            </Flex>
          </Center>
          {fileSelectErrorMessage && (
            <Text textAlign="center" color="red.500">
              <WarningIcon mr={2} />
              {fileSelectErrorMessage}
            </Text>
          )}
        </Stack>
      </>
    );
}
Example #29
Source File: PaddingPanel.tsx    From openchakra with MIT License 4 votes vote down vote up
PaddingPanel = ({ type }: PaddingPanelPropsType) => {
  const { setValueFromEvent } = useForm()

  const all = usePropsSelector(ATTRIBUTES[type].all)
  const left = usePropsSelector(ATTRIBUTES[type].left)
  const right = usePropsSelector(ATTRIBUTES[type].right)
  const bottom = usePropsSelector(ATTRIBUTES[type].bottom)
  const top = usePropsSelector(ATTRIBUTES[type].top)

  return (
    <Box mb={4}>
      <FormControl>
        <FormLabel fontSize="xs" htmlFor="width" textTransform="capitalize">
          {type}
        </FormLabel>

        <InputGroup size="sm">
          <Input
            mb={1}
            placeholder="All"
            size="sm"
            type="text"
            name={ATTRIBUTES[type].all}
            value={all || ''}
            onChange={setValueFromEvent}
          />
        </InputGroup>

        <SimpleGrid columns={2} spacing={1}>
          <InputGroup size="sm">
            <InputLeftElement
              children={
                <ArrowBackIcon path="" fontSize="md" color="gray.300" />
              }
            />
            <Input
              placeholder="left"
              size="sm"
              type="text"
              name={ATTRIBUTES[type].left}
              value={left || ''}
              onChange={setValueFromEvent}
              autoComplete="off"
            />
          </InputGroup>

          <InputGroup size="sm">
            <InputLeftElement
              children={
                <ArrowForwardIcon path="" fontSize="md" color="gray.300" />
              }
            />
            <Input
              placeholder="right"
              size="sm"
              type="text"
              value={right || ''}
              name={ATTRIBUTES[type].right}
              onChange={setValueFromEvent}
              autoComplete="off"
            />
          </InputGroup>

          <InputGroup size="sm">
            <InputLeftElement
              children={<ArrowUpIcon path="" fontSize="md" color="gray.300" />}
            />
            <Input
              placeholder="top"
              size="sm"
              type="text"
              value={top || ''}
              name={ATTRIBUTES[type].top}
              onChange={setValueFromEvent}
              autoComplete="off"
            />
          </InputGroup>

          <InputGroup size="sm">
            <InputLeftElement
              children={
                <ChevronDownIcon path="" fontSize="md" color="gray.300" />
              }
            />
            <Input
              placeholder="bottom"
              size="sm"
              type="text"
              value={bottom || ''}
              name={ATTRIBUTES[type].bottom}
              onChange={setValueFromEvent}
              autoComplete="off"
            />
          </InputGroup>
        </SimpleGrid>
      </FormControl>
    </Box>
  )
}