@chakra-ui/react#FormHelperText TypeScript Examples

The following examples show how to use @chakra-ui/react#FormHelperText. 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: AutoInstallUpdatesField.tsx    From bluebubbles-server with Apache License 2.0 6 votes vote down vote up
AutoInstallUpdatesField = ({ helpText }: AutoInstallUpdatesFieldProps): JSX.Element => {
    const autoInstall: boolean = (useAppSelector(state => state.config.auto_install_updates) ?? false);

    return (
        <FormControl>
            <Checkbox id='auto_install_updates' isChecked={autoInstall} onChange={onCheckboxToggle}>Auto Install / Apply Updates</Checkbox>
            <FormHelperText>
                {helpText ?? (
                    <Text>
                        When enabled, BlueBubbles will auto-install the latest available version when an update is detected
                    </Text>
                )}
            </FormHelperText>
        </FormControl>
    );
}
Example #2
Source File: AutoStartField.tsx    From bluebubbles-server with Apache License 2.0 6 votes vote down vote up
AutoStartField = ({ helpText }: AutoStartFieldProps): JSX.Element => {
    const autoStart: boolean = (useAppSelector(state => state.config.auto_start) ?? false);

    return (
        <FormControl>
            <Checkbox id='auto_start' isChecked={autoStart}  onChange={onCheckboxToggle}>Startup with macOS</Checkbox>
            <FormHelperText>
                {helpText ?? (
                    <Text>
                        When enabled, BlueBubbles will start automatically when you login.
                    </Text>
                )}
            </FormHelperText>
        </FormControl>
    );
}
Example #3
Source File: CheckForUpdatesField.tsx    From bluebubbles-server with Apache License 2.0 6 votes vote down vote up
CheckForUpdatesField = ({ helpText }: CheckForUpdatesFieldProps): JSX.Element => {
    const checkForUpdates: boolean = (useAppSelector(state => state.config.check_for_updates) ?? false);

    return (
        <FormControl>
            <Checkbox id='check_for_updates' isChecked={checkForUpdates} onChange={onCheckboxToggle}>Check for Updates on Startup</Checkbox>
            <FormHelperText>
                {helpText ?? (
                    <Text>
                        When enabled, BlueBubbles will automatically check for updates on startup
                    </Text>
                )}
            </FormHelperText>
        </FormControl>
    );
}
Example #4
Source File: EncryptCommunicationsField.tsx    From bluebubbles-server with Apache License 2.0 6 votes vote down vote up
EncryptCommunicationsField = ({ helpText }: EncryptCommunicationsFieldProps): JSX.Element => {
    const encryption: boolean = (useAppSelector(state => state.config.encrypt_coms) ?? false);

    return (
        <FormControl>
            <Checkbox id='encrypt_coms' isChecked={encryption} onChange={onCheckboxToggle}>Encrypt Messages</Checkbox>
            <FormHelperText>
                {helpText ?? 'Enabling this will add an additional layer of security to the app communications by encrypting messages with a password-based AES-256-bit algorithm'}
            </FormHelperText>
        </FormControl>
    );
}
Example #5
Source File: AutoCaffeinateField.tsx    From bluebubbles-server with Apache License 2.0 6 votes vote down vote up
AutoCaffeinateField = ({ helpText }: AutoCaffeinateFieldProps): JSX.Element => {
    const keepAwake: boolean = (useAppSelector(state => state.config.auto_caffeinate) ?? false);

    return (
        <FormControl>
            <Checkbox id='auto_caffeinate' isChecked={keepAwake}  onChange={onCheckboxToggle}>Keep macOS Awake</Checkbox>
            <FormHelperText>
                {helpText ?? (
                    <Text>
                        When enabled, you mac will not fall asleep due to inactivity or a screen screen saver.
                        However, your computer lid's close action may override this.
                        Make sure your computer does not go to sleep when the lid is closed.
                    </Text>
                )}
            </FormHelperText>
        </FormControl>
    );
}
Example #6
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 #7
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 #8
Source File: OledDarkThemeField.tsx    From bluebubbles-server with Apache License 2.0 6 votes vote down vote up
UseOledDarkModeField = ({ helpText }: UseOledDarkModeFieldProps): JSX.Element => {
    const oledDark: boolean = (useAppSelector(state => state.config.use_oled_dark_mode) ?? false);

    return (
        <FormControl>
            <Checkbox id='use_oled_dark_mode' isChecked={oledDark} onChange={onCheckboxToggle}>Use OLED Black Dark Mode</Checkbox>
            <FormHelperText>
                {helpText ?? (
                    <Text>
                        Enabling this will set the dark mode theme to OLED black
                    </Text>
                )}
            </FormHelperText>
        </FormControl>
    );
}
Example #9
Source File: UseHttpsField.tsx    From bluebubbles-server with Apache License 2.0 6 votes vote down vote up
UseHttpsField = ({ helpText }: UseHttpsFieldProps): JSX.Element => {
    const useHttps: boolean = (useAppSelector(state => state.config.use_custom_certificate) ?? false);

    return (
        <FormControl>
            <Checkbox id='use_custom_certificate' isChecked={useHttps} onChange={onCheckboxToggle}>Use Custom Certificate</Checkbox>
            <FormHelperText>
                {helpText ?? (
                    <Text>
                        This will install a self-signed certificate at: <Code>~/Library/Application Support/bluebubbles-server/Certs</Code>
                        <br />
                        Note: Only use this this option if you have your own certificate! Replace the certificates in the <Code>Certs</Code> directory
                    </Text>
                )}
            </FormHelperText>
        </FormControl>
    );
}
Example #10
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 #11
Source File: FeatureSettings.tsx    From bluebubbles-server with Apache License 2.0 5 votes vote down vote up
FeatureSettings = (): JSX.Element => {
    const hideDockIcon = (useAppSelector(state => state.config.hide_dock_icon) ?? false);
    const useTerminal = (useAppSelector(state => state.config.start_via_terminal) ?? false);
    return (
        <section>
            <Stack direction='column' p={5}>
                <Text fontSize='2xl'>Features</Text>
                <Divider orientation='horizontal' />
                <Spacer />
                <PrivateApiField />
                <Spacer />
                <AutoCaffeinateField />
                <Spacer />
                <AutoStartField />
                <Spacer />

                <FormControl>
                    <Checkbox id='hide_dock_icon' isChecked={hideDockIcon} onChange={onCheckboxToggle}>Hide Dock Icon</Checkbox>
                    <FormHelperText>
                        Hiding the dock icon will not close the app. You can open the app again via the status bar icon.
                    </FormHelperText>
                </FormControl>

                <Spacer />
                <Accordion allowMultiple>
                    <AccordionItem>
                        <AccordionButton>
                            <Box flex='1' textAlign='left' width="15em">
                                Advanced Feature Settings
                            </Box>
                            <AccordionIcon />
                        </AccordionButton>
                        <AccordionPanel pb={4}>
                            <FormControl>
                                <Checkbox id='start_via_terminal' isChecked={useTerminal} onChange={onCheckboxToggle}>Always Start via Terminal</Checkbox>
                                <FormHelperText>
                                    When BlueBubbles starts up, it will auto-reload itself in terminal mode.
                                    When in terminal, type "help" for command information.
                                </FormHelperText>
                            </FormControl>
                        </AccordionPanel>
                    </AccordionItem>
                </Accordion>
            </Stack>
        </section>
    );
}
Example #12
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 #13
Source File: PrivateApiField.tsx    From bluebubbles-server with Apache License 2.0 5 votes vote down vote up
PrivateApiField = ({ helpText }: PrivateApiFieldProps): JSX.Element => {
    const privateApi: boolean = (useAppSelector(state => state.config.enable_private_api) ?? false);
    const alertRef = useRef(null);
    const [requiresConfirmation, confirm] = useState((): string | null => {
        return null;
    });

    return (
        <Box mt={1}>
            <PrivateApiRequirements />

            <FormControl mt={5}>
                <Stack direction='row'>
                    <Checkbox
                        id='enable_private_api'
                        isChecked={privateApi}
                        onChange={onCheckboxToggle}
                    >
                        Private API
                    </Checkbox>
                    <Button
                        size='xs'
                        onClick={() => confirm('reinstall')}
                    >
                        Re-install Helper
                    </Button>
                </Stack>
                <FormHelperText>
                    {helpText ?? (
                        <Text>
                            If you have set up the Private API features (via MacForge or MySIMBL),
                            enable this option to allow the server to communicate with the iMessage Private API.
                        </Text>
                    )}
                </FormHelperText>
            </FormControl>

            <ConfirmationDialog
                modalRef={alertRef}
                onClose={() => confirm(null)}
                body={confirmationActions[requiresConfirmation as string]?.message}
                onAccept={() => {
                    confirmationActions[requiresConfirmation as string].func();
                }}
                isOpen={requiresConfirmation !== null}
            />
        </Box>
    );
}
Example #14
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 #15
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 #16
Source File: ProxyServiceField.tsx    From bluebubbles-server with Apache License 2.0 4 votes vote down vote up
ProxyServiceField = ({ helpText, showAddress = true }: ProxyServiceFieldProps): JSX.Element => {
    const dispatch = useAppDispatch();
    const dnsRef = useRef(null);
    const alertRef = useRef(null);
    const proxyService: string = (useAppSelector(state => state.config.proxy_service) ?? '').toLowerCase().replace(' ', '-');
    const address: string = useAppSelector(state => state.config.server_address) ?? '';
    const port: number = useAppSelector(state => state.config.socket_port) ?? 1234;
    const [dnsModalOpen, setDnsModalOpen] = useBoolean();
    const [requiresConfirmation, confirm] = useState((): string | null => {
        return null;
    });
    return (
        <FormControl>
            <FormLabel htmlFor='proxy_service'>Proxy Service</FormLabel>
            <Flex flexDirection='row' justifyContent='flex-start' alignItems='center'>
                <Select
                    id='proxy_service'
                    placeholder='Select Proxy Service'
                    maxWidth="15em"
                    mr={3}
                    value={proxyService}
                    onChange={(e) => {
                        if (!e.target.value || e.target.value.length === 0) return;
                        onSelectChange(e);
                        if (e.target.value === 'dynamic-dns') {
                            setDnsModalOpen.on();
                        } else if (e.target.value === 'cloudflare') {
                            confirm('confirmation');
                        }
                    }}
                >
                    <option value='ngrok'>Ngrok</option>
                    <option value='cloudflare'>Cloudflare</option>
                    <option value='dynamic-dns'>Dynamic DNS</option>
                </Select>
                {(proxyService === 'dynamic-dns')
                    ? (
                        <IconButton
                            mr={3}
                            aria-label='Set address'
                            icon={<AiOutlineEdit />}
                            onClick={() => setDnsModalOpen.on()}
                        />
                    ) : null}
                {(showAddress) ? (
                    <>
                        <Text fontSize="md" color="grey">Address: {address}</Text>
                        <IconButton
                            ml={3}
                            aria-label='Copy address'
                            icon={<BiCopy />}
                            onClick={() => copyToClipboard(address)}
                        />
                    </>
                ) : null}
            </Flex>
            <FormHelperText>
                {helpText ?? 'Select a proxy service to use to make your server internet-accessible. Without one selected, your server will only be accessible on your local network'}
            </FormHelperText>

            <DynamicDnsDialog
                modalRef={dnsRef}
                onConfirm={(address) => dispatch(setConfig({ name: 'server_address', value: address }))}
                isOpen={dnsModalOpen}
                port={port as number}
                onClose={() => setDnsModalOpen.off()}
            />

            <ConfirmationDialog
                title="Notice"
                modalRef={alertRef}
                onClose={() => confirm(null)}
                body={confirmationActions[requiresConfirmation as string]?.message}
                acceptText="OK"
                declineText={null}
                onAccept={() => {
                    confirmationActions[requiresConfirmation as string].func();
                }}
                isOpen={requiresConfirmation !== null}
            />
        </FormControl>
    );
}
Example #17
Source File: NgrokAuthTokenField.tsx    From bluebubbles-server with Apache License 2.0 4 votes vote down vote up
NgrokAuthTokenField = ({ helpText }: NgrokAuthTokenFieldProps): JSX.Element => {
    const dispatch = useAppDispatch();
    const ngrokToken: string = (useAppSelector(state => state.config.ngrok_key) ?? '');
    const [showNgrokToken, setShowNgrokToken] = useBoolean();
    const [newNgrokToken, setNewNgrokToken] = useState(ngrokToken);
    const [ngrokTokenError, setNgrokTokenError] = useState('');
    const hasNgrokTokenError: boolean = (ngrokTokenError ?? '').length > 0;

    useEffect(() => { setNewNgrokToken(ngrokToken); }, [ngrokToken]);

    /**
     * A handler & validator for saving a new Ngrok auth token.
     *
     * @param theNewNgrokToken - The new auth token to save
     */
    const saveNgrokToken = (theNewNgrokToken: string): void => {
        theNewNgrokToken = theNewNgrokToken.trim();

        // Validate the port
        if (theNewNgrokToken === ngrokToken) {
            setNgrokTokenError('You have not changed the token since your last save!');
            return;
        } else if (theNewNgrokToken.includes(' ')) {
            setNgrokTokenError('Invalid Ngrok Auth Token! Please check that you have copied it correctly.');
            return;
        }

        dispatch(setConfig({ name: 'ngrok_key', value: theNewNgrokToken }));
        setNgrokTokenError('');
        showSuccessToast({
            id: 'settings',
            duration: 4000,
            description: 'Successfully saved new Ngrok Auth Token! Restarting Proxy service...'
        });
    };

    return (
        <FormControl isInvalid={hasNgrokTokenError}>
            <FormLabel htmlFor='ngrok_key'>Ngrok Auth Token (Optional)</FormLabel>
            <Input
                id='password'
                type={showNgrokToken ? 'text' : 'password'}
                maxWidth="20em"
                value={newNgrokToken}
                onChange={(e) => {
                    if (hasNgrokTokenError) setNgrokTokenError('');
                    setNewNgrokToken(e.target.value);
                }}
            />
            <IconButton
                ml={3}
                verticalAlign='top'
                aria-label='View Ngrok token'
                icon={showNgrokToken ? <AiFillEye /> : <AiFillEyeInvisible />}
                onClick={() => setShowNgrokToken.toggle()}
            />
            <IconButton
                ml={3}
                verticalAlign='top'
                aria-label='Save Ngrok token'
                icon={<AiOutlineSave />}
                onClick={() => saveNgrokToken(newNgrokToken)}
            />
            {!hasNgrokTokenError ? (
                <FormHelperText>
                    {helpText ?? (
                        <Text>
                            Using an Auth Token will allow you to use the benefits of the upgraded Ngrok
                            service. This can improve connection stability and reliability. If you do not have
                            an Ngrok Account, sign up for free here:&nbsp;
                            <Box as='span' color={baseTheme.colors.brand.primary}>
                                <Link href='https://dashboard.ngrok.com/get-started/your-authtoken' target='_blank'>ngrok.com</Link>
                            </Box>
                        </Text>
                    )}
                </FormHelperText>
            ) : (
                <FormErrorMessage>{ngrokTokenError}</FormErrorMessage>
            )}
        </FormControl>
    );
}
Example #18
Source File: index.tsx    From ksana.in with Apache License 2.0 4 votes vote down vote up
export function UrlForm({ user, onSuccess }: IUrlFormProps) {
  const { showAlert, hideAlert } = useAlertContext()

  const [url, setUrl] = useState<string>('')
  const [slug, setSlug] = useState<string>('')
  const [isCheckPass, setIsCheckPass] = useState<boolean>(false)
  const [isDynamic, setIsDynamic] = useState<boolean>(false)
  const [errorUrl, setErrorUrl] = useState<boolean | string>(false)
  const [errorSlug, setErrorSlug] = useState<boolean | string>(false)
  const [loading, setLoading] = useState<boolean>(false)

  const handleChangeUrl = (e: ChangeEvent<HTMLInputElement>) => {
    const value = e.target.value
    setUrl(value)
    setErrorUrl('')
  }

  const handleChangeSlug = (e: ChangeEvent<HTMLInputElement>) => {
    const value = e.target.value
    setSlug(value)
    setErrorSlug('')
  }

  const handleChangeIsDynamic = (e: ChangeEvent<HTMLInputElement>) => {
    const value = e.target.checked
    setIsDynamic(value)
  }

  const resetErrorMessage = () => {
    setErrorUrl('')
    setErrorSlug('')
  }

  const checkIsEmpty = () => {
    if (url === '') {
      setErrorUrl('URL dan slug tidak bisa dikosongkan')
      return true
    }

    if (url.indexOf('http://') === -1 && url.indexOf('https://') === -1) {
      setErrorUrl('Pastikan URL dimulai dengan http:// atau https://')
      return true
    }

    if (slug === '') {
      setErrorSlug('URL dan slug tidak bisa dikosongkan')
      return true
    }

    return false
  }

  const checkParamRequired = () => {
    const params = url.match(/{param}/g) || []

    if (isDynamic && !params.length) {
      setErrorUrl('Tautan dinamis membutuhkan teks {param} di dalamnya')
      return false
    }

    if (isDynamic && params.length > 1) {
      setErrorUrl('Teks {param} cukup satu saja')
      return false
    }

    return true
  }

  const handleCheckAvailability = async () => {
    setLoading(true)
    resetErrorMessage()
    const isEmpty = checkIsEmpty()
    const hasParam = checkParamRequired()
    if (!isEmpty && hasParam) {
      const response = await checkSlug({ slug: sanitizeSlug(slug) })
      if (response.error) {
        setIsCheckPass(true)
        resetErrorMessage()
      } else {
        setErrorSlug(`Slug ${slug} telah digunakan, coba slug lain`)
      }
    }
    setLoading(false)
  }

  const handleSaveNew = async () => {
    setLoading(true)
    const isEmpty = checkIsEmpty()
    if (!isEmpty) {
      const { error: errorInsert } = await saveUrl({
        url: url,
        slug: sanitizeSlug(slug),
        is_dynamic: isDynamic,
        userId: user?.id
      })

      if (!errorInsert) {
        showAlert({
          title: 'Sukses menyimpan tautan baru',
          message: 'Tautan telah disimpan dalam basis data kami, silahkan mulai bagikan',
          onClose: () => {
            hideAlert()
            mutate(apiUrlsGet(user?.id))
            setUrl('')
            setSlug('')
            setIsCheckPass(false)
            resetErrorMessage()
            onSuccess()
          }
        })
      } else {
        showAlert({
          title: 'Terjadi galat pada saat berusaha menyimpan data',
          message: `Pesan: ${errorInsert.message}`,
          onClose: () => {
            hideAlert()
            setIsCheckPass(false)
            resetErrorMessage()
          }
        })
      }
    }
    setLoading(false)
  }

  return (
    <Box width={{ base: '100%' }}>
      <Stack spacing={4} direction={{ base: 'column' }}>
        <FormControl id="url" isRequired>
          <Input
            isRequired
            isInvalid={Boolean(errorUrl)}
            size="lg"
            name="url"
            placeholder={'Tautan yang akan dipercantik'}
            variant="filled"
            value={url}
            onChange={handleChangeUrl}
          />
          {errorUrl && <FormHelperText color="red.500">Error: {errorUrl}</FormHelperText>}
          <FormHelperText>
            Membutuhkan tautan dalam bentuk utuh, termasuk awalan https://
          </FormHelperText>
          {isDynamic && (
            <FormHelperText>
              Sisipkan teks <code>{'{param}'}</code> pada tautan
            </FormHelperText>
          )}
        </FormControl>

        <FormControl display="flex" alignItems="center">
          <FormLabel htmlFor="is-dynamic" mb="0" display="flex">
            Tautan dinamis{' '}
            <Tooltip
              label="Kamu bisa membuat tautan dinamis macam: https://mazipan.space/{param}"
              placement="bottom"
            >
              <i>
                <HiQuestionMarkCircle />
              </i>
            </Tooltip>
          </FormLabel>
          <Switch id="is-dynamic" onChange={handleChangeIsDynamic} />
        </FormControl>

        <FormControl id="slug" isRequired>
          <InputGroup size="lg">
            <InputLeftAddon
              color={'orange.400'}
              fontWeight="bold"
              px={2}
              children={HOME?.replace('https://', '').replace('http://', '')}
              fontSize="xs"
            />
            <Input
              isRequired
              isInvalid={Boolean(errorSlug)}
              size="lg"
              name="slug"
              placeholder={'Slug cantik dambaanmu'}
              variant="filled"
              value={slug}
              onChange={handleChangeSlug}
            />
          </InputGroup>
          {errorSlug && <FormHelperText color="red.500">Error: {errorSlug}</FormHelperText>}
          <FormHelperText>
            Hanya diperbolehkan menggunakan huruf, angka, karakter titik dan strip saja
          </FormHelperText>
        </FormControl>

        {isCheckPass ? (
          <Button
            isLoading={loading}
            loadingText="Processing"
            size="lg"
            px={6}
            mt="4"
            color={'white'}
            bg={'green.400'}
            _hover={{
              bg: 'green.500'
            }}
            _focus={{
              bg: 'green.500'
            }}
            onClick={handleSaveNew}
          >
            Simpan sekarang
          </Button>
        ) : (
          <Button
            isLoading={loading}
            loadingText="Processing"
            size="lg"
            px={6}
            my="4"
            color={'white'}
            bg={'orange.400'}
            _hover={{
              bg: 'orange.500'
            }}
            _focus={{
              bg: 'orange.500'
            }}
            onClick={handleCheckAvailability}
          >
            Cek ketersediaan
          </Button>
        )}
      </Stack>
    </Box>
  )
}
Example #19
Source File: Inspector.tsx    From openchakra with MIT License 4 votes vote down vote up
Inspector = () => {
  const dispatch = useDispatch()
  const component = useSelector(getSelectedComponent)
  const { isOpen, onOpen, onClose } = useDisclosure()
  const [componentName, onChangeComponentName] = useState('')
  const componentsNames = useSelector(getComponentNames)

  const { clearActiveProps } = useInspectorUpdate()

  const saveComponent = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault()
    dispatch.components.setComponentName({
      componentId: component.id,
      name: componentName,
    })
    onClose()
    onChangeComponentName('')
  }
  const isValidComponentName = useMemo(() => {
    return (
      !!componentName.match(/^[A-Z]\w*$/g) &&
      !componentsNames.includes(componentName) &&
      // @ts-ignore
      !componentsList.includes(componentName)
    )
  }, [componentName, componentsNames])

  const { type, rootParentType, id, children } = component

  const isRoot = id === 'root'
  const parentIsRoot = component.parent === 'root'

  const docType = rootParentType || type
  const componentHasChildren = children.length > 0

  useEffect(() => {
    clearActiveProps()
  }, [clearActiveProps])

  return (
    <>
      <Box bg="white">
        <Box
          fontWeight="semibold"
          fontSize="md"
          color="yellow.900"
          py={2}
          px={2}
          boxShadow="sm"
          bg="yellow.100"
          display="flex"
          justifyContent="space-between"
          flexDir="column"
        >
          {isRoot ? 'Document' : type}
          {!!component.componentName && (
            <Text fontSize="xs" fontWeight="light">
              {component.componentName}
            </Text>
          )}
        </Box>
        {!isRoot && (
          <Stack
            direction="row"
            py={2}
            spacing={2}
            align="center"
            zIndex={99}
            px={2}
            flexWrap="wrap"
            justify="flex-end"
          >
            <CodeActionButton />
            {!component.componentName && (
              <ActionButton
                label="Name component"
                icon={<EditIcon path="" />}
                onClick={onOpen}
              />
            )}
            <ActionButton
              label="Duplicate"
              onClick={() => dispatch.components.duplicate()}
              icon={<CopyIcon path="" />}
            />
            <ActionButton
              label="Reset props"
              icon={<IoMdRefresh />}
              onClick={() => dispatch.components.resetProps(component.id)}
            />
            <ActionButton
              label="Chakra UI Doc"
              as={Link}
              onClick={() => {
                window.open(
                  `https://chakra-ui.com/${docType.toLowerCase()}`,
                  '_blank',
                )
              }}
              icon={<GoRepo />}
            />
            <ActionButton
              bg="red.500"
              label="Remove"
              onClick={() => dispatch.components.deleteComponent(component.id)}
              icon={<FiTrash2 />}
            />
          </Stack>
        )}
      </Box>

      <Box pb={1} bg="white" px={3}>
        <Panels component={component} isRoot={isRoot} />
      </Box>

      <StylesPanel
        isRoot={isRoot}
        showChildren={componentHasChildren}
        parentIsRoot={parentIsRoot}
      />
      <Modal onClose={onClose} isOpen={isOpen} isCentered>
        <ModalOverlay>
          <ModalContent>
            <form onSubmit={saveComponent}>
              <ModalHeader>Save this component</ModalHeader>
              <ModalCloseButton />
              <ModalBody>
                <FormControl isInvalid={!isValidComponentName}>
                  <FormLabel>Component name</FormLabel>
                  <Input
                    size="md"
                    autoFocus
                    variant="outline"
                    isFullWidth
                    focusBorderColor="blue.500"
                    errorBorderColor="red.500"
                    value={componentName}
                    onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
                      onChangeComponentName(e.target.value)
                    }
                  />
                  {!isValidComponentName && (
                    <FormErrorMessage>
                      Component name must start with a capital character and
                      must not contain space or special character, and name
                      should not be already taken (including existing chakra-ui
                      components).
                    </FormErrorMessage>
                  )}
                  <FormHelperText>
                    This will name your component that you will see in the code
                    panel as a separated component.
                  </FormHelperText>
                </FormControl>
              </ModalBody>
              <ModalFooter>
                <Button
                  colorScheme="blue"
                  mr={3}
                  type="submit"
                  isDisabled={!isValidComponentName}
                >
                  Save
                </Button>
                <Button onClick={onClose}>Cancel</Button>
              </ModalFooter>
            </form>
          </ModalContent>
        </ModalOverlay>
      </Modal>
    </>
  )
}
Example #20
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>
  );
}