formik#Field TypeScript Examples

The following examples show how to use formik#Field. 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: form-field.component.tsx    From MyWay-client with MIT License 6 votes vote down vote up
export default function FormField({
  children,
  name,
  type = "text",
  placeholder = "",
  as = null,
}: {
  children?: ReactNode | undefined;
  name: string;
  type?: string;
  placeholder?: string;
  as?: null | string;
}) {
  return (
    <Field
      name={name}
      type={type}
      placeholder={placeholder}
      as={as}
      className="w-full my-2"
    >
      {children}
    </Field>
  );
}
Example #2
Source File: SignInProject.view.tsx    From tezos-link with Apache License 2.0 6 votes vote down vote up
SignInProjectView = ({ handleSubmitForm }: NewPostViewProps) => (
  <SignInProjectCard>
    <h1>Sign In</h1>
    <Formik
      initialValues={{
        uuid: ''
      }}
      validationSchema={signInProjectValidator}
      validateOnBlur={false}
      onSubmit={values => handleSubmitForm(values)}
    >
      {formikProps => {
        const { handleSubmit } = formikProps
        return (
          <form onSubmit={handleSubmit}>
            <Field
              InputType={Input}
              component={FormInputField}
              icon="user"
              name="uuid"
              placeholder="e4efba4d-47e6-42a5-905e-589d3f673853"
            />

            <Button text="Sign in project" icon="login" type="submit" />
          </form>
        )
      }}
    </Formik>
  </SignInProjectCard>
)
Example #3
Source File: CommentForm.tsx    From End-to-End-Web-Testing-with-Cypress with MIT License 6 votes vote down vote up
CommentForm: React.FC<CommentFormProps> = ({ transactionId, transactionComment }) => {
  const classes = useStyles();
  const initialValues = { content: "" };

  return (
    <div>
      <Formik
        initialValues={initialValues}
        validationSchema={validationSchema}
        onSubmit={(values, { setSubmitting }) => {
          setSubmitting(true);
          transactionComment({ transactionId, ...values });
        }}
      >
        {() => (
          <Form className={classes.form}>
            <Field name="content">
              {({ field, meta }: FieldProps) => (
                <TextField
                  variant="outlined"
                  margin="dense"
                  fullWidth
                  id={`transaction-comment-input-${transactionId}`}
                  type="text"
                  placeholder="Write a comment..."
                  inputProps={{ "data-test": `transaction-comment-input-${transactionId}` }}
                  error={meta.touched && Boolean(meta.error)}
                  helperText={meta.touched ? meta.error : ""}
                  {...field}
                />
              )}
            </Field>
          </Form>
        )}
      </Formik>
    </div>
  );
}
Example #4
Source File: NewProject.view.tsx    From tezos-link with Apache License 2.0 6 votes vote down vote up
NewProjectView = ({ handleSubmitForm, loading  }: NewPostViewProps) => {
        const [networkValue, setNetwork] = useState('MAINNET')

        return (
        <NewProjectCard>
            <h1>New Project</h1>
            <Formik
                initialValues={{title: '', network: ''}}
                validationSchema={newProjectValidator}
                validateOnBlur={false}
                onSubmit={(values, actions) => handleSubmitForm({ ...values, network: networkValue }, actions)}
            >
                {formikProps => {
                    const {handleSubmit} = formikProps
                    return (
                        <div>
                            <Select
                                options={['MAINNET', 'CARTHAGENET']}
                                defaultOption={networkValue}
                                selectCallback={(e: string) => setNetwork(e)}
                            />
                            <form onSubmit={handleSubmit}>

                                <Field InputType={Input} component={FormInputField} icon="user" name="title"
                                       placeholder="Project title"/>

                                <Button text="Create project" icon="sign-up" type="submit" loading={loading}/>
                            </form>
                        </div>
                    )
                }}
            </Formik>
        </NewProjectCard>
    )
}
Example #5
Source File: Input.tsx    From panvala with Apache License 2.0 6 votes vote down vote up
StyledInput = styled(Field)`
  box-sizing: border-box;
  display: block;
  width: 100%;
  font-size: 0.875em;
  margin-top: 8px;
  padding: 16px 8px;
  border-radius: 8px;

  ${space};
  ${color};
  ${layout};
  ${typography};
  ${flexbox};
  ${border};
  ${background}
  ${shadow};
  ${position};
`
Example #6
Source File: InputBox.tsx    From slice-machine with Apache License 2.0 6 votes vote down vote up
InputBox: React.FunctionComponent<InputBoxProps> = ({
  name,
  label,
  placeholder,
  error,
  dataCy,
}) => (
  <Box mb={3}>
    <Label htmlFor={name} mb={2}>
      {label}
    </Label>
    <Field
      name={name}
      type="text"
      placeholder={placeholder}
      as={Input}
      autoComplete="off"
      {...(dataCy ? { "data-cy": dataCy } : null)}
    />
    {error ? (
      <Text
        data-cy={dataCy ? `${dataCy}-error` : "input-error"}
        sx={{ color: "error", mt: 1 }}
      >
        {error}
      </Text>
    ) : null}
  </Box>
)
Example #7
Source File: index.tsx    From advanced-formik-validations-with-yup with MIT License 6 votes vote down vote up
FormikField: React.FC<FormikFieldProps> = ({ name, label, type = "text", required = false}) => {
  return (
    <div className="FormikField">
      <Field
        required={required}
        autoComplete="off"
        as={TextField}
        label={label}
        name={name}
        fullWidth
        type={type}
        helperText={<ErrorMessage name={name} />}
      />
    </div>
  );
}
Example #8
Source File: index.tsx    From Mokku with MIT License 6 votes vote down vote up
Input = styled(Field).attrs({ id: "mock-create-input" })<{
  small?: boolean;
  marginRight?: boolean;
}>`
  border: 1px solid ${({ theme }) => theme.colors.border};
  border-radius: 4px;
  border-style: solid;
  ${({ small }) => small && `width: 124px;`};
  ${({ marginRight }) => marginRight && `margin-right: 8px;`};
`
Example #9
Source File: Autocomplete.test.tsx    From abacus with GNU General Public License v2.0 6 votes vote down vote up
test('it shows as loading data', () => {
  const isLoading = true

  const { container } = render(
    <MockFormik initialValues={{ name: 'no_name' }}>
      <Field
        component={Autocomplete}
        name='name'
        id='name'
        fullWidth
        options={[]}
        loading={isLoading}
        renderInput={(params: AutocompleteRenderInputParams) => {
          return (
            <MuiTextField
              {...params}
              placeholder='wp_username'
              helperText='Use WordPress.com username.'
              variant='outlined'
              required
              label='Owner'
              InputProps={{
                ...autocompleteInputProps(params, isLoading),
                startAdornment: <InputAdornment position='start'>@</InputAdornment>,
              }}
              InputLabelProps={{
                shrink: true,
              }}
            />
          )
        }}
      />
    </MockFormik>,
  )
  expect(container).toMatchSnapshot('loading')
})
Example #10
Source File: index.tsx    From Account-Manager with MIT License 6 votes vote down vote up
FormInput: FC<ComponentProps> = ({
  hideErrorBlock = false,
  hideErrorText = false,
  label,
  required,
  ...baseInputProps
}) => {
  const {className, name} = baseInputProps;
  const {errors, touched} = useFormContext();
  const error = !!errors[name] && !!touched[name];

  return (
    <div className={clsx('FormInput FormFieldComponent', className)}>
      {renderFormLabel({className, label, name, required})}
      <Field {...baseInputProps} as={Input} className="FormField" error={error} required={required} />
      {hideErrorBlock ? null : renderFormError({className, hideErrorText, name})}
    </div>
  );
}
Example #11
Source File: add-training-goal-task.form.component.tsx    From MyWay-client with MIT License 6 votes vote down vote up
export default function AddTrainingGoalTaskForm({
  dogId,
  goalId,
}: {
  dogId: number;
  goalId: number;
}) {
  return (
    <Formik
      initialValues={{ description: "" }}
      onSubmit={(values, actions) => {
        axios.post(`/api/dogs/${dogId}/training-goals/${goalId}/tasks`, {
          ...values,
        });
      }}
    >
      <Form>
        <Field name="description" placeholder="description" />
        <ErrorMessage name="description" />

        <button type="submit">Add Task</button>
      </Form>
    </Formik>
  );
}
Example #12
Source File: Beginning.tsx    From abacus with GNU General Public License v2.0 6 votes vote down vote up
Beginning = (): JSX.Element => {
  const classes = useStyles()

  return (
    <div className={classes.root}>
      <Typography variant='h4' gutterBottom>
        Design and Document Your Experiment
      </Typography>
      <Typography variant='body2'>
        We think one of the best ways to prevent a failed experiment is by documenting what you hope to learn.{/* */}
        <br />
        <br />
      </Typography>
      <Alert severity='info'>
        <Link underline='always' href='https://github.com/Automattic/experimentation-platform/wiki' target='_blank'>
          Our wiki is a great place to start
        </Link>
        , it will instruct you on creating a P2 post.
      </Alert>
      <Field
        className={classes.p2EntryField}
        component={TextField}
        id='experiment.p2Url'
        name='experiment.p2Url'
        placeholder='https://your-p2-post-here'
        label={`Your Post's URL`}
        variant='outlined'
        InputLabelProps={{
          shrink: true,
        }}
      />
    </div>
  )
}
Example #13
Source File: index.tsx    From youtube-2020-june-multi-step-form-formik with MIT License 5 votes vote down vote up
export default function Home() {
  return (
    <Card>
      <CardContent>
        <FormikStepper
          initialValues={{
            firstName: '',
            lastName: '',
            millionaire: false,
            money: 0,
            description: '',
          }}
          onSubmit={async (values) => {
            await sleep(3000);
            console.log('values', values);
          }}
        >
          <FormikStep label="Personal Data">
            <Box paddingBottom={2}>
              <Field fullWidth name="firstName" component={TextField} label="First Name" />
            </Box>
            <Box paddingBottom={2}>
              <Field fullWidth name="lastName" component={TextField} label="Last Name" />
            </Box>
            <Box paddingBottom={2}>
              <Field
                name="millionaire"
                type="checkbox"
                component={CheckboxWithLabel}
                Label={{ label: 'I am a millionaire' }}
              />
            </Box>
          </FormikStep>
          <FormikStep
            label="Bank Accounts"
            validationSchema={object({
              money: mixed().when('millionaire', {
                is: true,
                then: number()
                  .required()
                  .min(
                    1_000_000,
                    'Because you said you are a millionaire you need to have 1 million'
                  ),
                otherwise: number().required(),
              }),
            })}
          >
            <Box paddingBottom={2}>
              <Field
                fullWidth
                name="money"
                type="number"
                component={TextField}
                label="All the money I have"
              />
            </Box>
          </FormikStep>
          <FormikStep label="More Info">
            <Box paddingBottom={2}>
              <Field fullWidth name="description" component={TextField} label="Description" />
            </Box>
          </FormikStep>
        </FormikStepper>
      </CardContent>
    </Card>
  );
}
Example #14
Source File: index.tsx    From Mokku with MIT License 5 votes vote down vote up
Input = styled(Field)<{ small?: boolean }>`
  height: 25px;
  border: 1px solid ${({ theme }) => theme.colors.border};
  border-radius: 4px;
  border-style: solid;
  ${({ small }) => small && `width: 124px;`};
`
Example #15
Source File: Login.tsx    From krmanga with MIT License 5 votes vote down vote up
Login = ({ navigation, dispatch, isLogin, loading }: IProps) => {

    const [disabled, setDisabled] = useState<boolean>(false);

    useEffect(() => {
        if (isLogin) {
            setTimeout(() => {
                navigation.goBack();
            }, 100);
        }
    }, [isLogin]);

    const onSubmit = (values: Values) => {

        if (disabled || loading) {
            return;
        }

        setDisabled(true);

        dispatch({
            type: "user/login",
            payload: values,
            callback: () => {
                setDisabled(false);
            }
        });
    };

    const cancel = (form: FormikProps<string>, field: FieldInputProps<string>) => {
        if (field.name === "account") {
            form.setFieldValue("account", "");
        } else if (field.name === "password") {
            form.setFieldValue("password", "");
        }
    };

    return (
        <ScrollView keyboardShouldPersistTaps="handled" style={styles.container}>
            <Formik
                initialValues={initialValues}
                onSubmit={onSubmit}
                validationSchema={customerValidation}>
                {({ handleSubmit }) => (
                    <View>
                        <Field
                            name="account"
                            placeholder="请输入用户名"
                            component={Input}
                            iconName={"icon-Account"}
                            cancel={cancel}
                        />
                        <Field
                            name="password"
                            placeholder="请输入密码"
                            component={Input}
                            iconName={"icon-mima"}
                            secureTextEntry
                            cancel={cancel}
                        />
                        <View style={styles.jumpView}>
                            <Text style={styles.jumpTitle}>忘记密码?</Text>
                            <Touchable onPress={() => navigation.navigate("Register")}>
                                <Text style={styles.jumpTitle}>注册账号</Text>
                            </Touchable>
                        </View>
                        <Touchable disabled={disabled} onPress={handleSubmit} style={styles.login}>
                            <Text style={styles.loginText}>登录</Text>
                        </Touchable>
                    </View>
                )}
            </Formik>
        </ScrollView>
    );
}
Example #16
Source File: SwarmSelect.tsx    From bee-dashboard with BSD 3-Clause "New" or "Revised" License 5 votes vote down vote up
export function SwarmSelect({ defaultValue, formik, name, options, onChange, label }: Props): ReactElement {
  const classes = useStyles()

  if (formik) {
    return (
      <>
        {label && <FormHelperText>{label}</FormHelperText>}
        <Field
          required
          component={Select}
          name={name}
          fullWidth
          variant="outlined"
          defaultValue={defaultValue || ''}
          className={classes.select}
          placeholder={label}
          MenuProps={{ MenuListProps: { disablePadding: true }, PaperProps: { square: true } }}
        >
          {options.map((x, i) => (
            <MenuItem key={i} value={x.value} className={classes.option}>
              {x.label}
            </MenuItem>
          ))}
        </Field>
      </>
    )
  }

  return (
    <>
      {label && <FormHelperText>{label}</FormHelperText>}
      <SimpleSelect
        required
        name={name}
        fullWidth
        variant="outlined"
        className={classes.select}
        defaultValue={defaultValue || ''}
        onChange={onChange}
        placeholder={label}
        MenuProps={{ MenuListProps: { disablePadding: true }, PaperProps: { square: true } }}
      >
        {options.map((x, i) => (
          <MenuItem key={i} value={x.value} className={classes.option}>
            {x.label}
          </MenuItem>
        ))}
      </SimpleSelect>
    </>
  )
}
Example #17
Source File: MetaFile.tsx    From gear-js with GNU General Public License v3.0 5 votes vote down vote up
MetaFile = (props: Props) => {
  const alert = useAlert();
  const metaFieldRef = useRef<HTMLInputElement>(null);

  const { file, className, onUpload, onDelete } = props;

  const uploadMetaFile = () => {
    metaFieldRef.current?.click();
  };

  const handleChangeMetaFile = (event: React.ChangeEvent<HTMLInputElement>) => {
    if (event.target.files && event.target.files.length) {
      const isCorrectFormat = checkFileFormat(event.target.files[0]);

      if (isCorrectFormat) {
        onUpload(event.target.files[0]);
      } else {
        alert.error('Wrong file format');
      }

      event.target.value = '';
    }
  };

  return (
    <div className={clsx(styles.upload, className)}>
      <label htmlFor="meta" className={styles.caption}>
        Metadata file:
      </label>
      <div className={styles.block}>
        <Field
          id="meta"
          name="meta"
          className={styles.hidden}
          type="file"
          innerRef={metaFieldRef}
          onChange={handleChangeMetaFile}
        />
        {file ? (
          <div className={clsx(styles.value, styles.filename)}>
            {file.name}
            <button type="button" onClick={onDelete}>
              <Trash2 color="#ffffff" size="20" strokeWidth="1" />
            </button>
          </div>
        ) : (
          <Button
            text="Select file"
            type="button"
            color="secondary"
            className={styles.button}
            onClick={uploadMetaFile}
          />
        )}
      </div>
    </div>
  );
}
Example #18
Source File: TextArea.tsx    From core with GNU Affero General Public License v3.0 5 votes vote down vote up
TextArea: React.FC<TextAreaProps> = ({ name, placeholder, theme='auto', max, setValue, value }) => {
	const ref = useRef()
	const [ emojiPickerHidden, setEmojiPickerHidden ] = useState(true)
	useOutsideClick(ref, () => {
		setEmojiPickerHidden(true)
	})
	
	return <div className='border border-grey-light dark:border-transparent h-96 text-black dark:bg-very-black dark:text-white rounded px-4 py-3 inline-block relative w-full'>
		<Field as='textarea' name={name} className='dark:border-transparent text-black dark:bg-very-black dark:text-white w-full relative h-full resize-none outline-none' placeholder={placeholder} />
		<div ref={ref}>
			<div className='absolute bottom-12 left-10 z-30'>
				{
					!emojiPickerHidden && <Picker title='선택해주세요' emoji='sunglasses' set='twitter' enableFrequentEmojiSort	theme={theme} showSkinTones={false} onSelect={(e) => {
						setEmojiPickerHidden(true)
						setValue(value + ' ' + ((e as { native: string }).native || e.colons))
					}} i18n={{
						search: '검색',
						notfound: '검색 결과가 없습니다.',
						categories: {
							search: '검색 결과',
							recent: '최근 사용',
							people: '사람',
							nature: '자연',
							foods: '음식',
							activity: '활동',
							places: '장소',
							objects: '사물',
							symbols: '기호',
							flags: '국기',
							custom: '커스텀'
						}
					}} custom={KoreanbotsEmoji}/>
				}
			</div>
			<div className='absolute bottom-2 left-4 hidden sm:block'>
				<div className='emoji-selector-button outline-none' onClick={() => setEmojiPickerHidden(false)} onKeyPress={() => setEmojiPickerHidden(false)} role='button' tabIndex={0} />
			</div>
			{
				max && <span className={`absolute bottom-2 right-4 ${max < value.length ? ' text-red-400' : ''}`}>
					{max-value.length}
				</span>
			}
		</div>
	</div>
}
Example #19
Source File: RecurrenceEditor.tsx    From querybook with Apache License 2.0 5 votes vote down vote up
RecurrenceEditorDatePicker: React.FunctionComponent<IDatePickerProps> = ({
    label,
    onKey,
    options,
    error,
    recurrence,
    setRecurrence,
}) => {
    const formattedError = (error?.[onKey] || '').replace(
        `recurrence.on.${onKey}`,
        label
    );
    return (
        <FormField label={`Recurrence ${label}`} error={formattedError}>
            <Field
                name={`recurrence.on.${onKey}`}
                render={({ field }) => (
                    <Select<IOptionType, true>
                        menuPortalTarget={overlayRoot}
                        styles={recurrenceReactSelectStyle}
                        value={options.filter((option: { value: any }) =>
                            field.value?.includes(option.value)
                        )}
                        options={options}
                        onChange={(value) => {
                            const newRecurrence = {
                                ...recurrence,
                                on: {
                                    ...recurrence.on,
                                    [onKey]: value.map((v) => v.value),
                                },
                            };
                            setRecurrence(newRecurrence);
                        }}
                        isMulti
                    />
                )}
            />
        </FormField>
    );
}
Example #20
Source File: CheckBox.tsx    From core with GNU Affero General Public License v3.0 5 votes vote down vote up
CheckBox: React.FC<CheckBoxProps> = ({ name, ...props }) => {
	return <Field type='checkbox' name={name} className='form-checkbox text-koreanbots-blue bg-gray-300 h-4 w-4 rounded' {...props} />
}
Example #21
Source File: TagFormFields.tsx    From abacus with GNU General Public License v2.0 5 votes vote down vote up
TagFormFields = (): JSX.Element => {
  const classes = useStyles()

  return (
    <>
      <div className={classes.row}>
        <Field
          component={TextField}
          name='tag.namespace'
          id='tag.namespace'
          label='Tag namespace'
          placeholder='tag_namespace'
          helperText='Use snake_case.'
          variant='outlined'
          fullWidth
          required
          InputLabelProps={{
            shrink: true,
          }}
        />
      </div>
      <div className={classes.row}>
        <Field
          component={TextField}
          name='tag.name'
          id='tag.name'
          label='Tag name'
          placeholder='tag_name'
          helperText='Use snake_case.'
          variant='outlined'
          fullWidth
          required
          InputLabelProps={{
            shrink: true,
          }}
        />
      </div>
      <div className={classes.row}>
        <Field
          component={TextField}
          name='tag.description'
          id='tag.description'
          label='Tag description'
          placeholder='Put your Tag description here!'
          variant='outlined'
          fullWidth
          required
          multiline
          rows={4}
          InputLabelProps={{
            shrink: true,
          }}
        />
      </div>
    </>
  )
}
Example #22
Source File: dog-profile.component.tsx    From MyWay-client with MIT License 5 votes vote down vote up
export default function DogProfile({ dog }: { dog: DogDto }) {
  return (
    <div>
      <div>
        <div>
          <div>Dog{"'"}s picture</div>
          <span></span>
        </div>
        <div>
          <div>
            Upload {dog.name}
            {"'"}s photo
          </div>
          <div>
            <button>Upload photo</button>
          </div>
        </div>
      </div>
      <Formik
        initialValues={{
          name: dog.name,
          breed: dog.breed,
          age_years: calculateDiffInYears(new Date(), new Date(dog.birthDate)),
          age_months: calculateDiffInMonths(
            new Date(),
            new Date(dog.birthDate)
          ),
        }}
        onSubmit={async (values, actions) => {
          axios
            .patch(`/api/dogs/${dog.id}`, values)
            .then((res) =>
              actions.setStatus({
                status: true,
                message: "Changes were saved successfully.",
              })
            )
            .catch((e) =>
              actions.setStatus({ status: true, message: e.message })
            )
            .finally(() => actions.setSubmitting(false));
        }}
        validationSchema={DogProfileSchema}
      >
        {({ isSubmitting, status }) => (
          <Form>
            <Field name="name" type="text" placeholder="Name" />
            <ErrorMessage name="name" />

            <Field name="breed" type="text" placeholder="Breed" />
            <ErrorMessage name="breed" />

            <Field name="age_years" type="number" placeholder="Age (years)" />
            <ErrorMessage name="age_years" />

            <Field name="age_months" type="number" placeholder="Age (months)" />
            <ErrorMessage name="age_months" />

            {status && <>{status.message}</>}

            <SubmitButton isSubmitting={isSubmitting} />
          </Form>
        )}
      </Formik>
    </div>
  );
}
Example #23
Source File: index.tsx    From core with GNU Affero General Public License v3.0 4 votes vote down vote up
Bots: NextPage<BotsProps> = ({ data, desc, date, user, theme, csrfToken }) => {
	const bg = checkBotFlag(data?.flags, 'trusted') && data?.banner
	const router = useRouter()
	const [ nsfw, setNSFW ] = useState<boolean>()
	const [ reportModal, setReportModal ] = useState(false)
	const [ reportRes, setReportRes ] = useState<ResponseProps<unknown>>(null)
	useEffect(() => {
		setNSFW(localStorage.nsfw)
	}, [])
	if (!data?.id) return <NotFound />
	return <div style={bg ? { background: `linear-gradient(to right, rgba(34, 36, 38, 0.68), rgba(34, 36, 38, 0.68)), url("${data.bg}") center top / cover no-repeat fixed` } : {}}>
		<Container paddingTop className='py-10'>
			<NextSeo
				title={data.name}
				description={data.intro}
				twitter={{
					cardType: 'summary_large_image'
				}}
				openGraph={{
					images: [
						{
							url: KoreanbotsEndPoints.OG.bot(data.id, data.name, data.intro, data.category, [formatNumber(data.votes), formatNumber(data.servers)]),
							width: 2048,
							height: 1170,
							alt: 'Bot Preview Image'
						}
					]
				}}
			/>
			{
				data.state === 'blocked' ? <div className='pb-40'>
					<Message type='error'>
						<h2 className='text-lg font-black'>해당 봇은 관리자에 의해 삭제되었습니다.</h2>
					</Message>
				</div>
					: data.category.includes('NSFW') && !nsfw ? <NSFW onClick={() => setNSFW(true)} onDisableClick={() => localStorage.nsfw = true} /> 
						: <>
							<div className='w-full pb-2'>
								{
									data.state === 'private' ? <Message type='info'>
										<h2 className='text-lg font-black'>해당 봇은 특수목적 봇이므로 초대하실 수 없습니다.</h2>
										<p>해당 봇은 공개 사용이 목적이 아닌 특수목적봇입니다. 따라서 따로 초대하실 수 없습니다.</p>
									</Message> :
										data.state === 'reported' ?
											<Message type='error'>
												<h2 className='text-lg font-black'>해당 봇은 신고가 접수되어, 관리자에 의해 잠금 상태입니다.</h2>
												<p>해당 봇 사용에 주의해주세요.</p>
												<p>봇 소유자분은 <Link href='/guidelines'><a className='text-blue-500 hover:text-blue-400'>가이드라인</a></Link>에 대한 위반사항을 확인해주시고 <Link href='/discord'><a className='text-blue-500 hover:text-blue-400'>디스코드 서버</a></Link>로 문의해주세요.</p>
											</Message> : ''
								}
							</div>
							<div className='lg:flex w-full'>
								<div className='w-full text-center lg:w-2/12'>
									<DiscordAvatar
										userID={data.id}
										size={256}
										className='w-full'
									/>
								</div>
								<div className='flex-grow px-5 py-12 w-full text-center lg:w-5/12 lg:text-left'>
									<Tag
										circular
										text={
											<>
												<i className={`fas fa-circle text-${Status[data.status]?.color}`} />{' '}
												{Status[data.status]?.text}
											</>
										}
									/>
									<h1 className='mb-2 mt-3 text-4xl font-bold' style={bg ? { color: 'white' } : {}}>
										{data.name}{' '}
										{checkBotFlag(data.flags, 'trusted') ? (
											<Tooltip placement='bottom' overlay='해당 봇은 한국 디스코드 리스트에서 엄격한 기준을 통과한 봇입니다!'>
												<span className='text-koreanbots-blue text-3xl'>
													<i className='fas fa-award' />
												</span>
											</Tooltip>
										) : ''}
									</h1>
									<p className={`${bg ? 'text-gray-300' : 'dark:text-gray-300 text-gray-800'} text-base`}>{data.intro}</p>
								</div>
								<div className='w-full lg:w-1/4'>
									{
										data.state === 'ok' && <LongButton
											newTab
											href={`/bots/${router.query.id}/invite`}
										>
											<h4 className='whitespace-nowrap'>
												<i className='fas fa-user-plus text-discord-blurple' /> 초대하기
											</h4>
										</LongButton>
									}
									<Link href={`/bots/${router.query.id}/vote`}>
										<LongButton>
											<h4>
												<i className='fas fa-heart text-red-600' /> 하트 추가
											</h4>
											<span className='ml-1 px-2 text-center text-black dark:text-gray-400 text-sm bg-little-white-hover dark:bg-very-black rounded-lg'>
												{formatNumber(data.votes)}
											</span>
										</LongButton>
									</Link>
									{
										((data.owners as User[]).find(el => el.id === user?.id) || checkUserFlag(user?.flags, 'staff')) && <LongButton href={`/bots/${data.id}/edit`}>
											<h4>
												<i className='fas fa-cogs' /> 관리하기
											</h4>
										</LongButton>
									}
									{
										((data.owners as User[]).find(el => el.id === user?.id) || checkUserFlag(user?.flags, 'staff')) && <LongButton onClick={async() => {
											const res = await Fetch(`/bots/${data.id}/stats`, { method: 'PATCH'} )
											if(res.code !== 200) return alert(res.message)
											else window.location.reload()
										}}>
											<h4>
												<i className='fas fa-sync' /> 정보 갱신하기
											</h4>
										</LongButton>
									}
								</div>
							</div>
							<Divider className='px-5' />
							<div className='hidden lg:block'>
								<Advertisement />
							</div>
							<div className='lg:flex lg:flex-row-reverse' style={bg ? { color: 'white' } : {}}>
								<div className='mb-1 w-full lg:w-1/4'>
									<h2 className='3xl mb-2 font-bold'>정보</h2>
									<div className='grid gap-4 grid-cols-2 px-4 py-4 text-black dark:text-gray-400 dark:bg-discord-black bg-little-white rounded-sm'>
										<div>
											<i className='far fa-flag' /> 접두사
										</div>
										<div className='markdown-body text-black dark:text-gray-400'>
											<code>{data.prefix}</code>
										</div>
										<div>
											<i className='fas fa-users' /> 서버수
										</div>
										<div>{data.servers || 'N/A'}</div>
										{
											data.shards && data.servers > 1500 && <>
												<div>
													<i className='fas fa-sitemap' /> 샤드수
												</div>
												<div>{data.shards}</div>
											</>
										}
										<div>
											<i className='fas fa-calendar-day' /> 봇 생성일
										</div>
										<div>{Day(date).fromNow(false)}</div>
										{
											checkBotFlag(data.flags, 'verified') ?
												<Tooltip overlay='해당 봇은 디스코드측에서 인증된 봇입니다.'>
													<div className='col-span-2'>
														<i className='fas fa-check text-discord-blurple' /> 디스코드 인증됨
													</div>
												</Tooltip>
												: ''
										}
									</div>
									<h2 className='3xl mb-2 mt-2 font-bold'>카테고리</h2>
									<div className='flex flex-wrap'>
										{data.category.map(el => (
											<Tag key={el} text={el} href={`/bots/categories/${el}`} />
										))}
									</div>
									<h2 className='3xl mb-2 mt-2 font-bold'>제작자</h2>
									{(data.owners as User[]).map(el => (
										<Owner
											key={el.id}
											id={el.id}
											tag={el.tag}
											username={el.username}
										/>
									))}
									<div className='list grid'>
										<Link href={`/bots/${router.query.id}/report`}>
											<a className='text-red-600 hover:underline cursor-pointer' aria-hidden='true'>
												<i className='far fa-flag' /> 신고하기
											</a>
										</Link>
										<Modal header={`${data.name}#${data.tag} 신고하기`} closeIcon isOpen={reportModal} onClose={() => {
											setReportModal(false)
											setReportRes(null)
										}} full dark={theme === 'dark'}>
											{
												reportRes?.code === 200 ? <Message type='success'>
													<h2 className='text-lg font-semibold'>성공적으로 신고하였습니다!</h2>
													<p>더 자세한 설명이 필요할 수 있습니다! <a className='text-blue-600 hover:text-blue-500' href='/discord'>공식 디스코드</a>에 참여해주세요</p>
												</Message> : <Formik onSubmit={async (body) => {
													const res = await Fetch(`/bots/${data.id}/report`, { method: 'POST', body: JSON.stringify(body) })
													setReportRes(res)
												}} validationSchema={ReportSchema} initialValues={{
													category: null,
													description: '',
													_csrf: csrfToken
												}}>
													{
														({ errors, touched, values, setFieldValue }) => (
															<Form>
																<div className='mb-5'>
																	{
																		reportRes && <div className='my-5'>
																			<Message type='error'>
																				<h2 className='text-lg font-semibold'>{reportRes.message}</h2>
																				<ul className='list-disc'>
																					{reportRes.errors?.map((el, n) => <li key={n}>{el}</li>)}
																				</ul>
																			</Message>
																		</div>
																	}
																	<h3 className='font-bold'>신고 구분</h3>
																	<p className='text-gray-400 text-sm mb-1'>해당되는 항목을 선택해주세요.</p>
																	{
																		reportCats.map(el => 
																			<div key={el}>
																				<label>
																					<Field type='radio' name='category' value={el} className='mr-1.5 py-2' />
																					{el}
																				</label>
																			</div>
																		)
																	}
																	<div className='mt-1 text-red-500 text-xs font-light'>{errors.category && touched.category ? errors.category : null}</div>
																	<h3 className='font-bold mt-2'>설명</h3>
																	<p className='text-gray-400 text-sm mb-1'>신고하시는 내용을 자세하게 설명해주세요.</p>
																	<TextArea name='description' placeholder='최대한 자세하게 설명해주세요!' theme={theme === 'dark' ? 'dark' : 'light'} value={values.description} setValue={(value) => setFieldValue('description', value)} />
																	<div className='mt-1 text-red-500 text-xs font-light'>{errors.description && touched.description ? errors.description : null}</div>
																</div>
																<div className='text-right'>
																	<Button className='bg-gray-500 hover:opacity-90 text-white' onClick={()=> setReportModal(false)}>취소</Button>
																	<Button type='submit' className='bg-red-500 hover:opacity-90 text-white'>제출</Button>
																</div>
															</Form>
														)
													}
												</Formik>
											}
										</Modal>
										{data.discord && (
											<a
												rel='noopener noreferrer'
												target='_blank'
												className='text-discord-blurple hover:underline'
												href={`https://discord.gg/${data.discord}`}
											>
												<i className='fab fa-discord' />
						디스코드 서버
											</a>
										)}
										{data.web && (
											<a
												rel='noopener noreferrer'
												target='_blank'
												className='text-blue-500 hover:underline'
												href={data.web}
											>
												<i className='fas fa-globe' />
						웹사이트
											</a>
										)}
										{data.git && (
											<a
												rel='noopener noreferrer'
												target='_blank'
												className='hover:underline'
												href={data.git}
											>
												<i className={`fab fa-${git[new URL(data.git).hostname]?.icon ?? 'git-alt'}`} />
												{git[new URL(data.git).hostname]?.text ?? 'Git'}
											</a>
										)}
									</div>
									<Advertisement size='tall' />
								</div>
								<div className='w-full lg:pr-5 lg:w-3/4'>
									{
										checkBotFlag(data.flags, 'hackerthon') ? <Segment className='mt-10'>
											<h1 className='text-3xl font-semibold'>
												<i className='fas fa-trophy mr-4 my-2 text-yellow-300' /> 해당 봇은 한국 디스코드 리스트 해커톤 수상작품입니다!
											</h1>
											<p>해당 봇은 한국 디스코드 리스트 주최로 진행되었던 "한국 디스코드 리스트 제1회 해커톤"에서 우수한 성적을 거둔 봇입니다.</p>
											<p>자세한 내용은 <a className='text-blue-500 hover:text-blue-400' href='https://blog.koreanbots.dev/first-hackathon-results/'>해당 글</a>을 확인해주세요.</p>
										</Segment> : ''
									}
									<Segment className='my-4'>
										<Markdown text={desc}/>
									</Segment>
									<Advertisement />
								</div>
							</div>
						</>
			}
		</Container>
	</div>
}
Example #24
Source File: index.tsx    From formik-chakra-ui with MIT License 4 votes vote down vote up
App = () => {
  return (
    <ChakraProvider>
      <Heading as="h1" size="xl" textAlign="center">
        Formik Chakra UI
      </Heading>
      <Box as="p" textAlign="center">
        Example using{' '}
        <Link href="https://github.com/kgnugur/formik-chakra-ui" isExternal>
          Formik Chakra UI{' '}
        </Link>
      </Box>

      <Formik
        initialValues={initialValues}
        onSubmit={onSubmit}
        validationSchema={validationSchema}
      >
        {({ handleSubmit, values, errors }) => (
          <Stack
            spacing={5}
            borderWidth="1px"
            rounded="lg"
            shadow="1px 1px 3px rgba(0,0,0,0.3)"
            maxWidth={800}
            p={6}
            m="10px auto"
            as="form"
            onSubmit={handleSubmit as any}
          >
            <InputControl name="firstName" label="First Name" />
            <InputControl name="lastName" label="Last Name" />
            <NumberInputControl
              name="age"
              label="Age"
              helperText="Helper text"
            />
            <CheckboxSingleControl name="employed">
              Employed
            </CheckboxSingleControl>
            <RadioGroupControl name="favoriteColor" label="Favorite Color">
              <Radio value="#ff0000">Red</Radio>
              <Radio value="#00ff00">Green</Radio>
              <Radio value="#0000ff">Blue</Radio>
            </RadioGroupControl>
            <CheckboxContainer name="toppings" label="Toppings">
              <CheckboxControl name="toppings1" value="chicken">
                Chicken
              </CheckboxControl>
              <CheckboxControl name="toppings" value="ham">
                Ham
              </CheckboxControl>
              <CheckboxControl name="toppings" value="mushrooms">
                Mushrooms
              </CheckboxControl>
              <CheckboxControl name="toppings" value="cheese">
                Cheese
              </CheckboxControl>
              <CheckboxControl name="toppings" value="tuna">
                Tuna
              </CheckboxControl>
              <CheckboxControl name="toppings" value="pineapple">
                Pineapple
              </CheckboxControl>
            </CheckboxContainer>
            <TextareaControl name="notes" label="Notes" />
            <SwitchControl name="employedd" label="Employed" />
            <SelectControl
              label="Select label"
              name="select"
              selectProps={{ placeholder: 'Select option' }}
            >
              <option value="option1">Option 1</option>
              <option value="option2">Option 2</option>
              <option value="option3">Option 3</option>
            </SelectControl>
            <SliderControl name="foo" sliderProps={{ max: 40 }} />
            <PinInputControl
              name="bar"
              pinAmount={4}
              pinInputProps={{ size: 'sm' }}
            />

            <PercentComplete />
            <FormControl
              name="customField"
              label="Custom FormControl"
              helperText="Helper text"
            >
              <Field
                as={Input}
                name="customField"
                placeholder="A custom field"
              />
            </FormControl>
            <InputControl
              name="customElements"
              label="Custom elements"
              labelProps={{ color: 'blue' }}
              errorMessageProps={{ fontWeight: 'bold' }}
              helperText="Helper text"
              helperTextProps={{ fontStyle: 'italic' }}
            />
            <ButtonGroup>
              <SubmitButton>Submit</SubmitButton>
              <ResetButton>Reset</ResetButton>
            </ButtonGroup>

            <Box as="pre" marginY={10}>
              {JSON.stringify(values, null, 2)}
              <br />
              {JSON.stringify(errors, null, 2)}
            </Box>
          </Stack>
        )}
      </Formik>
    </ChakraProvider>
  );
}
Example #25
Source File: AddAttachment.tsx    From glific-frontend with GNU Affero General Public License v3.0 4 votes vote down vote up
AddAttachment: React.FC<AddAttachmentPropTypes> = ({
  setAttachment,
  setAttachmentAdded,
  setAttachmentURL,
  setAttachmentType,
  attachmentURL,
  attachmentType,
  uploadPermission,
}: AddAttachmentPropTypes) => {
  const [errors, setErrors] = useState<any>(null);
  const [uploading, setUploading] = useState(false);
  const [fileName, setFileName] = useState<null | string>(null);
  const [verifying, setVerifying] = useState(false);
  const [uploadDisabled] = useState(!uploadPermission);

  const { t } = useTranslation();

  const [uploadMedia] = useMutation(UPLOAD_MEDIA, {
    onCompleted: (data: any) => {
      setAttachmentURL(data.uploadMedia);
      setUploading(false);
    },
    onError: () => {
      setUploading(false);
    },
  });

  const validateURL = () => {
    if (attachmentURL && attachmentType) {
      setVerifying(true);
      setErrors(null);

      validateMedia(attachmentURL, attachmentType)
        .then((response: any) => {
          if (!response.data.is_valid) {
            setVerifying(false);
            setErrors(response.data.message);
          } else if (response.data.is_valid) {
            setVerifying(false);
            setAttachmentAdded(true);

            setErrors(null);
          }
        })
        .catch((error) => {
          setLogs(error, 'error');
        });
    }
  };

  useEffect(() => {
    validateURL();
  }, [attachmentURL, attachmentType]);

  const helperText = (
    <div className={styles.HelperText}>
      {t('Please wait for the attachment URL verification')}
      <CircularProgress className={styles.ProgressIcon} />
    </div>
  );
  let timer: any = null;
  const input = {
    component: Input,
    name: 'attachmentURL',
    type: 'text',
    placeholder: t('Attachment URL'),
    helperText: verifying && helperText,
    disabled: fileName !== null,
    inputProp: {
      onBlur: (event: any) => {
        setAttachmentURL(event.target.value);
      },
      onChange: (event: any) => {
        clearTimeout(timer);
        timer = setTimeout(() => setAttachmentURL(event.target.value), 1000);
      },
    },
  };

  let formFieldItems: any = [
    {
      component: Dropdown,
      options,
      name: 'attachmentType',
      placeholder: 'Type',
      fieldValue: attachmentType,
      fieldChange: (event: any) => {
        setAttachmentType(event?.target.value);
        setErrors(null);
      },
    },
  ];

  formFieldItems = attachmentType !== '' ? [...formFieldItems, input] : formFieldItems;

  const validationSchema = Yup.object().shape({
    attachmentType: Yup.string().required(t('Type is required.')),
    attachmentURL: Yup.string().required(t('URL is required.')),
  });

  const displayWarning = () => {
    if (attachmentType === 'STICKER') {
      return (
        <div className={styles.FormHelperText}>
          <ol>
            <li>{t('Animated stickers are not supported.')}</li>
            <li>{t('Captions along with stickers are not supported.')}</li>
          </ol>
        </div>
      );
    }
    return (
      <div className={styles.FormHelperText}>
        <ol>
          <li>{t('Captions along with audio are not supported.')}</li>
        </ol>
      </div>
    );
  };

  const onSubmitHandle = (itemData: { attachmentURL: any; attachmentType: any }) => {
    setAttachmentType(itemData.attachmentType);
    setAttachmentURL(itemData.attachmentURL);
    setAttachment(false);
  };

  const addAttachment = (event: any) => {
    const media = event.target.files[0];

    if (media) {
      const mediaName = media.name;
      const extension = mediaName.slice((Math.max(0, mediaName.lastIndexOf('.')) || Infinity) + 1);
      const shortenedName = mediaName.length > 15 ? `${mediaName.slice(0, 15)}...` : mediaName;
      setFileName(shortenedName);
      setUploading(true);
      uploadMedia({
        variables: {
          media,
          extension,
        },
      });
    }
  };

  const form = (
    <Formik
      enableReinitialize
      initialValues={{ attachmentURL, attachmentType }}
      validationSchema={validationSchema}
      onSubmit={(itemData) => {
        if (!errors) {
          onSubmitHandle(itemData);
        }
      }}
    >
      {({ submitForm }) => (
        <Form className={styles.Form} data-testid="formLayout" encType="multipart/form-data">
          <DialogBox
            titleAlign="left"
            title={t('Add attachments to message')}
            handleOk={() => {
              submitForm();
            }}
            handleCancel={() => {
              setAttachment(false);
              setAttachmentType('');
              setAttachmentURL('');
              setAttachmentAdded(false);
            }}
            buttonOk={t('Add')}
            alignButtons="left"
            disableOk={verifying}
          >
            <div className={styles.DialogContent} data-testid="attachmentDialog">
              {formFieldItems.map((field: any) => (
                <Field {...field} key={field.name} validateURL={errors} />
              ))}
              {attachmentType !== '' && (
                <div className={styles.CrossIcon}>
                  <CrossIcon
                    data-testid="crossIcon"
                    onClick={() => {
                      setAttachmentType('');
                      setAttachmentURL('');
                      setAttachmentAdded(false);
                      setErrors(null);
                      setFileName(null);
                    }}
                  />
                </div>
              )}
              <div className={styles.FormError}>{errors}</div>
            </div>
            {attachmentType !== '' && (
              <>
                <div className={styles.UploadContainer}>
                  <label
                    className={`${uploadDisabled ? styles.UploadDisabled : styles.UploadEnabled} ${
                      fileName && attachmentURL ? styles.Uploaded : ''
                    }`}
                    htmlFor="uploadFile"
                  >
                    {!uploadPermission && <AlertIcon className={styles.AlertIcon} />}
                    <span>
                      {fileName !== null ? (
                        fileName
                      ) : (
                        <>
                          <UploadIcon className={styles.UploadIcon} /> Upload File
                        </>
                      )}

                      <input
                        type="file"
                        id="uploadFile"
                        data-testid="uploadFile"
                        onClick={(event) => {
                          if (uploadDisabled) {
                            event.preventDefault();
                          }
                        }}
                        onChange={(event) => {
                          addAttachment(event);
                        }}
                      />
                    </span>
                  </label>
                </div>
                {uploading && <div className={styles.WaitUpload}>Please wait for upload</div>}
              </>
            )}
            {!uploadPermission && attachmentType !== '' && (
              <div className={styles.FormHelperText}>
                {t('Please integrate Google Cloud Storage to use the upload')}
              </div>
            )}
            {attachmentType === 'STICKER' || attachmentType === 'AUDIO' ? displayWarning() : null}
          </DialogBox>
        </Form>
      )}
    </Formik>
  );

  return form;
}
Example #26
Source File: report.tsx    From core with GNU Affero General Public License v3.0 4 votes vote down vote up
ReportBot: NextPage<ReportBotProps> = ({ data, user, csrfToken }) => {
	const [ reportRes, setReportRes ] = useState<ResponseProps<unknown>>(null)
	if(!data?.id) return <NotFound />
	if(!user) return <Login>
		<NextSeo title='신고하기' />
	</Login>
	return <Container paddingTop className='py-10'>
		<NextSeo title={`${data.name} 신고하기`} />
		<Link href={makeBotURL(data)}>
			<a className='text-blue-500 hover:opacity-80'><i className='fas fa-arrow-left mt-3 mb-3' /> <strong>{data.name}</strong>{getJosaPicker('로')(data.name)} 돌아가기</a>
		</Link>
		{
			reportRes?.code === 200 ? <Message type='success'>
				<h2 className='text-lg font-semibold'>성공적으로 제출하였습니다!</h2>
				<p>더 자세한 설명이 필요할 수 있습니다. <strong>반드시 <a className='text-blue-600 hover:text-blue-500' href='/discord'>공식 디스코드</a>에 참여해주세요!!</strong></p>
			</Message> : <Formik onSubmit={async (body) => {
				const res = await Fetch(`/bots/${data.id}/report`, { method: 'POST', body: JSON.stringify(body) })
				setReportRes(res)
			}} validationSchema={ReportSchema} initialValues={{
				category: null,
				description: '',
				_csrf: csrfToken
			}}>
				{
					({ errors, touched, values, setFieldValue }) => (
						<Form>
							<div className='mb-5'>
								{
									reportRes && <div className='my-5'>
										<Message type='error'>
											<h2 className='text-lg font-semibold'>{reportRes.message}</h2>
											<ul className='list-disc'>
												{reportRes.errors?.map((el, n) => <li key={n}>{el}</li>)}
											</ul>
										</Message>
									</div>
								}
								<h3 className='font-bold'>신고 구분</h3>
								<p className='text-gray-400 text-sm mb-1'>해당되는 항목을 선택해주세요.</p>
								{
									reportCats.map(el => 
										<div key={el}>
											<label>
												<Field type='radio' name='category' value={el} className='mr-1.5 py-2' />
												{el}
											</label>
										</div>
									)
								}
								<div className='mt-1 text-red-500 text-xs font-light'>{errors.category && touched.category ? errors.category : null}</div>
								{
									values.category && <>
										{
											{
												[reportCats[2]]: <Message type='info'>
													<h3 className='font-bold text-xl'>본인 혹은 다른 사람이 위험에 처해 있나요?</h3>
													<p>당신은 소중한 사람입니다.</p>
													<p className='list-disc list-item list-inside'>자살예방상담전화 1393 | 청소년전화 1388</p>
												</Message>,
												[reportCats[5]]: <DMCA values={values} errors={errors} touched={touched} setFieldValue={setFieldValue} />,
												[reportCats[6]]: <Message type='warning'>
													<h3 className='font-bold text-xl'>디스코드 약관을 위반사항을 신고하시려고요?</h3>
													<p><a className='text-blue-400' target='_blank' rel='noreferrer' href='http://dis.gd/report'>디스코드 문의</a>를 통해 직접 디스코드에 신고하실 수도 있습니다.</p>
												</Message>
                        
											}[values.category]
										}
										{
											!['오픈소스 라이선스, 저작권 위반 등 권리 침해'].includes(values.category) && <>
												<h3 className='font-bold mt-2'>설명</h3>
												<p className='text-gray-400 text-sm mb-1'>최대한 자세하게 기재해주세요.</p>
												<TextField values={values} errors={errors} touched={touched} setFieldValue={setFieldValue} />
											</>
										}
									</>
								}
							</div>
						</Form>
					)
				}
			</Formik>
		}
	</Container>
}
Example #27
Source File: Billing.tsx    From glific-frontend with GNU Affero General Public License v3.0 4 votes vote down vote up
BillingForm: React.FC<BillingProps> = () => {
  const stripe = useStripe();
  const elements = useElements();

  const [name, setName] = useState('');
  const [email, setEmail] = useState('');

  const [loading, setLoading] = useState(false);
  const [disable, setDisable] = useState(false);
  const [paymentMethodId, setPaymentMethodId] = useState('');
  const [cardError, setCardError] = useState<any>('');
  const [alreadySubscribed, setAlreadySubscribed] = useState(false);
  const [pending, setPending] = useState(false);
  const [couponApplied, setCouponApplied] = useState(false);
  const [coupon] = useState('');

  const { t } = useTranslation();

  const validationSchema = Yup.object().shape({
    name: Yup.string().required(t('Name is required.')),
    email: Yup.string().email().required(t('Email is required.')),
  });

  // get organization billing details
  const {
    data: billData,
    loading: billLoading,
    refetch,
  } = useQuery(GET_ORGANIZATION_BILLING, {
    fetchPolicy: 'network-only',
  });

  const [getCouponCode, { data: couponCode, loading: couponLoading, error: couponError }] =
    useLazyQuery(GET_COUPON_CODE, {
      onCompleted: ({ getCouponCode: couponCodeResult }) => {
        if (couponCodeResult.code) {
          setCouponApplied(true);
        }
      },
    });
  const [getCustomerPortal, { loading: portalLoading }] = useLazyQuery(GET_CUSTOMER_PORTAL, {
    fetchPolicy: 'network-only',
    onCompleted: (customerPortal: any) => {
      window.open(customerPortal.customerPortal.url, '_blank');
    },
  });

  const formFieldItems = [
    {
      component: Input,
      name: 'name',
      type: 'text',
      placeholder: 'Your Organization Name',
      disabled: alreadySubscribed || pending || disable,
    },
    {
      component: Input,
      name: 'email',
      type: 'text',
      placeholder: 'Email ID',
      disabled: alreadySubscribed || pending || disable,
    },
  ];

  useEffect(() => {
    // Set name and email if a customer is already created
    if (billData && billData.getOrganizationBilling?.billing) {
      const billing = billData.getOrganizationBilling?.billing;
      setName(billing?.name);
      setEmail(billing?.email);

      if (billing?.stripeSubscriptionStatus === null) {
        setPending(false);
      }
    }
  }, [billData]);

  const [updateBilling] = useMutation(UPDATE_BILLING);
  const [createBilling] = useMutation(CREATE_BILLING);

  const [createSubscription] = useMutation(CREATE_BILLING_SUBSCRIPTION, {
    onCompleted: (data) => {
      const result = JSON.parse(data.createBillingSubscription.subscription);
      // needs additional security (3d secure)
      if (result.status === 'pending') {
        if (stripe) {
          stripe
            .confirmCardSetup(result.client_secret, {
              payment_method: paymentMethodId,
            })
            .then((securityResult: any) => {
              if (securityResult.error?.message) {
                setNotification(securityResult.error?.message, 'warning');
                setLoading(false);
                refetch().then(({ data: refetchedData }) => {
                  updateBilling({
                    variables: {
                      id: refetchedData.getOrganizationBilling?.billing?.id,
                      input: {
                        stripeSubscriptionId: null,
                        stripeSubscriptionStatus: null,
                      },
                    },
                  }).then(() => {
                    refetch();
                  });
                });
              } else if (securityResult.setupIntent.status === 'succeeded') {
                setDisable(true);
                setLoading(false);
                setNotification('Your billing account is setup successfully');
              }
            });
        }
      } // successful subscription
      else if (result.status === 'active') {
        refetch();
        setDisable(true);
        setLoading(false);
        setNotification('Your billing account is setup successfully');
      }
    },
    onError: (error) => {
      refetch();
      setNotification(error.message, 'warning');
      setLoading(false);
    },
  });

  if (billLoading || portalLoading) {
    return <Loading />;
  }

  // check if the organization is already subscribed or in pending state
  if (billData && !alreadySubscribed && !pending) {
    const billingDetails = billData.getOrganizationBilling?.billing;
    if (billingDetails) {
      const { stripeSubscriptionId, stripeSubscriptionStatus } = billingDetails;
      if (stripeSubscriptionId && stripeSubscriptionStatus === 'pending') {
        setPending(true);
      } else if (stripeSubscriptionId && stripeSubscriptionStatus === 'active') {
        setAlreadySubscribed(true);
      }
    }
  }

  const stripePayment = async () => {
    if (!stripe || !elements) {
      // Stripe.js has not loaded yet. Make sure to disable
      // form submission until Stripe.js has loaded.
      return;
    }

    // Get a reference to a mounted CardElement. Elements knows how
    // to find your CardElement because there can only ever be one of
    // each type of element.
    const cardElement: any = elements.getElement(CardElement);

    // create a payment method
    const { error, paymentMethod } = await stripe.createPaymentMethod({
      type: 'card',
      card: cardElement,
    });

    if (error) {
      setLoading(false);
      refetch();
      setNotification(error.message ? error.message : 'An error occurred', 'warning');
    } else if (paymentMethod) {
      setPaymentMethodId(paymentMethod.id);

      const variables: any = {
        stripePaymentMethodId: paymentMethod.id,
      };

      if (couponApplied) {
        variables.couponCode = couponCode.getCouponCode.id;
      }

      await createSubscription({
        variables,
      });
    }
  };

  const handleSubmit = async (itemData: any) => {
    const { name: billingName, email: billingEmail } = itemData;
    setLoading(true);

    if (billData) {
      const billingDetails = billData.getOrganizationBilling?.billing;
      if (billingDetails) {
        // Check if customer needs to be updated
        if (billingDetails.name !== billingName || billingDetails.email !== billingEmail) {
          updateBilling({
            variables: {
              id: billingDetails.id,
              input: {
                name: billingName,
                email: billingEmail,
                currency: 'inr',
              },
            },
          })
            .then(() => {
              stripePayment();
            })
            .catch((error) => {
              setNotification(error.message, 'warning');
            });
        } else {
          stripePayment();
        }
      } else {
        // There is no customer created. Creating a customer first
        createBilling({
          variables: {
            input: {
              name: billingName,
              email: billingEmail,
              currency: 'inr',
            },
          },
        })
          .then(() => {
            stripePayment();
          })
          .catch((error) => {
            setNotification(error.message, 'warning');
          });
      }
    }
  };

  const backLink = (
    <div className={styles.BackLink}>
      <Link to="/settings">
        <BackIcon />
        Back to settings
      </Link>
    </div>
  );

  const cardElements = (
    <>
      <CardElement
        options={{ hidePostalCode: true }}
        className={styles.Card}
        onChange={(e) => {
          setCardError(e.error?.message);
        }}
      />
      <div className={styles.Error}>
        <small>{cardError}</small>
      </div>
      <div className={styles.Helper}>
        <small>Once subscribed you will be charged on basis of your usage automatically</small>
      </div>
    </>
  );

  const subscribed = (
    <div>
      <div className={styles.Subscribed}>
        <ApprovedIcon />
        You have an active subscription
        <div>
          Please <span>contact us</span> to deactivate
          <br />
          *Note that we do not store your credit card details, as Stripe securely does.
        </div>
      </div>

      <div
        aria-hidden
        className={styles.Portal}
        data-testid="customerPortalButton"
        onClick={() => {
          getCustomerPortal();
        }}
      >
        Visit Stripe portal <CallMadeIcon />
      </div>
    </div>
  );
  let paymentBody = alreadySubscribed || disable ? subscribed : cardElements;

  if (pending) {
    paymentBody = (
      <div>
        <div className={styles.Pending}>
          <PendingIcon className={styles.PendingIcon} />
          Your payment is in pending state
        </div>
        <div
          aria-hidden
          className={styles.Portal}
          data-testid="customerPortalButton"
          onClick={() => {
            getCustomerPortal();
          }}
        >
          Visit Stripe portal <CallMadeIcon />
        </div>
      </div>
    );
  }

  const couponDescription = couponCode && JSON.parse(couponCode.getCouponCode.metadata);
  const processIncomplete = !alreadySubscribed && !pending && !disable;
  return (
    <div className={styles.Form}>
      <Typography variant="h5" className={styles.Title}>
        <IconButton disabled className={styles.Icon}>
          <Settingicon />
        </IconButton>
        Billing
      </Typography>
      {backLink}
      <div className={styles.Description}>
        <div className={styles.UpperSection}>
          <div className={styles.Setup}>
            <div>
              <div className={styles.Heading}>One time setup</div>
              <div className={styles.Pricing}>
                <span>INR 15000</span> ($220)
              </div>
              <div className={styles.Pricing}>+ taxes</div>
              <ul className={styles.List}>
                <li>5hr consulting</li>
                <li>1 hr onboarding session</li>
              </ul>
            </div>
            <div>
              <div className={styles.Heading}>Monthly Recurring</div>
              <div className={styles.Pricing}>
                <span>INR 7,500</span> ($110)
              </div>
              <div className={styles.Pricing}>+ taxes</div>
              <ul className={styles.List}>
                <li>upto 250k messages</li>
                <li>1-10 users</li>
              </ul>
            </div>
          </div>
          <div className={styles.Additional}>
            <div className={styles.Heading}>Variable charges as usage increases</div>
            <div>For every staff member over 10 users – INR 150 ($2)</div>
            <div>For every 1K messages upto 1Mn messages – INR 10 ($0.14)</div>
            <div>For every 1K messages over 1Mn messages – INR 5 ($0.07)</div>
          </div>
        </div>
        <div className={styles.DottedSpaced} />
        <div className={styles.BottomSection}>
          <div className={styles.InactiveHeading}>
            Suspended or inactive accounts:{' '}
            <span className={styles.Amount}> INR 4,500/mo + taxes</span>
          </div>
        </div>
      </div>

      {couponApplied && (
        <div className={styles.CouponDescription}>
          <div className={styles.CouponHeading}>Coupon Applied!</div>
          <div>{couponDescription.description}</div>
        </div>
      )}

      {processIncomplete && couponError && (
        <div className={styles.CouponError}>
          <div>Invalid Coupon!</div>
        </div>
      )}

      <div>
        <Formik
          enableReinitialize
          validateOnBlur={false}
          initialValues={{
            name,
            email,
            coupon,
          }}
          validationSchema={validationSchema}
          onSubmit={(itemData) => {
            handleSubmit(itemData);
          }}
        >
          {({ values, setFieldError, setFieldTouched }) => (
            <Form>
              {processIncomplete && (
                <Field
                  component={Input}
                  name="coupon"
                  type="text"
                  placeholder="Coupon Code"
                  disabled={couponApplied}
                  endAdornment={
                    <InputAdornment position="end">
                      {couponLoading ? (
                        <CircularProgress />
                      ) : (
                        <div
                          aria-hidden
                          className={styles.Apply}
                          onClick={() => {
                            if (values.coupon === '') {
                              setFieldError('coupon', 'Please input coupon code');
                              setFieldTouched('coupon');
                            } else {
                              getCouponCode({ variables: { code: values.coupon } });
                            }
                          }}
                        >
                          {couponApplied ? (
                            <CancelOutlinedIcon
                              className={styles.CrossIcon}
                              onClick={() => setCouponApplied(false)}
                            />
                          ) : (
                            ' APPLY'
                          )}
                        </div>
                      )}
                    </InputAdornment>
                  }
                />
              )}
              {formFieldItems.map((field, index) => {
                const key = index;
                return <Field key={key} {...field} />;
              })}

              {paymentBody}

              {processIncomplete && (
                <Button
                  variant="contained"
                  data-testid="submitButton"
                  color="primary"
                  type="submit"
                  className={styles.Button}
                  disabled={!stripe || disable}
                  loading={loading}
                >
                  Subscribe for monthly billing
                </Button>
              )}
            </Form>
          )}
        </Formik>
      </div>
    </div>
  );
}
Example #28
Source File: report.tsx    From core with GNU Affero General Public License v3.0 4 votes vote down vote up
ReportUser: NextPage<ReportUserProps> = ({ data, user, csrfToken }) => {
	const [ reportRes, setReportRes ] = useState<ResponseProps<unknown>>(null)
	if(!data?.id) return <NotFound />
	if(!user) return <Login>
		<NextSeo title='신고하기' />
	</Login>
	if(user?.id === data.id) return <>
		<div className='flex items-center justify-center h-screen select-none'>
			<div className='container mx-auto px-20 md:text-left text-center'>
				<h1 className='text-6xl font-semibold'>저런..!</h1>
				<p className='text-gray-400 text-lg mt-2'>유감스럽게도 자기 자신은 신고할 수 없습니다.</p>
			</div>
		</div>
	</>
	return <Container paddingTop className='py-10'>
		<NextSeo title={`${data.username} 신고하기`} />
		<Link href={makeUserURL(data)}>
			<a className='text-blue-500 hover:opacity-80'><i className='fas fa-arrow-left mt-3 mb-3' /> <strong>{data.username}</strong>{getJosaPicker('로')(data.username)} 돌아가기</a>
		</Link>
		{
			reportRes?.code === 200 ? <Message type='success'>
				<h2 className='text-lg font-semibold'>성공적으로 제출하였습니다!</h2>
				<p>더 자세한 설명이 필요할 수 있습니다. <strong>반드시 <a className='text-blue-600 hover:text-blue-500' href='/discord'>공식 디스코드</a>에 참여해주세요!!</strong></p>
			</Message> : <Formik onSubmit={async (body) => {
				const res = await Fetch(`/users/${data.id}/report`, { method: 'POST', body: JSON.stringify(body) })
				setReportRes(res)
			}} validationSchema={ReportSchema} initialValues={{
				category: null,
				description: '',
				_csrf: csrfToken
			}}>
				{
					({ errors, touched, values, setFieldValue }) => (
						<Form>
							<div className='mb-5'>
								{
									reportRes && <div className='my-5'>
										<Message type='error'>
											<h2 className='text-lg font-semibold'>{reportRes.message}</h2>
											<ul className='list-disc'>
												{reportRes.errors?.map((el, n) => <li key={n}>{el}</li>)}
											</ul>
										</Message>
									</div>
								}
								<h3 className='font-bold'>신고 구분</h3>
								<p className='text-gray-400 text-sm mb-1'>해당되는 항목을 선택해주세요.</p>
								{
									reportCats.map(el => 
										<div key={el}>
											<label>
												<Field type='radio' name='category' value={el} className='mr-1.5 py-2' />
												{el}
											</label>
										</div>
									)
								}
								<div className='mt-1 text-red-500 text-xs font-light'>{errors.category && touched.category ? errors.category : null}</div>
								{
									values.category && <>
										{
											values.category === '오픈소스 라이선스, 저작권 위반 등 권리 침해' ? <DMCA values={values} errors={errors} touched={touched} setFieldValue={setFieldValue} /> :
												values.category === '괴롭힘, 모욕, 명예훼손' ? <>
													<Message type='info'>
														<h3 className='font-bold text-xl'>본인 혹은 다른 사람이 위험에 처해 있나요?</h3>
														<p>당신은 소중한 사람입니다.</p>
														<p className='list-disc list-item list-inside'>자살예방상담전화 1393 | 청소년전화 1388</p>
													</Message>
												</> : ''
										}
										{
											!['오픈소스 라이선스, 저작권 위반 등 권리 침해'].includes(values.category) && <>
												<h3 className='font-bold mt-2'>설명</h3>
												<p className='text-gray-400 text-sm mb-1'>최대한 자세하게 기재해주세요.</p>
												<TextField values={values} errors={errors} touched={touched} setFieldValue={setFieldValue} />
											</>
										}
									</>
								}
							</div>
						</Form>
					)
				}
			</Formik>
		}
	</Container>
}
Example #29
Source File: Register.tsx    From krmanga with MIT License 4 votes vote down vote up
function Register({ navigation, dispatch, isLogin, loading }: IProps) {

    const [disabled, setDisabled] = useState<boolean>(false);

    useEffect(() => {
        if (isLogin) {
            setTimeout(() => {
                navigation.goBack();
            }, 100);
        }
    }, [isLogin]);

    const onSubmit = (values: Values) => {

        if (disabled || loading) {
            return;
        }

        setDisabled(true);

        dispatch({
            type: "user/register",
            payload: values,
            callback: () => {
                setDisabled(false);
            }
        });
    };


    const cancel = (form: FormikProps<string>, field: FieldInputProps<string>) => {
        if (field.name === "account") {
            form.setFieldValue("account", "");
        } else if (field.name === "password") {
            form.setFieldValue("password", "");
        } else if (field.name === "repeat_password") {
            form.setFieldValue("repeat_password", "");
        } else if (field.name === "phone") {
            form.setFieldValue("phone", "");
        }
    };

    return (
        <ScrollView keyboardShouldPersistTaps="handled" style={styles.container}>
            <Formik
                initialValues={initialValues}
                onSubmit={onSubmit}
                validationSchema={customerValidation}>
                {({ handleSubmit }) => (
                    <View>
                        <Field
                            name="account"
                            placeholder="请输入用户名"
                            component={Input}
                            iconName={"icon-Account"}
                            cancel={cancel}
                        />
                        <Field
                            name="password"
                            placeholder="请输入密码"
                            component={Input}
                            iconName={"icon-mima"}
                            secureTextEntry
                            cancel={cancel}
                        />
                        <Field
                            name="repeat_password"
                            placeholder="请再输入密码"
                            component={Input}
                            iconName={"icon-mima"}
                            secureTextEntry
                            cancel={cancel}
                        />
                        <Field
                            name="phone"
                            placeholder="请输入手机号(选填)"
                            component={Input}
                            iconName={"icon-mobile-phone"}
                            cancel={cancel}
                        />
                        <View style={styles.jumpView}>
                            <Text style={styles.jumpTitle}>忘记密码?</Text>
                            <Touchable onPress={() => navigation.navigate("Login")}>
                                <Text style={styles.jumpTitle}>立即登录</Text>
                            </Touchable>
                        </View>
                        <Touchable disabled={disabled} onPress={handleSubmit} style={styles.login}>
                            <Text style={styles.loginText}>注册</Text>
                        </Touchable>
                    </View>
                )}
            </Formik>
        </ScrollView>
    );
}