formik#useFormik TypeScript Examples
The following examples show how to use
formik#useFormik.
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: useResetPasswordFormik.tsx From bouncecode-cms with GNU General Public License v3.0 | 7 votes |
function useResetPasswordFormik(
onSubmit: (
values: FormikValues,
formikHelpers: FormikHelpers<FormikValues>,
) => void | Promise<any>,
options?: Partial<FormikConfig<FormikValues>>,
) {
const {enqueueSnackbar} = useSnackbar();
const formik = useFormik({
...options,
initialValues: {
...initialValues,
...options?.initialValues,
},
validationSchema,
onSubmit,
});
useEffect(() => {
if (formik.submitCount > 0 && !formik.isSubmitting && !formik.isValid) {
enqueueSnackbar('누락된 입력 항목을 확인해주세요.', {
variant: 'error',
});
}
}, [formik.submitCount, formik.isSubmitting]);
return formik;
}
Example #2
Source File: RuleForm.tsx From netify with BSD 2-Clause "Simplified" License | 6 votes |
RuleForm = memo<RuleFormProps>(function RuleForm({initialRule, onSave, onCancel}) {
const initialValues = useMemo(() => serializeRuleForm(initialRule), [initialRule]);
const handleSubmit = useCallback(
(rawValue: RuleFormSchema) => {
const value = deserializeRuleForm(rawValue, initialRule.id, initialRule.active);
onSave(value);
},
[initialRule.id, initialRule.active, onSave],
);
const form = useFormik<RuleFormSchema>({
initialValues,
validateOnBlur: true,
validateOnChange: false,
validationSchema: ruleFormSchema,
onSubmit: handleSubmit,
});
return (
<div className={styles.root}>
<FormikProvider value={form}>
<Form className={styles.form}>
<RuleLabel />
<RuleFilter />
<RuleActionSwitcher />
<div className={styles.config}>
<RuleActionConfig />
</div>
<div className={styles.controls}>
<Button className={styles.saveButton} styleType='dark' type='submit'>
Save
</Button>
<Button onClick={onCancel}>Cancel</Button>
</div>
</Form>
</FormikProvider>
</div>
);
})
Example #3
Source File: useSignInFormik.tsx From bouncecode-cms with GNU General Public License v3.0 | 6 votes |
function useSigninFormik(
onSubmit: (
values: FormikValues,
formikHelpers: FormikHelpers<FormikValues>,
) => void | Promise<any>,
options?: Partial<FormikConfig<FormikValues>>,
) {
const {enqueueSnackbar} = useSnackbar();
const formik = useFormik({
...options,
initialValues: {
...initialValues,
...options?.initialValues,
},
validationSchema,
onSubmit,
});
useEffect(() => {
if (formik.submitCount > 0 && !formik.isSubmitting && !formik.isValid) {
enqueueSnackbar('누락된 입력 항목을 확인해주세요.', {
variant: 'error',
});
}
}, [formik.submitCount, formik.isSubmitting]);
return formik;
}
Example #4
Source File: useSignUpFormik.tsx From bouncecode-cms with GNU General Public License v3.0 | 6 votes |
function useSignUpFormik(
onSubmit: (
values: FormikValues,
formikHelpers: FormikHelpers<FormikValues>,
) => void | Promise<any>,
options?: Partial<FormikConfig<FormikValues>>,
) {
const {enqueueSnackbar} = useSnackbar();
const formik = useFormik({
...options,
initialValues: {
...initialValues,
...options?.initialValues,
},
validationSchema,
onSubmit,
});
useEffect(() => {
if (formik.submitCount > 0 && !formik.isSubmitting && !formik.isValid) {
enqueueSnackbar('누락된 입력 항목을 확인해주세요.', {
variant: 'error',
});
}
}, [formik.submitCount, formik.isSubmitting]);
return formik;
}
Example #5
Source File: useForm.ts From frontend with MIT License | 6 votes |
export default function useForm<Values>({
validateOnChange = true,
validateOnBlur = false,
...formikProps
}: FormikConfig<Values>): FormikType<Values> {
const formik = useFormik<Values>({
validateOnChange,
validateOnBlur,
...formikProps,
})
return { formik }
}
Example #6
Source File: AddAppDomain.tsx From ledokku with MIT License | 5 votes |
AddAppDomain = ({ appId, appDomainsRefetch }: AddDomainProps) => {
const toast = useToast();
const [addDomainMutation] = useAddDomainMutation();
const [showAddForm, setShowAddForm] = useState(false);
const formik = useFormik<{ domainName: string }>({
initialValues: {
domainName: '',
},
validateOnChange: true,
validationSchema: addAppDomainYupSchema,
onSubmit: async (values) => {
try {
await addDomainMutation({
variables: {
input: {
appId,
domainName: values.domainName,
},
},
});
await appDomainsRefetch();
toast.success('Domain added successfully');
formik.resetForm();
} catch (error) {
toast.error(error.message);
}
},
});
return (
<>
{!showAddForm ? (
<Button variant="outline" onClick={() => setShowAddForm(true)}>
Add custom domain
</Button>
) : (
<form onSubmit={formik.handleSubmit}>
<FormControl
id="domainName"
isInvalid={Boolean(
formik.errors.domainName && formik.touched.domainName
)}
>
<Input
placeholder="www.mydomain.com"
name="domainName"
value={formik.values.domainName}
onChange={formik.handleChange}
/>
<FormErrorMessage>{formik.errors.domainName}</FormErrorMessage>
</FormControl>
<ButtonGroup mt="4" spacing="2">
<Button isLoading={formik.isSubmitting} type="submit">
Save
</Button>
<Button
variant="outline"
disabled={formik.isSubmitting}
onClick={() => setShowAddForm(false)}
>
Cancel
</Button>
</ButtonGroup>
</form>
)}
</>
);
}
Example #7
Source File: debug.tsx From core with GNU Affero General Public License v3.0 | 5 votes |
ClientInfo = ():JSX.Element => {
const formik = useFormik({
initialValues: {
markdown: `<div align="center">
<h1>Hello, World</h1>
</div>
<kbd>X</kbd>키를 눌러 Joy를 표하세요.
\`\`\`
코드 블럭
\`\`\`
*도* **레** ***미*** __***파***__
[트위터](https://twitter.com/koreanbots)
https://github.com/koreanbots
`
},
onSubmit: ()=>{ alert('Pong') }
})
return <Container paddingTop className='pb-10'>
<h1 className='text-4xl font-bold mb-3 mt-3'>개발자모드</h1>
<h2 className='text-3xl font-semibold mb-4'>정보들</h2>
<Segment>
<div className='markdown-body text-black dark:text-white'>
<h1>빌드정보</h1>
<ul className='list-disc'>
<li>Tag: <code>{parseDockerhubTag(process.env.NEXT_PUBLIC_TAG)}</code></li>
<li>Version: <code>v{Package.version}</code></li>
<li>Hash: <code>{process.env.NEXT_PUBLIC_SOURCE_COMMIT}</code></li>
</ul>
</div>
</Segment>
<Divider />
<h2 className='text-3xl font-semibold mb-2'>테스트</h2>
<h3 className='text-2xl font-semibold mb-2'>마크다운</h3>
<Segment>
<div className='lg:flex'>
<div className='w-full lg:w-1/2 min-h-48'>
<textarea className='resize-none w-full h-full dark:bg-discord-dark outline-none p-5' name='markdown' value={formik.values.markdown} onChange={formik.handleChange}/>
</div>
<div className='w-full lg:w-1/2 p-10'>
<Markdown text={formik.values.markdown} />
</div>
</div>
</Segment>
</Container>
}
Example #8
Source File: Promotion.tsx From epcc-react-pwa-reference-storefront with GNU General Public License v3.0 | 5 votes |
Promotion: React.FC<PromotionProps> = (props) => {
const { promotionItems } = props;
const { t } = useTranslation();
const { updateCartItems } = useCartData();
const mcart = localStorage.getItem('mcart') || '';
const { addError } = useContext(APIErrorContext);
const initialValues:FormValues = {
promoCode: '',
};
const {handleSubmit, handleChange, values, errors, setErrors} = useFormik({
initialValues,
onSubmit: (values) => {
addPromotion(mcart, values.promoCode)
.then(() => {
updateCartItems();
setErrors({promoCode: ''});
values.promoCode = ''
})
.catch(error => {
console.error(error);
addError(error.errors);
})
},
});
const handleRemove = () => {
removeCartItem(mcart, promotionItems[0].id)
.then(() => {
updateCartItems();
})
.catch(error => {
addError(error.errors);
console.error(error);
})
};
return (
<div className="promotion">
<p className="promotion__txt">{t('gift-card')}</p>
{promotionItems && promotionItems.length > 0 ? (
<div className="promotion__wrapper">
<span className="promotion__code">{promotionItems[0].sku}</span>
<button className="epbtn --secondary" onClick={handleRemove}>{t('remove')}</button>
</div>
) : (
<form className="epform" onSubmit={handleSubmit}>
<div className={`epform__group ${errors.promoCode ? '--error' : ''}`}>
<input className="epform__input" id="promoCode" type="text" aria-label={t('promo-code')} onChange={handleChange} value={values.promoCode} />
<div className="epform__error">
{errors.promoCode ? errors.promoCode : null}
</div>
</div>
<div className="epform__group --btn-container">
<button className="epbtn --ghost" type="submit" disabled={!values.promoCode}>
{t('apply')}
</button>
</div>
</form>
)}
</div>
)
}
Example #9
Source File: index.tsx From tailchat with GNU General Public License v3.0 | 5 votes |
MetaForm: React.FC<MetaFormProps> = React.memo((props) => {
const initialValues = useMemo(() => {
return {
..._fromPairs(
props.fields.map((field) => [field.name, field.defaultValue ?? ''])
),
...props.initialValues,
};
}, [props.fields, props.initialValues]);
const [loading, setLoading] = useState(false);
useLayoutEffect(() => {
// 加载时提交一次initialValues
typeof props.onChange === 'function' && props.onChange(initialValues);
}, []);
const formik = useFormik({
initialValues,
validationSchema: props.schema,
onSubmit: async (values) => {
setLoading(true);
try {
_isFunction(props.onSubmit) && (await props.onSubmit(values));
} finally {
setLoading(false);
}
},
validate: (values) => {
_isFunction(props.onChange) && props.onChange(values);
},
});
const { handleSubmit, setFieldValue, values, errors } = formik;
const MetaFormContainer = getFormContainer();
if (_isNil(MetaFormContainer)) {
console.warn('MetaFormContainer 没有被注册');
return null;
}
const fieldsRender = useMemo(() => {
return props.fields.map((fieldMeta, i) => {
const fieldName = fieldMeta.name;
const value = values[fieldName];
const error = errors[fieldName];
const Component = getField(fieldMeta.type);
if (_isNil(Component)) {
return null;
} else {
return (
<Component
key={fieldName + i}
{...fieldMeta}
value={value}
error={error}
onChange={(val: any) => setFieldValue(fieldName, val)}
/>
);
}
});
}, [props.fields, values, errors, setFieldValue]);
return (
<MetaFormContext.Provider value={formik}>
<MetaFormContainer
loading={loading}
layout={props.layout ?? 'horizontal'}
submitLabel={props.submitLabel}
handleSubmit={handleSubmit}
canSubmit={_isEmpty(errors)}
>
{fieldsRender}
</MetaFormContainer>
</MetaFormContext.Provider>
);
})
Example #10
Source File: ChangePassword.tsx From mayoor with MIT License | 5 votes |
ChangePassword: React.FC = () => {
const { t } = useTranslation();
const [changePassword, { loading }] = useMutation<
ChangePasswordMutation,
ChangePasswordMutationVariables
>(CHANGE_PASSWORD_MUTATION);
const formik = useFormik<FormValues>({
initialValues: {
oldPassword: '',
newPassword: '',
newPasswordRepeat: '',
},
validate: (values) => {
const errors: FormikErrors<FormValues> = {};
if (values.newPassword !== values.newPasswordRepeat) {
errors.newPasswordRepeat = t('new_passwords_must_match');
}
return errors;
},
onSubmit: async ({ oldPassword, newPassword }) => {
try {
await changePassword({ variables: { oldPassword, newPassword } });
message.success(t('pwd_changed'));
formik.resetForm();
} catch (err) {
if (err instanceof ApolloError) {
if (err.graphQLErrors[0].extensions?.code === 'INVALID_PASSWORD') {
formik.setErrors({
oldPassword: t('old_pwd_incorrect'),
});
} else {
message.error(t('error_changing_pwd'));
}
}
}
},
});
const getPasswordField = (name: keyof FormValues, label: string) => {
const errorMessage = formik.touched[name] && formik.errors[name];
const status = errorMessage ? 'error' : '';
return (
<FormItemStyled validateStatus={status} help={errorMessage}>
<Input
prefix={<LockFilled />}
placeholder={label}
name={name}
onChange={formik.handleChange}
value={formik.values[name]}
type="password"
/>
</FormItemStyled>
);
};
return (
<form onSubmit={formik.handleSubmit}>
<h4>{t('Change your password')}</h4>
{getPasswordField('oldPassword', t('Old Password'))}
<Row gutter={16}>
<Col span={12}>{getPasswordField('newPassword', t('New Password'))}</Col>
<Col span={12}>
{getPasswordField('newPasswordRepeat', t('Repeat New Password'))}
</Col>
</Row>
<Button type="primary" htmlType="submit" loading={loading}>
{t('Change password')}
</Button>
</form>
);
}
Example #11
Source File: useCommentCreateFormik.tsx From bouncecode-cms with GNU General Public License v3.0 | 5 votes |
export function useCommentCreateFormik(
postId: string,
options?: Partial<FormikConfig<FormikValues>>,
) {
const {enqueueSnackbar} = useSnackbar();
const [create] = useCommentCreateMutation({
onCompleted: () => {
formik.handleReset(null);
},
});
const onSubmit = async (
values: FormikValues,
formikHelpers: FormikHelpers<FormikValues>,
) => {
return create({
variables: {
data: {
postId,
text: values.text,
payload: {},
},
},
refetchQueries: [CommentsDocument, CommentStatDocument],
});
};
const formik = useFormik({
...options,
initialValues: {
...initialValues,
...options?.initialValues,
},
validationSchema,
onSubmit,
});
useEffect(() => {
if (formik.submitCount > 0 && !formik.isSubmitting && !formik.isValid) {
enqueueSnackbar('누락된 입력 항목을 확인해주세요.', {
variant: 'error',
});
}
}, [formik.submitCount, formik.isSubmitting]);
return formik;
}
Example #12
Source File: Registration.tsx From Mokku with MIT License | 4 votes |
Registration = ({ user, location }: IProps) => {
const isRegistration = location.pathname === "/auth/register";
const [loading, setLoading] = React.useState(false);
const [error, setError] = React.useState("");
const [redirect, setRedirect] = React.useState<string | null>(null);
React.useEffect(() => {
if (user) {
setRedirect("/");
}
}, [user]);
const { values, handleBlur, handleChange, handleSubmit } = useFormik({
initialValues: {
name: "",
email: "",
password: "",
},
onSubmit: (values) => {
if (isRegistration) {
signUp(values, setLoading, setError);
} else {
signIn(values, setLoading, setError, setRedirect);
}
},
});
if (redirect) {
return <Redirect to={redirect} />;
}
return (
<div className="flex flex-column align-items-center justify-content-center h-100">
{loading && <ProgressBar />}
<Logo src="../mokku.svg" />
<h2>Mokku</h2>
<form className="flex flex-column p-relative">
<Route path="/auth/register">
<Input
placeholder="Name"
className="mb-8"
required
name="name"
value={values.name}
onChange={handleChange}
onBlur={handleBlur}
disabled={loading}
/>
</Route>
<Input
placeholder="Email"
className="mb-8"
required
name="email"
type="email"
value={values.email}
onChange={handleChange}
onBlur={handleBlur}
disabled={loading}
/>
<Input
placeholder="Password"
className="mb-8"
required
name="password"
type="password"
value={values.password}
onChange={handleChange}
onBlur={handleBlur}
disabled={loading}
/>
<div className="flex justify-content-between mt-8">
<div className="mr-8 flex flex-column justify-content-between">
<StyledLink to="/forget-password">Forget password?</StyledLink>
{isRegistration && <StyledLink to="/auth">Sign in</StyledLink>}
{!isRegistration && (
<StyledLink to="/auth/register">Sign up</StyledLink>
)}
</div>
<Button
disabled={loading}
color="white"
background="primary"
type="button"
onClick={() => handleSubmit()}
>
{isRegistration ? "SIGN UP" : "SIGN IN"}
</Button>
</div>
{error && (
<Text className="p-absolute" style={{ bottom: -48 }} color="alert">
{error}
</Text>
)}
</form>
</div>
);
}
Example #13
Source File: LoginForm.tsx From mayoor with MIT License | 4 votes |
LoginForm: React.FC = () => {
const dispatch = useAppDispatch();
const { t } = useTranslation();
const [login, { loading }] = useMutation<LoginMutation, LoginMutationVariables>(LOGIN_MUTATION);
const { errors, handleSubmit, values, handleChange, isValid, setErrors, touched } = useFormik<
FormValues
>({
initialValues: {
username: '',
password: '',
},
validate: (values) => {
const errors: FormikErrors<FormValues> = {};
if (!values.password) {
errors.password = t('password_required');
}
if (!values.username) {
errors.username = t('username_required');
}
return errors;
},
onSubmit: async ({ username, password }) => {
try {
const result = await login({ variables: { email: username, password } });
if (result.data?.login) {
dispatch({
type: 'SET_CURRENT_USER',
user: result.data.login.user,
});
localStorage.setItem('auth-token', result.data.login.token);
}
} catch (err) {
if (err instanceof ApolloError) {
if (err.graphQLErrors[0].extensions?.code === 'USER_NOT_FOUND') {
setErrors({
username: t('user_not_found'),
});
}
if (err.graphQLErrors[0].extensions?.code === 'INVALID_PASSWORD') {
setErrors({
password: t('invalid_password'),
});
}
}
}
},
});
return (
<CenteredWrapper>
<S.LoginWrapper onSubmit={handleSubmit}>
<S.Logo src={LogoImage} />
<S.FormItemStyled
validateStatus={touched.username && errors.username ? 'error' : ''}
help={touched.username && errors.username}
>
<Input
prefix={<UserOutlined />}
placeholder={t('Username')}
name="username"
onChange={handleChange}
value={values.username}
data-test-id="login-username"
/>
</S.FormItemStyled>
<S.FormItemStyled
validateStatus={touched.password && errors.password ? 'error' : ''}
help={touched.password && errors.password}
>
<Input
prefix={<LockFilled />}
placeholder={t('Password')}
name="password"
onChange={handleChange}
value={values.password}
type="password"
data-test-id="login-password"
/>
</S.FormItemStyled>
<Button
icon={<LoginOutlined />}
loading={loading}
disabled={!isValid}
htmlType="submit"
data-test-id="login-submit-button"
>
{t('Log In')}
</Button>
<S.LanguageSwitchWrapper>
<LanguageSwitch />
</S.LanguageSwitchWrapper>
</S.LoginWrapper>
</CenteredWrapper>
);
}
Example #14
Source File: settings.tsx From ledokku with MIT License | 4 votes |
Settings = () => {
const { id: databaseId } = useParams<{ id: string }>();
let history = useHistory();
const toast = useToast();
const [
destroyDatabaseMutation,
{ loading: destroyDbLoading },
] = useDestroyDatabaseMutation();
const { data, loading /* error */ } = useDatabaseByIdQuery({
variables: {
databaseId,
},
ssr: false,
skip: !databaseId,
});
const {
data: databaseInfoData,
loading: databaseInfoLoading,
} = useDatabaseInfoQuery({
variables: {
databaseId,
},
ssr: false,
skip: !databaseId,
pollInterval: 15000,
});
const DeleteDatabaseNameSchema = yup.object().shape({
databaseName: yup
.string()
.required('Required')
.test(
'Equals database name',
'Must match database name',
(val) => val === data?.database?.name
),
});
const formik = useFormik<{ databaseName: string }>({
initialValues: {
databaseName: '',
},
validateOnChange: true,
validationSchema: DeleteDatabaseNameSchema,
onSubmit: async (values) => {
try {
await destroyDatabaseMutation({
variables: {
input: { databaseId },
},
refetchQueries: [
{
query: DashboardDocument,
},
],
});
toast.success('Database deleted successfully');
history.push('/dashboard');
} catch (error) {
toast.error(error.message);
}
},
});
if (!data) {
return null;
}
// // TODO display error
if (loading) {
// TODO nice loading
return <p>Loading...</p>;
}
const { database } = data;
if (!database) {
// TODO nice 404
return <p>Database not found.</p>;
}
const databaseInfos = databaseInfoData?.databaseInfo.info
.map((data) => data.trim())
.map((info) => {
const name = info.split(':')[0];
const value = info.substring(info.indexOf(':') + 1).trim();
return { name, value };
});
return (
<div>
<HeaderContainer>
<Header />
<DatabaseHeaderInfo database={database} />
<DatabaseHeaderTabNav database={database} />
</HeaderContainer>
<Container maxW="5xl" mt={10}>
<div className="grid sm:grid-cols-1 md:grid-cols-1 lg:grid-cols-1 xl:grid-cols-1 gap-4 mt-10">
<div className="col-span-2 w-5/5">
<Heading as="h2" size="md" py={5}>
Infos
</Heading>
{databaseInfoLoading ? (
<p className="text-gray-400 text-sm">Loading...</p>
) : null}
{!databaseInfoLoading && databaseInfos
? databaseInfos.map((info) => (
<div key={info.name} className="py-2">
<div className="font-semibold">{info.name}</div>
<div>{info.value}</div>
</div>
))
: null}
</div>
<div className="w-3/3 mb-6">
<h1 className="text-lg font-bold py-5">Database settings</h1>
<h2 className="text-gray-400 w-6/6">
This action cannot be undone. This will permanently delete{' '}
{database.name} database and everything related to it. Please type{' '}
<b>{database.name}</b> to confirm deletion.
</h2>
<form onSubmit={formik.handleSubmit}>
<div className="grid md:grid-cols-3">
<div className="mt-4">
<FormInput
autoComplete="off"
id="databaseName"
name="databaseName"
value={formik.values.databaseName}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={Boolean(
formik.errors.databaseName && formik.touched.databaseName
)}
/>
{formik.errors.databaseName && formik.errors.databaseName ? (
<FormHelper status="error">
{formik.errors.databaseName}
</FormHelper>
) : null}
<Button
type="submit"
isLoading={destroyDbLoading}
disabled={
!formik.values.databaseName ||
!!formik.errors.databaseName
}
color="red"
className="mt-2"
>
Delete
</Button>
</div>
</div>
</form>
</div>
</div>
</Container>
</div>
);
}
Example #15
Source File: create-database.tsx From ledokku with MIT License | 4 votes |
CreateDatabase = () => {
const location = useLocation();
const history = useHistory();
const toast = useToast();
const { data: dataDb } = useDatabaseQuery();
const [arrayOfCreateDbLogs, setArrayofCreateDbLogs] = useState<RealTimeLog[]>(
[]
);
const [isTerminalVisible, setIsTerminalVisible] = useState(false);
const [createDatabaseMutation] = useCreateDatabaseMutation();
const [
isDbCreationSuccess,
setIsDbCreationSuccess,
] = useState<DbCreationStatus>();
useCreateDatabaseLogsSubscription({
onSubscriptionData: (data) => {
const logsExist = data.subscriptionData.data?.createDatabaseLogs;
if (logsExist) {
setArrayofCreateDbLogs((currentLogs) => {
return [...currentLogs, logsExist];
});
if (logsExist.type === 'end:success') {
setIsDbCreationSuccess(DbCreationStatus.SUCCESS);
} else if (logsExist.type === 'end:failure') {
setIsDbCreationSuccess(DbCreationStatus.FAILURE);
}
}
},
});
const createDatabaseSchema = yup.object({
type: yup
.string()
.oneOf(['POSTGRESQL', 'MYSQL', 'MONGODB', 'REDIS'])
.required(),
name: yup.string().when('type', (type: DatabaseTypes) => {
return yup
.string()
.required('Database name is required')
.matches(/^[a-z0-9-]+$/)
.test(
'Name already exists',
`You already have created ${type} database with this name`,
(name) =>
!dataDb?.databases.find(
(db) => db.name === name && type === db.type
)
);
}),
});
const [
isDokkuPluginInstalled,
{ data, loading, error: isDokkuPluginInstalledError },
] = useIsPluginInstalledLazyQuery({
// we poll every 5 sec
pollInterval: 5000,
});
const formik = useFormik<{ name: string; type: DatabaseTypes }>({
initialValues: {
name: location.state ? (location.state as string) : '',
type: 'POSTGRESQL',
},
validateOnChange: true,
validationSchema: createDatabaseSchema,
onSubmit: async (values) => {
try {
await createDatabaseMutation({
variables: {
input: { name: values.name, type: values.type },
},
});
setIsTerminalVisible(true);
trackGoal(trackingGoals.createDatabase, 0);
} catch (error) {
toast.error(error.message);
}
},
});
const isPluginInstalled = data?.isPluginInstalled.isPluginInstalled;
const handleNext = () => {
setIsTerminalVisible(false);
const dbId = arrayOfCreateDbLogs[arrayOfCreateDbLogs.length - 1].message;
history.push(`database/${dbId}`);
};
// Effect for checking whether plugin is installed
useEffect(() => {
isDokkuPluginInstalled({
variables: {
pluginName: dbTypeToDokkuPlugin(formik.values.type),
},
});
}, [formik.values.type, isPluginInstalled, isDokkuPluginInstalled]);
// Effect for db creation
useEffect(() => {
isDbCreationSuccess === DbCreationStatus.FAILURE
? toast.error('Failed to create database')
: isDbCreationSuccess === DbCreationStatus.SUCCESS &&
toast.success('Database created successfully');
}, [isDbCreationSuccess, toast]);
return (
<>
<HeaderContainer>
<Header />
</HeaderContainer>
<Container maxW="5xl" my="4">
<Heading as="h2" size="md">
Create a new database
</Heading>
<Box mt="12">
{isTerminalVisible ? (
<>
<Text mb="2">
Creating <b>{formik.values.type}</b> database{' '}
<b>{formik.values.name}</b>
</Text>
<Text mb="2" color="gray.500">
Creating database usually takes a couple of minutes. Breathe in,
breathe out, logs are about to appear below:
</Text>
<Terminal>
{arrayOfCreateDbLogs.map((log) => (
<Text key={arrayOfCreateDbLogs.indexOf(log)} size="small">
{log.message}
</Text>
))}
</Terminal>
{!!isDbCreationSuccess &&
isDbCreationSuccess === DbCreationStatus.SUCCESS ? (
<Box mt="12" display="flex" justifyContent="flex-end">
<Button
onClick={() => handleNext()}
rightIcon={<FiArrowRight size={20} />}
>
Next
</Button>
</Box>
) : !!isDbCreationSuccess &&
isDbCreationSuccess === DbCreationStatus.FAILURE ? (
<Box mt="12" display="flex" justifyContent="flex-end">
<Button
onClick={() => {
setIsTerminalVisible(false);
formik.resetForm();
}}
rightIcon={<FiArrowLeft size={20} />}
>
Back
</Button>
</Box>
) : null}
</>
) : (
<Box mt="8">
<form onSubmit={formik.handleSubmit}>
<Box mt="12">
{loading && (
<Center>
<Spinner />
</Center>
)}
{isDokkuPluginInstalledError ? (
<Alert
status="error"
variant="top-accent"
flexDirection="column"
alignItems="flex-start"
borderBottomRadius="base"
boxShadow="md"
>
<AlertTitle mr={2}>Request failed</AlertTitle>
<AlertDescription>
{isDokkuPluginInstalledError.message}
</AlertDescription>
</Alert>
) : null}
{data?.isPluginInstalled.isPluginInstalled === false &&
!loading && (
<>
<Text mt="3">
Before creating a{' '}
<b>{formik.values.type.toLowerCase()}</b> database,
you will need to run this command on your dokku
server.
</Text>
<Terminal>{`sudo dokku plugin:install https://github.com/dokku/dokku-${dbTypeToDokkuPlugin(
formik.values.type
)}.git ${dbTypeToDokkuPlugin(
formik.values.type
)}`}</Terminal>
<Text mt="3">
Couple of seconds later you will be able to proceed
further.
</Text>
</>
)}
{data?.isPluginInstalled.isPluginInstalled === true &&
!loading && (
<SimpleGrid columns={{ sm: 1, md: 3 }}>
<FormControl
id="name"
isInvalid={Boolean(
formik.errors.name && formik.touched.name
)}
>
<FormLabel>Database name</FormLabel>
<Input
autoComplete="off"
id="name"
name="name"
value={formik.values.name}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
<FormErrorMessage>
{formik.errors.name}
</FormErrorMessage>
</FormControl>
</SimpleGrid>
)}
</Box>
<Box mt="12">
<Text mb="2">Choose your database</Text>
<Grid
templateColumns={{
base: 'repeat(2, minmax(0, 1fr))',
md: 'repeat(4, minmax(0, 1fr))',
}}
gap="4"
>
<DatabaseBox
selected={formik.values.type === 'POSTGRESQL'}
label="PostgreSQL"
icon={<PostgreSQLIcon size={40} />}
onClick={() => formik.setFieldValue('type', 'POSTGRESQL')}
/>
<DatabaseBox
selected={formik.values.type === 'MYSQL'}
label="MySQL"
icon={<MySQLIcon size={40} />}
onClick={() => formik.setFieldValue('type', 'MYSQL')}
/>
<DatabaseBox
selected={formik.values.type === 'MONGODB'}
label="Mongo"
icon={<MongoIcon size={40} />}
onClick={() => formik.setFieldValue('type', 'MONGODB')}
/>
<DatabaseBox
selected={formik.values.type === 'REDIS'}
label="Redis"
icon={<RedisIcon size={40} />}
onClick={() => formik.setFieldValue('type', 'REDIS')}
/>
</Grid>
</Box>
<Box mt="12" display="flex" justifyContent="flex-end">
<Button
isLoading={formik.isSubmitting}
disabled={
data?.isPluginInstalled.isPluginInstalled === false ||
!formik.values.name ||
!!formik.errors.name ||
!dataDb?.databases
}
rightIcon={<FiArrowRight size={20} />}
type="submit"
>
Create
</Button>
</Box>
</form>
</Box>
)}
</Box>
</Container>
</>
);
}
Example #16
Source File: create-app.tsx From ledokku with MIT License | 4 votes |
CreateApp = () => {
const history = useHistory();
const toast = useToast();
const createAppSchema = yup.object({
type: yup
.string()
.oneOf(['GITHUB', 'GITLAB', 'DOCKER', 'DOKKU'])
.required(),
});
const formik = useFormik<{ type: AppTypes }>({
initialValues: {
type: AppTypes.GITHUB,
},
validateOnChange: true,
validationSchema: createAppSchema,
onSubmit: async (values) => {
try {
values.type === AppTypes.GITHUB
? history.push('/create-app-github')
: history.push('/create-app-dokku');
} catch (error) {
toast.error(error.message);
}
},
});
return (
<>
<HeaderContainer>
<Header />
</HeaderContainer>
<Container maxW="5xl" my="4">
<Heading py="2" as="h2" size="md">
App source
</Heading>
<Box mt="24">
<Box mt="4">
<form onSubmit={formik.handleSubmit}>
<Box mt="20">
<Text mb="5" color="gray.500">
Choose between creating app from a Github repository or
creating a standalone Dokku app.
</Text>
<Grid
templateColumns={{
base: 'repeat(2, minmax(0, 1fr))',
md: 'repeat(4, minmax(0, 1fr))',
}}
gap="4"
>
<SourceBox
selected={formik.values.type === AppTypes.GITHUB}
label="GitHub"
icon={<GithubIcon size={40} />}
onClick={() => formik.setFieldValue('type', 'GITHUB')}
/>
<SourceBox
selected={formik.values.type === AppTypes.DOKKU}
label="Dokku"
icon={
<Image
boxSize="48px"
objectFit="cover"
src="/dokku.png"
alt="dokkuLogo"
/>
}
onClick={() => formik.setFieldValue('type', 'DOKKU')}
/>
<SourceBox
selected={formik.values.type === AppTypes.GITLAB}
label="Gitlab"
icon={<GitlabIcon size={40} />}
badge={
<Badge ml="1" colorScheme="red">
Coming soon
</Badge>
}
// Uncomment this when we can handle docker deployments
// onClick={() => formik.setFieldValue('type', 'GITLAB')}
/>
<SourceBox
selected={formik.values.type === AppTypes.DOCKER}
label="Docker"
icon={<DockerIcon size={40} />}
badge={
<Badge ml="1" colorScheme="red">
Coming soon
</Badge>
}
// Uncomment this when we can handle docker deployments
// onClick={() => formik.setFieldValue('type', 'DOCKER')}
/>
</Grid>
</Box>
<Box mt="36" display="flex" justifyContent="flex-end">
<Button
isLoading={formik.isSubmitting}
disabled={!formik.values.type || !!formik.errors.type}
rightIcon={<FiArrowRight size={20} />}
type="submit"
>
Next
</Button>
</Box>
</form>
</Box>
</Box>
</Container>
</>
);
}
Example #17
Source File: create-app-github.tsx From ledokku with MIT License | 4 votes |
CreateAppGithub = () => {
const history = useHistory();
const toast = useToast();
const { user } = useAuth();
const { data: dataApps } = useAppsQuery();
const [isNewWindowClosed, setIsNewWindowClosed] = useState(false);
const [selectedRepo, setSelectedRepo] = useState<Repository>();
const [selectedBranch, setSelectedBranch] = useState('');
const [isProceedModalOpen, setIsProceedModalOpen] = useState(false);
const {
data: installationData,
loading: installationLoading,
} = useGithubInstallationIdQuery({ fetchPolicy: 'network-only' });
const [
getRepos,
{ data: reposData, loading: reposLoading },
] = useRepositoriesLazyQuery({ fetchPolicy: 'network-only' });
const [
getBranches,
{ data: branchesData, loading: branchesLoading },
] = useBranchesLazyQuery({ fetchPolicy: 'network-only' });
const [arrayOfCreateAppLogs, setArrayOfCreateAppLogs] = useState<
RealTimeLog[]
>([]);
const [isTerminalVisible, setIsTerminalVisible] = useState(false);
const [isToastShown, setIsToastShown] = useState(false);
const [createAppGithubMutation, { loading }] = useCreateAppGithubMutation();
const [
isAppCreationSuccess,
setIsAppCreationSuccess,
] = useState<AppCreationStatus>();
useAppCreateLogsSubscription({
onSubscriptionData: (data) => {
const logsExist = data.subscriptionData.data?.appCreateLogs;
if (logsExist) {
setArrayOfCreateAppLogs((currentLogs) => {
return [...currentLogs, logsExist];
});
if (logsExist.type === 'end:success') {
setIsAppCreationSuccess(AppCreationStatus.SUCCESS);
} else if (logsExist.type === 'end:failure') {
setIsAppCreationSuccess(AppCreationStatus.FAILURE);
}
}
},
});
const createAppGithubSchema = yup.object().shape({
name: yup
.string()
.required('App name is required')
.matches(/^[a-z0-9-]+$/)
.test(
'Name exists',
'App with this name already exists',
(val) => !dataApps?.apps.find((app) => app.name === val)
),
repo: yup.object({
fullName: yup.string().required(),
id: yup.string().required(),
name: yup.string().required(),
}),
installationId: yup.string().required(),
gitBranch: yup.string().optional(),
});
const formik = useFormik<{
name: string;
repo: {
fullName: string;
id: string;
name: string;
};
installationId: string;
gitBranch: string;
}>({
initialValues: {
name: '',
repo: {
fullName: '',
id: '',
name: '',
},
installationId: '',
gitBranch: '',
},
validateOnChange: true,
validationSchema: createAppGithubSchema,
onSubmit: async (values) => {
if (installationData) {
try {
await createAppGithubMutation({
variables: {
input: {
name: values.name,
gitRepoFullName: values.repo.fullName,
branchName: values.gitBranch,
gitRepoId: values.repo.id,
githubInstallationId: values.installationId,
},
},
});
setIsTerminalVisible(true);
} catch (error) {
error.message === 'Not Found'
? toast.error(`Repository : ${values.repo.fullName} not found`)
: toast.error(error.message);
}
}
},
});
const handleNext = () => {
setIsTerminalVisible(false);
const appId = arrayOfCreateAppLogs[arrayOfCreateAppLogs.length - 1].message;
history.push(`app/${appId}`, 'new');
trackGoal(trackingGoals.createAppGithub, 0);
};
const handleOpen = () => {
const newWindow = window.open(
`https://github.com/apps/${config.githubAppName}/installations/new`,
'Install App',
'resizable=1, scrollbars=1, fullscreen=0, height=1000, width=1020,top=' +
window.screen.width +
', left=' +
window.screen.width +
', toolbar=0, menubar=0, status=0'
);
const timer = setInterval(async () => {
if (newWindow && newWindow.closed) {
setIsNewWindowClosed(true);
clearInterval(timer);
}
}, 100);
};
useEffect(() => {
if (!installationLoading && installationData && isNewWindowClosed) {
getRepos({
variables: {
installationId: installationData.githubInstallationId.id,
},
});
setIsNewWindowClosed(false);
}
}, [
installationData,
installationLoading,
isNewWindowClosed,
setIsNewWindowClosed,
getRepos,
]);
useEffect(() => {
if (
!installationLoading &&
installationData &&
!reposLoading &&
reposData &&
selectedRepo
) {
getBranches({
variables: {
installationId: installationData.githubInstallationId.id,
repositoryName: selectedRepo.name,
},
});
}
}, [
installationData,
installationLoading,
reposData,
reposLoading,
getBranches,
selectedRepo?.name,
selectedRepo,
]);
const handleChangeRepo = (active: RepoOption) => {
setSelectedRepo(active.value);
setSelectedBranch('');
if (installationData) {
formik.setValues({
name: active.value.name,
installationId: installationData?.githubInstallationId.id,
repo: {
fullName: active.value.fullName,
name: active.value.name,
id: active.value.id,
},
gitBranch: '',
});
}
};
const handleChangeBranch = (active: BranchOption) => {
setSelectedBranch(active.value.name);
formik.setFieldValue('gitBranch', active.value.name);
};
const repoOptions: RepoOption[] = [];
if (reposData && !reposLoading) {
reposData?.repositories.map((r) =>
repoOptions.push({ value: r, label: r.fullName })
);
}
let branchOptions: BranchOption[] = [];
if (branchesData && !branchesLoading) {
branchesData.branches.map((b) =>
branchOptions.push({ value: b, label: b.name })
);
}
useEffect(() => {
if (installationData && !installationLoading) {
getRepos({
variables: {
installationId: installationData?.githubInstallationId.id,
},
});
}
}, [installationLoading, getRepos, installationData]);
useEffect(() => {
if (selectedRepo && installationData) {
getBranches({
variables: {
installationId: installationData?.githubInstallationId.id,
repositoryName: selectedRepo.name,
},
});
}
}, [selectedRepo, getBranches, installationData]);
// Effect for app creation
useEffect(() => {
isAppCreationSuccess === AppCreationStatus.FAILURE && !isToastShown
? toast.error('Failed to create an app') && setIsToastShown(true)
: isAppCreationSuccess === AppCreationStatus.SUCCESS &&
!isToastShown &&
toast.success('App created successfully') &&
setIsToastShown(true);
}, [isToastShown, isAppCreationSuccess, toast]);
return (
<>
<HeaderContainer>
<Header />
</HeaderContainer>
<Container maxW="5xl" mt={10}>
{isTerminalVisible ? (
<>
<p className="mb-2 ">
Creating <b>{formik.values.name}</b> app from{' '}
<b>{formik.values.repo.name}</b>
</p>
<p className="text-gray-500 mb-2">
Creating app usually takes a couple of minutes. Breathe in,
breathe out, logs are about to appear below:
</p>
<Terminal className={'w-6/6'}>
{arrayOfCreateAppLogs.map((log) => (
<p
key={arrayOfCreateAppLogs.indexOf(log)}
className={'text-s leading-5'}
>
{log.message?.replaceAll('[1G', '')}
</p>
))}
</Terminal>
{!!isAppCreationSuccess &&
isAppCreationSuccess === AppCreationStatus.SUCCESS ? (
<div className="mt-12 flex justify-end">
<Button
onClick={() => handleNext()}
color="grey"
iconEnd={<FiArrowRight size={20} />}
>
Next
</Button>
</div>
) : !!isAppCreationSuccess &&
isAppCreationSuccess === AppCreationStatus.FAILURE ? (
<div className="mt-12 flex justify-start">
<Button
onClick={() => {
setIsTerminalVisible(false);
formik.resetForm();
}}
color="grey"
iconEnd={<FiArrowLeft size={20} />}
>
Back
</Button>
</div>
) : null}
</>
) : (
<>
<Heading as="h2" size="md">
Create a new GitHub application
</Heading>
{installationData &&
!installationLoading &&
reposData &&
!reposLoading ? (
<>
<Text color="gray.400">
When you push to Git, your application will be redeployed
automatically.
</Text>
<Grid
templateColumns={{
sm: 'repeat(1, 1fr)',
md: 'repeat(3, 1fr)',
}}
>
<GridItem colSpan={2}>
<Flex alignItems="center" mt="12">
<Avatar
size="sm"
name={user?.userName}
src={user?.avatarUrl}
/>
<Text ml="2" fontWeight="bold">
{user?.userName}
</Text>
</Flex>
<form onSubmit={formik.handleSubmit}>
<Box mt="8">
<FormLabel>Repository</FormLabel>
<Select
placeholder="Select repository"
isSearchable={false}
onChange={handleChangeRepo}
options={repoOptions}
/>
</Box>
<Text mt="1" color="gray.400" fontSize="sm">
Can't see your repo in the list?{' '}
<Link
onClick={() => setIsProceedModalOpen(true)}
textDecoration="underline"
>
Configure the GitHub app.
</Link>
</Text>
<Box mt="8">
<FormLabel>Branch to deploy</FormLabel>
<Select
placeholder="Select branch"
isSearchable={false}
disabled={
!branchesData ||
branchesLoading ||
reposLoading ||
!reposData
}
onChange={handleChangeBranch}
options={branchOptions}
/>
</Box>
<Box mt="8" display="flex" justifyContent="flex-end">
<Button
type="submit"
color="grey"
disabled={!selectedBranch || !selectedRepo}
isLoading={loading}
>
Create
</Button>
</Box>
</form>
</GridItem>
</Grid>
</>
) : !reposLoading && !installationLoading && !reposData ? (
<>
<Alert mb="4" mt="4" w="65%" status="info">
<AlertIcon />
<Box flex="1">
<AlertTitle>Set up repository permissions</AlertTitle>
<AlertDescription display="block">
First you will need to set up permissions for repositories
that you would like to use with Ledokku. Once that's done,
it's time to choose repo and branch that you would like to
create app from and off we go.
</AlertDescription>
</Box>
</Alert>
<Button
color="grey"
onClick={() => setIsProceedModalOpen(true)}
>
Set up permissions
</Button>
</>
) : (
<Spinner />
)}
</>
)}
<Modal
isOpen={isProceedModalOpen}
onClose={() => setIsProceedModalOpen(false)}
isCentered
>
<ModalOverlay />
<ModalContent>
<ModalHeader>Github setup info</ModalHeader>
<ModalCloseButton />
<ModalBody>
New window is about to open. After you are done selecting github
repos, close the window and refresh page.
</ModalBody>
<ModalFooter>
<Button
color="grey"
variant="outline"
className="mr-3"
onClick={() => setIsProceedModalOpen(false)}
>
Cancel
</Button>
<Button
color="grey"
onClick={() => {
handleOpen();
setIsProceedModalOpen(false);
}}
>
Proceed
</Button>
</ModalFooter>
</ModalContent>
</Modal>
</Container>
</>
);
}
Example #18
Source File: create-app-dokku.tsx From ledokku with MIT License | 4 votes |
CreateAppDokku = () => {
const history = useHistory();
const toast = useToast();
const { data: dataApps } = useAppsQuery();
const [createAppDokkuMutation, { loading }] = useCreateAppDokkuMutation();
const createAppSchema = yup.object().shape({
name: yup
.string()
.required('App name is required')
.matches(/^[a-z0-9-]+$/)
.test(
'Name exists',
'App with this name already exists',
(val) => !dataApps?.apps.find((app) => app.name === val)
),
});
const formik = useFormik<{
name: string;
}>({
initialValues: {
name: '',
},
validateOnChange: true,
validationSchema: createAppSchema,
onSubmit: async (values) => {
try {
const res = await createAppDokkuMutation({
variables: {
input: {
name: values.name,
},
},
});
trackGoal(trackingGoals.createAppDokku, 0);
if (res.data) {
history.push(`app/${res.data?.createAppDokku.appId}`);
toast.success('App created successfully');
}
} catch (error) {
toast.error(error);
}
},
});
return (
<>
<HeaderContainer>
<Header />
</HeaderContainer>
<Container maxW="5xl" mt={10}>
<Heading as="h2" size="md">
Create a new app
</Heading>
<Text mt="12" mb="4" color="gray.400">
Enter app name, click create and voila!
</Text>
<Grid templateColumns={{ sm: 'repeat(1, 1fr)', md: 'repeat(3, 1fr)' }}>
<GridItem colSpan={2}>
<form onSubmit={formik.handleSubmit}>
<FormControl
id="v"
isInvalid={Boolean(formik.errors.name && formik.touched.name)}
>
<FormLabel>App name:</FormLabel>
<Input
autoComplete="off"
id="name"
name="name"
placeholder="Name"
value={formik.values.name}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
<FormErrorMessage>{formik.errors.name}</FormErrorMessage>
</FormControl>
<Box mt="4" display="flex" justifyContent="flex-end">
<Button
type="submit"
color="grey"
disabled={
loading || !formik.values.name || !!formik.errors.name
}
isLoading={loading}
>
Create
</Button>
</Box>
</form>
</GridItem>
</Grid>
</Container>
</>
);
}
Example #19
Source File: advanced.tsx From ledokku with MIT License | 4 votes |
AppSettingsAdvanced = () => {
const { id: appId } = useParams<{ id: string }>();
const toast = useToast();
const history = useHistory();
const { data, loading } = useAppByIdQuery({
variables: {
appId,
},
});
const [
destroyAppMutation,
{ loading: destroyAppMutationLoading },
] = useDestroyAppMutation();
const DeleteAppNameSchema = yup.object().shape({
appName: yup
.string()
.required('Required')
.test(
'Equals app name',
'Must match app name',
(val) => val === data?.app?.name
),
});
const formik = useFormik<{ appName: string }>({
initialValues: {
appName: '',
},
validateOnChange: true,
validationSchema: DeleteAppNameSchema,
onSubmit: async (values) => {
try {
await destroyAppMutation({
variables: {
input: { appId },
},
refetchQueries: [
{
query: DashboardDocument,
},
],
});
toast.success('App deleted successfully');
history.push('/dashboard');
} catch (error) {
toast.error(error.message);
}
},
});
// TODO display error
if (loading) {
// TODO nice loading
return <p>Loading...</p>;
}
if (!data?.app) {
// TODO nice 404
return <p>App not found.</p>;
}
const { app } = data;
return (
<>
<HeaderContainer>
<Header />
<AppHeaderInfo app={app} />
<AppHeaderTabNav app={app} />
</HeaderContainer>
<Container maxW="5xl" mt={10}>
<Grid
templateColumns={{ sm: 'repeat(1, 1fr)', md: 'repeat(6, 1fr)' }}
gap={{ sm: 0, md: 16 }}
>
<GridItem colSpan={2} py={5}>
<AppSettingsMenu app={app} />
</GridItem>
<GridItem colSpan={4}>
<AppRestart appId={app.id} />
<AppRebuild appId={app.id} />
<Box py="5">
<Heading as="h2" size="md">
Delete app
</Heading>
<Text fontSize="sm" color="gray.400">
This action cannot be undone. This will permanently delete{' '}
{app.name} app and everything related to it. Please type{' '}
<b>{app.name}</b> to confirm deletion.
</Text>
</Box>
<form onSubmit={formik.handleSubmit}>
<FormControl
id="appName"
isInvalid={Boolean(
formik.errors.appName && formik.touched.appName
)}
>
<Input
autoComplete="off"
id="appNme"
name="appName"
placeholder="App name"
value={formik.values.appName}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
<FormErrorMessage>{formik.errors.appName}</FormErrorMessage>
</FormControl>
<Button
my={4}
type="submit"
colorScheme="red"
isLoading={destroyAppMutationLoading}
>
Delete
</Button>
</form>
</GridItem>
</Grid>
</Container>
</>
);
}
Example #20
Source File: env.tsx From ledokku with MIT License | 4 votes |
EnvForm = ({ name, value, appId, isNewVar }: EnvFormProps) => {
const [inputType, setInputType] = useState('password');
const toast = useToast();
const [
setEnvVarMutation,
{ loading: setEnvVarLoading },
] = useSetEnvVarMutation();
const [
unsetEnvVarMutation,
{ loading: unsetEnvVarLoading },
] = useUnsetEnvVarMutation();
const handleDeleteEnvVar = async (event: any) => {
event.preventDefault();
try {
await unsetEnvVarMutation({
variables: { key: name, appId },
refetchQueries: [{ query: EnvVarsDocument, variables: { appId } }],
});
} catch (error) {
toast.error(error.message);
}
};
const formik = useFormik<{ name: string; value: string }>({
initialValues: {
name,
value,
},
onSubmit: async (values) => {
// TODO validate values
try {
await setEnvVarMutation({
variables: { key: values.name, value: values.value, appId },
refetchQueries: [{ query: EnvVarsDocument, variables: { appId } }],
});
if (isNewVar) {
formik.resetForm();
}
toast.success('Environment variable set successfully');
} catch (error) {
toast.error(error.message);
}
},
});
return (
//TODO Handle visual feedback on changing env
//TODO Provide infos about env vars
<form onSubmit={formik.handleSubmit} autoComplete="off">
<Grid
templateColumns={{ sm: 'repeat(1, 1fr)', md: 'repeat(3, 1fr)' }}
gap="3"
mt="3"
>
<GridItem>
<Input
autoComplete="off"
id={isNewVar ? 'newVarName' : name}
name="name"
placeholder="Name"
key={name}
value={formik.values.name}
onChange={formik.handleChange}
/>
</GridItem>
<GridItem>
<Input
autoComplete="off"
onMouseEnter={() => setInputType('text')}
onMouseLeave={() => setInputType('password')}
onFocus={() => setInputType('text')}
onBlur={() => setInputType('password')}
id={isNewVar ? 'newVarValue' : value}
name="value"
placeholder="Value"
key={value}
value={formik.values.value}
onChange={formik.handleChange}
type={inputType}
/>
</GridItem>
<GridItem display="flex">
<Button isLoading={setEnvVarLoading} type="submit">
{isNewVar ? 'Add' : 'Save'}
</Button>
{!isNewVar && (
<IconButton
aria-label="Delete"
variant="outline"
ml="3"
icon={<FiTrash2 />}
isLoading={unsetEnvVarLoading}
onClick={handleDeleteEnvVar}
/>
)}
</GridItem>
</Grid>
</form>
);
}
Example #21
Source File: SettingsCart.tsx From epcc-react-pwa-reference-storefront with GNU General Public License v3.0 | 4 votes |
SettingsCart: React.FC<SettingsCartParams> = (props) => {
const { name, description, isEditCart, title, onCartCreate, showSettings, handleHideSettings, setShowCartAlert } = props;
const { t } = useTranslation();
const { createCart, editCart } = useMultiCartData();
const [isLoading, setIsLoading] = useState(false);
const [nameError, setNameError ] = useState("");
const [descriptionError , setDescriptionError ] = useState("");
let initialValues: FormValues = {
name: isEditCart && name ? name.toString() : '',
description: isEditCart && description ? description.toString() : '',
};
const validate = (values:any) => {
const errors:any = {};
if (!values.name) {
errors.name = t('cart-name-is-required');
}
return errors;
};
const {handleSubmit, handleChange, values, errors, setFieldValue, resetForm} = useFormik({
initialValues,
validate,
onSubmit: async (values) => {
setIsLoading(true);
try{
if(isEditCart) {
await editCart(values);
}
else {
const cartData = await createCart(values);
if (onCartCreate) onCartCreate(cartData);
}
setIsLoading(false);
handleHideSettings();
setShowCartAlert();
resetForm();
if(descriptionError !== "" || nameError !== ""){
setDescriptionError("");
setNameError("");
}
}
catch(errors) {
errors.errors.map((error:any) => {
if(error.source === "data.name") {
setNameError(error.detail)
}
else if (error.source === "data.description"){
setDescriptionError(error.detail)
}
return setIsLoading(false);
})
}
},
});
return (
<div className={`settingscart${showSettings ? ' --show' : ''}`}>
<div className="settingscart__addcartform">
{isEditCart &&
<button className="settingscart__closebutton" type="button" aria-label="close" onClick={handleHideSettings}>
<BackArrowIcon/>
</button>
}
{title ?? (
<h2 className="settingscart__title">
{isEditCart ? `${t("cart")} ${t("settings")}` : t("new-cart")}
</h2>
)}
<form>
<div className={`epform__group ${errors.name ? '--error' : ''}`}>
<label className="epform__label" htmlFor="name">{t('cart-name')}</label>
<input className="epform__input" id="name" placeholder={t('new-cart')} onChange={handleChange} value={values.name} />
{(values.name && values.name.length > 0) && (
<button tabIndex={-1} type="button" className="settingscart__clearname settingscart__clearbtn" onClick={() => setFieldValue('name', '')}>
<ClearIcon />
</button>
)}
<div className="epform__error">
{errors.name ? errors.name : null}
{values.name.length > 250 ? nameError : null}
</div>
</div>
<div className="epform__group">
<label className="epform__label" htmlFor="description">{t('cart-description')}</label>
<textarea className="epform__input" id="description" onChange={handleChange} value={values.description} placeholder={t('carts-description')} />
{(values.description && values.description.length > 0) && (
<button tabIndex={-1} className="settingscart__clearbtn" onClick={() => setFieldValue('description', '')}>
<ClearIcon />
</button>
)}
<div className="epform__error">
{values.description.length > 250 ? descriptionError : null}
</div>
</div>
<div className="settingscart__btns">
<button className="epbtn --bordered" type="button" onClick={handleHideSettings}>{t('cancel')}</button>
<button
className={`epbtn --primary ${
isLoading ? "--loading" : ""
}`}
type="submit"
onClick={() => {handleSubmit()}}
disabled={isLoading || !values.name }
>
{!isLoading? t("save") : <span className="circularLoader" aria-label={t('loading')} />}
</button>
</div>
</form>
</div>
</div>
)
}
Example #22
Source File: AddAppProxyPorts.tsx From ledokku with MIT License | 4 votes |
AddAppProxyPorts = ({
appId,
appProxyPortsRefetch,
open,
onClose,
}: AddAppProxyPortsProps) => {
const [addAppProxyPortMutation] = useAddAppProxyPortMutation();
const toast = useToast();
const formik = useFormik<{ host: string; container: string }>({
initialValues: {
host: '',
container: '',
},
validateOnChange: true,
validationSchema: createAppProxyPortSchema,
onSubmit: async (values) => {
try {
await addAppProxyPortMutation({
variables: {
input: {
appId,
host: values.host,
container: values.container,
},
},
});
await appProxyPortsRefetch();
toast.success('Port mapping created successfully');
onClose();
} catch (error) {
toast.error(error.message);
}
},
});
if (!open) {
return null;
}
return (
<Modal isOpen={open} onClose={onClose} isCentered size="xl">
<ModalOverlay />
<ModalContent>
<ModalHeader>Add port mapping</ModalHeader>
<ModalCloseButton />
<ModalBody>
<SimpleGrid columns={{ sm: 1, md: 2 }} spacing={3}>
<FormControl
id="host"
isInvalid={Boolean(formik.errors.host && formik.touched.host)}
>
<FormLabel>Host port:</FormLabel>
<Input
name="host"
value={formik.values.host}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
<FormErrorMessage>{formik.errors.host}</FormErrorMessage>
</FormControl>
<FormControl
id="container"
isInvalid={Boolean(
formik.errors.container && formik.touched.container
)}
>
<FormLabel>Container port:</FormLabel>
<Input
name="container"
value={formik.values.container}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
<FormErrorMessage>{formik.errors.container}</FormErrorMessage>
</FormControl>
</SimpleGrid>
</ModalBody>
<ModalFooter>
<Button mr={3} onClick={onClose}>
Cancel
</Button>
<Button
colorScheme="red"
isLoading={formik.isSubmitting}
onClick={() => formik.handleSubmit()}
>
Create
</Button>
</ModalFooter>
</ModalContent>
</Modal>
);
}
Example #23
Source File: index.tsx From GroupChat with MIT License | 4 votes |
Login: React.FC<Props> = props => {
const dispatch = useDispatch();
const history = useHistory();
const [isLoading, setIsLoading] = useState(false);
const [checked, setChecked] = useState(false);
const [snack, setSnack] = useState<SnackData>({ open: false, message: null });
// Async Requests
const loginSubmit = async (checked: boolean, email: string, password: string) => {
setIsLoading(true);
let response;
try {
response = await axios.post(`${process.env.REACT_APP_SERVER_URL}/users/login`, {
checked,
email: email.toLowerCase(),
password: password.toLowerCase()
});
} catch (error) {
console.log('[ERROR][AUTH][LOGIN]: ', error);
setIsLoading(false);
return;
}
if (!response.data.access) {
setSnack({ open: true, message: response.data.message });
setIsLoading(false);
return;
}
if (checked) {
localStorage.setItem('userData', JSON.stringify({ id: response.data.user.id, token: response.data.user.token }));
}
dispatch({ type: 'LOGIN', payload: { ...response.data.user } });
history.push('');
setIsLoading(false);
};
const formik = useFormik({
initialValues: {
email: '',
password: ''
},
validationSchema: Yup.object({
email: Yup.string().email('Invalid email address').required('Required'),
password: Yup.string()
.min(6, 'Must be 6 characters at least')
.required('Required')
.max(20, 'Can not exceed 20 characters')
}),
onSubmit: values => loginSubmit(checked, values.email, values.password)
});
return (
<div className={styles.container}>
<Link to="/">
<img className={styles.logo} alt="logo" src={logo} />
</Link>
<form className={styles.form}>
<TextField
className={styles.input}
id="email"
label="Email"
variant="outlined"
type="text"
helperText={formik.touched.email && formik.errors.email}
error={formik.touched.email && !!formik.errors.email}
{...formik.getFieldProps('email')}
/>
<TextField
className={styles.input}
id="password"
label="Password"
variant="outlined"
type="password"
{...formik.getFieldProps('password')}
helperText={formik.touched.password && formik.errors.password}
error={formik.touched.password && !!formik.errors.password}
/>
<FormControlLabel
className={styles.check}
control={
<Checkbox checked={checked} onChange={() => setChecked(prev => !prev)} name="checked" color="primary" />
}
label="Remember me"
/>
<CustomButton type="submit" onClick={formik.handleSubmit} isPurple title="Login" small={false} />
</form>
<Link to="/signup">
<p className={styles.guest}>Don't have an account? Sign Up</p>
</Link>
{isLoading && <CircularProgress />}
<Snackbar open={snack.open} onClose={() => setSnack({ open: false, message: null })} autoHideDuration={5000}>
<MuiAlert variant="filled" onClose={() => setSnack({ open: false, message: null })} severity="error">
{snack.message}
</MuiAlert>
</Snackbar>
</div>
);
}
Example #24
Source File: RegistrationForm.tsx From epcc-react-pwa-reference-storefront with GNU General Public License v3.0 | 4 votes |
RegistrationForm: React.FC = (props) => {
const { setCustomerData } = useCustomerData();
const { setMultiCartData, updateSelectedCart, setMultiCartDataList } = useMultiCartData();
const { t } = useTranslation();
const history = useHistory();
const [registrationErrors, setRegistrationErrors] = useState('');
const [isLoading, setIsLoading] = useState(false);
const initialValues:FormValues = {
firstName: '',
lastName: '',
email: '',
emailConfirm: '',
password: '',
passwordConfirm: '',
};
const validate = (values:FormValues) => {
const errors:any = {};
if (!values.firstName) {
errors.firstName = t('required');
}
if (!values.lastName) {
errors.lastName = t('required');
}
if (!values.email) {
errors.email = t('required');
}
if (!values.emailConfirm) {
errors.emailConfirm = t('required');
}
if (values.email && values.emailConfirm && values.email !== values.emailConfirm) {
errors.emailConfirm = t('email-slash-username-confirm-error');
}
if (!values.password) {
errors.password = t('required');
}
if (!values.passwordConfirm) {
errors.passwordConfirm = t('required');
}
if (values.password && values.passwordConfirm && values.password !== values.passwordConfirm) {
errors.passwordConfirm = t('password-confirm-error');
}
return errors;
};
const {handleSubmit, handleChange, values, errors} = useFormik({
initialValues,
validate,
onSubmit: (values) => {
setRegistrationErrors('');
setIsLoading(true);
const guestCart = localStorage.getItem('mcart') || '';
register(`${values.firstName} ${values.lastName}`, values.email, values.password)
.then(() => {
login(values.email.toLowerCase(), values.password).then((result) => {
setIsLoading(false);
setCustomerData(result.token, result.customer_id);
addCustomerAssociation(guestCart, result.customer_id, result.token)
.then(() =>
getMultiCartsList(result.token, 1).then((res) => {
setMultiCartDataList(res.data);
getMultiCarts(result.token).then(res => {
setMultiCartData(res.data);
updateSelectedCart(res.data[0]);
localStorage.setItem('mcart', res.data[0].id);
})
})
.catch(error => {
console.error(error);
})
)
.catch(error => {
console.error(error);
});
history.push('/');
})
})
.catch(error => {
const errorsContainer = error.errors.map((el:any) => el.detail).join('\n');
setIsLoading(false);
setRegistrationErrors(errorsContainer);
console.error(error);
});
},
});
return (
<div className="registrationform container">
<h1 className="eppagetitle">
{t('register-new-account')}
</h1>
<div className="registrationform__feedback">
{registrationErrors}
</div>
<div className={`registrationform__content ${isLoading ? '--loading' : ''}`}>
<form className="epform" onSubmit={handleSubmit}>
{
(isLoading) ? <div className="epminiLoader --centered" /> : ('')
}
<div className={`epform__group ${errors.firstName ? '--error' : ''}`}>
<label htmlFor="firstName" className="epform__label">
{t('first-name')} *
</label>
<input id="firstName" name="firstName" className="epform__input" type="text" onChange={handleChange} value={values.firstName} />
<div className="epform__error">
{errors.firstName ? errors.firstName : null}
</div>
</div>
<div className={`epform__group ${errors.lastName ? '--error' : ''}`}>
<label htmlFor="lastName" className="epform__label">
{t('last-name')} *
</label>
<input id="lastName" name="lastName" className="epform__input" type="text" onChange={handleChange} value={values.lastName} />
<div className="epform__error">
{errors.lastName ? errors.lastName : null}
</div>
</div>
<div className={`epform__group ${errors.email ? '--error' : ''}`}>
<label htmlFor="email" className="epform__label">
{t('email-slash-username')} *
</label>
<input id="email" name="email" className="epform__input" type="email" onChange={handleChange} value={values.email} />
<div className="epform__error">
{errors.email ? errors.email : null}
</div>
</div>
<div className={`epform__group ${errors.emailConfirm ? '--error' : ''}`}>
<label htmlFor="email" className="epform__label">
{t('email-slash-username-confirmation')} *
</label>
<input id="email" name="emailConfirm" className="epform__input" type="email" onChange={handleChange} value={values.emailConfirm} />
<div className="epform__error">
{errors.emailConfirm ? errors.emailConfirm : null}
</div>
</div>
<div className={`epform__group ${errors.password ? '--error' : ''}`}>
<label htmlFor="password" className="epform__label">
{t('password')} *
</label>
<input id="password" name="password" className="epform__input" type="password" onChange={handleChange} value={values.password} />
<div className="epform__error">
{errors.password ? errors.password : null}
</div>
</div>
<div className={`epform__group ${errors.passwordConfirm ? '--error' : ''}`}>
<label htmlFor="passwordConfirm" className="epform__label">
{t('password-confirmation')} *
</label>
<input id="passwordConfirm" name="passwordConfirm" className="epform__input" type="password" onChange={handleChange} value={values.passwordConfirm} />
<div className="epform__error">
{errors.passwordConfirm ? errors.passwordConfirm : null}
</div>
</div>
<div className="epform__group --btn-container">
<button className="epbtn --secondary" id="registration_form_register_button" type="submit" disabled={isLoading}>
{t('submit')}
</button>
</div>
</form>
</div>
</div>
);
}
Example #25
Source File: Profile.tsx From epcc-react-pwa-reference-storefront with GNU General Public License v3.0 | 4 votes |
Profile: React.FC = (props) => {
const { id, token, customerEmail, customerName, setEmail, setName } = useCustomerData();
const { t } = useTranslation();
const [isEditMode, setIsEditMode] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const { addError } = useContext(APIErrorContext);
const initialValues:FormValues = {
email: customerEmail,
username: customerName,
};
const validate = (values:FormValues) => {
const errors:any = {};
if (!values.email) {
errors.email = t('required');
} else if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(values.email)) {
errors.email = t('invalid-email');
}
if (!values.username) {
errors.username = t('required');
}
return errors;
};
const {handleSubmit, handleChange, values, errors, setFieldValue} = useFormik({
initialValues,
validate,
onSubmit: (values) => {
setIsLoading(true);
// @ts-ignore
updateCustomer(id, values.username, values.email, token)
.then((result) => {
setIsLoading(false);
setEmail(result.data.email);
setName(result.data.name);
setIsEditMode(false);
})
.catch(error => {
addError(error.errors);
console.error(error);
});
},
});
const handleShowForm = () => {
setIsEditMode(true);
setFieldValue('email', customerEmail, false);
setFieldValue('username', customerName, false);
};
const handleHideForm = () => {
setIsEditMode(false);
errors.email = '';
errors.username = '';
};
return (
<div className="profile">
<h1 className="profile__title">{t('my-profile')}</h1>
<div className="profile__data">
<p className="profile__titlesmall">{t('general')}</p>
<div className="profile__container">
<h2>{t('personal-information')}</h2>
{!isEditMode ? (
<div className="profile__info">
<p className="profile__infoitem">
<span className="profile__infolabel">{t('email')}:</span>
{customerEmail}
</p>
<p className="profile__infoitem">
<span className="profile__infolabel">{t('username')}:</span>
{customerName}
</p>
<button className="epbtn" onClick={handleShowForm}>{t('edit')}</button>
</div>
) : (
<div className={`profile__form ${isLoading ? '--loading' : ''}`}>
<form className="epform" onSubmit={handleSubmit}>
{
(isLoading) ? <div className="epminiLoader --centered" /> : ('')
}
<div className="epform__group">
<label className="epform__label" htmlFor="email">{t('email')}:</label>
<input type="text" id="email" className="epform__input" onChange={handleChange} value={values.email} />
<div className="epform__error">
{errors.email ? errors.email : null}
</div>
</div>
<div className="epform__group">
<label className="epform__label" htmlFor="username">{t('username')}:</label>
<input type="text" id="username" name="username" className="epform__input" onChange={handleChange} value={values.username} />
<div className="epform__error">
{errors.username ? errors.username : null}
</div>
</div>
<div className="epform__group">
<button className="epbtn --secondary" type="submit">{t('save')}</button>
<button className="epbtn --bordered" type="submit" onClick={handleHideForm}>{t('cancel')}</button>
</div>
</form>
</div>
)}
</div>
</div>
</div>
)
}
Example #26
Source File: LoginForm.tsx From epcc-react-pwa-reference-storefront with GNU General Public License v3.0 | 4 votes |
LoginForm: React.FC<LoginFormProps> = (props) => {
const { handleModalClose,openCartModal, onSubmit, openModal, createCart, handleCloseCartModal, handleShowNewCart } = props;
const { setCustomerData } = useCustomerData();
const { t } = useTranslation();
const { setGuestCartId, setIsCreateNewCart, createDefaultCart } = useMultiCartData();
const registrationUrl = createRegistrationUrl();
const browserHistory = createBrowserHistory();
const history = useHistory();
const [failedLogin, setFailedLogin] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const initialValues:FormValues = {
emailField: '',
passwordField: '',
};
const validate = (values:any) => {
const errors:any = {};
if (!values.emailField) {
errors.emailField = t('required');
}
if (!values.passwordField) {
errors.passwordField = t('required');
}
return errors;
};
const {handleSubmit, handleChange, resetForm, values, errors} = useFormik({
initialValues,
validate,
onSubmit: async (values) => {
const cartId = localStorage.getItem('mcart') || '';
setGuestCartId(cartId);
setIsLoading(true);
login(values.emailField.toLowerCase(), values.passwordField)
.then((result) => {
setCustomerData(result.token, result.customer_id);
setIsLoading(false);
createDefaultCart();
if(browserHistory.location.pathname === "/registration")
{
history.push('/');
}
if (handleModalClose) {
handleModalClose();
}
if(openCartModal && handleShowNewCart){
openCartModal();
handleShowNewCart(true);
}
if (createCart) {
setIsCreateNewCart(true);
}
if (onSubmit) {
onSubmit();
}
})
.catch(error => {
setIsLoading(false);
setFailedLogin(true);
console.error(error);
});
},
});
const registerNewUser = () => {
if (handleModalClose) {
handleModalClose();
}
if (handleCloseCartModal) {
handleCloseCartModal();
}
};
useEffect(() => {
if (!openModal) {
setFailedLogin(false);
resetForm();
}
}, [openModal, resetForm]);
return (
<div className={`loginform${isLoading ? ' --loading' : ''}`}>
{
(isLoading) ? <div className="epminiLoader --centered" /> : ('')
}
<div className="loginform__feedback">
{failedLogin ? t('invalid-email-or-password') : ('')}
</div>
<form className="epform" id="login_modal_form" onSubmit={handleSubmit}>
<div className={`epform__group ${errors.emailField ? '--error' : ''}`}>
<label className="epform__label" htmlFor="emailField">
{t('email')}:
</label>
<input className="epform__input" id="emailField" type="text" onChange={handleChange} value={values.emailField} />
<div className="epform__error">
{errors.emailField ? errors.emailField : null}
</div>
</div>
<div className={`epform__group ${errors.passwordField ? '--error' : ''}`}>
<label className="epform__label" htmlFor="passwordField">
{t('password')}:
</label>
<input className="epform__input" id="passwordField" type="password" onChange={handleChange} value={values.passwordField} />
<div className="epform__error">
{errors.passwordField ? errors.passwordField : null}
</div>
</div>
<div className="epform__group --btn-container">
<button className="epbtn --secondary" id="login_modal_login_button" type="submit" disabled={isLoading}>
{t('login')}
</button>
<Link to={registrationUrl} className="epbtn --secondary" id="login_modal_register_button" onClick={registerNewUser}>
{t('register')}
</Link>
</div>
</form>
</div>
);
}
Example #27
Source File: PasswordLoginForm.tsx From epcc-react-pwa-reference-storefront with GNU General Public License v3.0 | 4 votes |
PasswordLoginForm: React.FC<PasswordLoginFormProps> = (props) => {
const { handleModalClose,handleCloseCartModal, setIsLoading, setFailedLogin,onSubmit, openCartModal, openModal, createCart, handleShowNewCart } = props;
const { setCustomerData } = useCustomerData();
const { t } = useTranslation();
const registrationUrl = createRegistrationUrl();
const { setGuestCartId, setIsCreateNewCart, createDefaultCart } = useMultiCartData();
const browserHistory = createBrowserHistory();
const history = useHistory();
const initialValues:FormValues = {
emailField: '',
passwordField: '',
};
const validate = (values:any) => {
const errors:any = {};
if (!values.emailField) {
errors.emailField = t('required');
}
if (!values.passwordField) {
errors.passwordField = t('required');
}
return errors;
}
const {handleSubmit, handleChange,resetForm, values, errors} = useFormik({
initialValues,
validate,
onSubmit: (values) => {
const cartId = localStorage.getItem('mcart') || '';
setGuestCartId(cartId);
setIsLoading(true);
login(values.emailField.toLowerCase(), values.passwordField)
.then((result) => {
setCustomerData(result.token, result.customer_id);
setIsLoading(false);
createDefaultCart();
if(browserHistory.location.pathname === "/registration")
{
history.push('/');
}
if (handleModalClose) {
handleModalClose();
}
if(openCartModal && handleShowNewCart){
openCartModal();
handleShowNewCart(true);
}
if (createCart) {
setIsCreateNewCart(true);
}
if (onSubmit) {
onSubmit();
}
})
.catch(error => {
setIsLoading(false);
if(setFailedLogin)
setFailedLogin(true);
console.error(error);
});
},
});
const registerNewUser = () => {
if (handleModalClose) {
handleModalClose();
}
if (handleCloseCartModal) {
handleCloseCartModal();
}
}
useEffect(() => {
if (!openModal && setFailedLogin) {
setFailedLogin(false);
resetForm();
}
}, [openModal, resetForm, setFailedLogin]);
return (
<form className="epform" id="login_modal_form" onSubmit={handleSubmit}>
<div className={`epform__group ${errors.emailField ? '--error' : ''}`}>
<label className="epform__label" htmlFor="emailField">
{t('email')}
</label>
<input className="epform__input" id="emailField" type="text" onChange={handleChange} value={values.emailField} />
<div className="epform__error">
{errors.emailField ? errors.emailField : null}
</div>
</div>
<div className={`epform__group ${errors.passwordField ? '--error' : ''}`}>
<label className="epform__label" htmlFor="passwordField">
{t('password')}
</label>
<input className="epform__input" id="passwordField" type="password" onChange={handleChange} value={values.passwordField} />
<div className="epform__error">
{errors.passwordField ? errors.passwordField : null}
</div>
</div>
<div className="epform__group --btn-container">
<button className="epbtn --primary loginbtn" id="login_modal_login_button" type="submit" disabled={props.isLoading}>
{t('login')}
</button>
<Link to={registrationUrl} className="epbtn --secondary registerbtn" id="login_modal_register_button" onClick={registerNewUser}>
{t('register')}
</Link>
</div>
</form>
);
}
Example #28
Source File: BulkOrder.tsx From epcc-react-pwa-reference-storefront with GNU General Public License v3.0 | 4 votes |
BulkOrder: React.FC = (props) => {
const { t } = useTranslation();
const { updateCartItems, setCartQuantity, handleShowCartPopup } = useCartData();
const { multiCartData, updateCartData, updateSelectedCart, setIsCartSelected } = useMultiCartData();
const { isLoggedIn } = useCustomerData();
const { selectedLanguage } = useTranslation();
const { selectedCurrency } = useCurrency();
const [bulkOrderItems, setBulkOrderItems] = useState([]);
const [bulkError, setBulkError] = useState('');
const [showLoader, setShowLoader] = useState(false);
const [dropdownOpen, setDropdownOpen] = useState(false);
const [modalOpen, setModalOpen] = useState(false);
const [cartID, setCartId] = useState("");
const modalRef = useOnclickOutside(() => {
setModalOpen(false)
});
const dropdownRef = useOnclickOutside(() => {
setDropdownOpen(false)
});
const handleAddToSelectedCart = (cart:any) => {
updateSelectedCart(cart);
setCartId(cart.id);
handleSubmit();
};
const handleAddToDefaultCart = () => {
if (multiCartData && multiCartData.length > 0) {
handleAddToSelectedCart(multiCartData[0]);
}
};
const CartButton = () => {
if (isLoggedIn) {
return (
<div className="bulkorder__addtocartdropdowncontainer">
<div className="bulkorder__addtocartdropdownwrap">
<button
className="epbtn --primary bulkorder__addtocartbtn"
onClick={handleAddToDefaultCart}
disabled={!values.productSKU}
>
{t("add-to-cart")}
{' - '}
{multiCartData && multiCartData.length > 0 && multiCartData[0].name}
</button>
<button onClick={() => setDropdownOpen(!dropdownOpen)} disabled={!values.productSKU} className={`epbtn --primary bulkorder__addtocartdropdowntoggle${
dropdownOpen ? " --open" : ""
}`}>
{showLoader ? (
<SpinnerIcon className="bulkorder__addtocartdropdownicspinner" />
) : (
<CaretIcon
className={`bulkorder__addtocartdropdowniscaret ${
dropdownOpen ? "--rotated" : ""
}`}
/>
)}
</button>
</div>
{dropdownOpen ? (
<div className="bulkorder__addtocartdropdowncontent">
{multiCartData.slice(1).map((cart: moltin.CartItem) => (
<button
className="bulkorder__addtocartdropdownbtn"
key={cart.id}
onClick={() => { handleAddToSelectedCart(cart) }}
>
{cart.name}
</button>
))}
<button
className="bulkorder__addtocartdropdownbtn"
key="create-cart-btn"
onClick={() => setModalOpen(true)}
type="submit"
>
{t('create-new-cart')}
</button>
</div>
) : null}
</div>
);
}
return (
<button className="epbtn --secondary bulkorder__addtocartbtn" type="submit">
{t("add-to-cart")}
</button>
);
};
const CreateCartHeader = (
<div className="bulkorder__createcartheader">
<span className="bulkorder__createcartheadertext">{t("create-cart")}</span>
<button
className="bulkorder__createcartheaderbnt"
onClick={() => setModalOpen(false)}
>
<CloseIcon />
</button>
</div>
);
const initialValues:FormValues = {
productSKU: '',
};
const {handleSubmit, resetForm, handleChange, values} = useFormik({
initialValues,
onSubmit: (values) => {
setBulkError('');
setShowLoader(true);
const currentCart = localStorage.getItem("mcart") || "";
const mcart = cartID ? cartID : currentCart;
bulkAdd(mcart, bulkOrderItems, selectedLanguage, selectedCurrency)
.then((res:any) => {
const totalQuantity = bulkOrderItems.reduce((sum, { quantity }) => sum + quantity, 0);
const errorsWQ: [{type:string, sku:string, quantity:number}] = [{type: "", sku: "", quantity: 0}];
if (res.errors) {
res.errors.map(function(x:any){
var result = bulkOrderItems.filter((a:any) => a.sku === x.meta.sku)
errorsWQ.push(result[0]);
return errorsWQ;
})
}
const errorsquantity = errorsWQ.reduce((sum, { quantity }) => sum + quantity, 0);
if (cartID && cartID !== currentCart) {
localStorage.setItem('mcart', cartID);
} else {
updateCartItems();
}
updateCartData();
setCartQuantity(totalQuantity - errorsquantity);
handleShowCartPopup();
resetForm();
setShowLoader(false);
setIsCartSelected(true);
setDropdownOpen(false);
const errorsContainer = res.errors.map((el:any) => (`"${el.meta.sku}" ${el.detail}
`)).join('\n');
setBulkError(errorsContainer);
})
.catch(error => {
setShowLoader(false);
console.error(error);
});
}
});
useEffect(() => {
const bulkOrderItems:any = values.productSKU
.split('\n')
.filter(l => l.trim().length)
.map(l => l.split(/[ ,;]+/))
.map(p => ({ type: 'cart_item', sku: p[0] || '', quantity: isNaN(parseInt(p[1])) ? 1 : parseInt(p[1]) }));
setBulkOrderItems(bulkOrderItems);
}, [values.productSKU]);
const handleClear = () => {
resetForm();
setBulkError('');
};
const clearError = () => {
setBulkError('');
}
useEffect(() => {
document.body.style.overflow = modalOpen ? 'hidden' : 'unset';
}, [modalOpen])
return (
<div className="bulkorder">
<form className="bulkorder__form" onSubmit={handleSubmit}>
<div className="bulkorder__group">
<label className="bulkorder__label" htmlFor="productSKU">
{t('add-products-by-sku')}:
</label>
<textarea className="bulkorder__textarea" id="productSKU" rows={5} onChange={handleChange} value={values.productSKU} />
{values.productSKU &&
<button className="bulkorder__clearbtn" type="reset" onClick={handleClear}>
<ClearIcon className="bulkorder__clearicon" />
</button>
}
</div>
<div className="bulkorder__info">
<p>{t('bulk-order-format')}</p>
</div>
<div className="" ref={dropdownRef}>
<CartButton/>
</div>
</form>
{
bulkError &&
<div className="bulkorder__messagewrap">
<button className="bulkorder__clearerrorbtn" type="reset" onClick={clearError} >
<ClearIcon className="bulkorder__clearerrorbtnicon" />
</button>
<div className="bulkorder__feedback">{bulkError}</div>
</div>
}
{modalOpen ? (
<div className="bulkorder__createcartmodalbg">
<div className="bulkorder__createcartmodal" ref={modalRef}>
<SettingsCart
title={CreateCartHeader}
onCartCreate={() => {setModalOpen(false)}}
handleHideSettings={() => {setModalOpen(false)}}
setShowCartAlert={() => ''}
/>
</div>
</div>
) : null}
</div>
)
}
Example #29
Source File: AddressForm.tsx From epcc-react-pwa-reference-storefront with GNU General Public License v3.0 | 4 votes |
AddressForm: React.FC<AddressFormParams> = (props) => {
const { handleModalClose, isModalOpen, addressData } = props;
const { t } = useTranslation();
const [isLoading, setIsLoading] = useState(false);
const [addressErrors, setAddressErrors] = useState<any[]>([]);
const { updateAddresses } = useAddressData();
let initialValues:FormValues = {
id: addressData?.id ?? '',
type: addressData?.type ?? '',
first_name: addressData?.first_name ?? '',
last_name: addressData?.last_name ?? '',
line_1: addressData?.line_1 ?? '',
line_2: addressData?.line_2 ?? '',
phone_number: addressData?.phone_number ?? '',
county: addressData?.county ?? '',
country: addressData?.country ?? '',
postcode: addressData?.postcode ?? '',
company_name: addressData?.company_name ?? '',
city: addressData?.city ?? '',
name: addressData?.name ?? '',
instructions: addressData?.instructions ?? '',
};
const validate = (values:any) => {
const errors:any = {};
if (!values.first_name) {
errors.first_name = t('required');
}
if (!values.last_name) {
errors.last_name = t('required');
}
if (!values.line_1) {
errors.line_1 = t('required');
}
if (!values.country) {
errors.country = t('required');
}
if (!values.county) {
errors.county = t('required');
}
if (!values.postcode) {
errors.postcode = t('required');
}
return errors;
};
const handleClose = () => {
handleModalClose();
resetForm({})
};
const {handleSubmit, handleChange, resetForm, values, errors} = useFormik({
initialValues,
validate,
onSubmit: (values) => {
setIsLoading(true);
const data = values;
const token = localStorage.getItem('mtoken') || '';
const customer = localStorage.getItem('mcustomer') || '';
if(values.id) {
updateAddress(customer, values.id, data, token)
.then(() => {
updateAddresses();
handleClose();
setIsLoading(false);
})
.catch(error => {
setIsLoading(false);
setAddressErrors(error.errors);
console.error(error);
});
} else {
addNewAddress(customer, data, token)
.then(() => {
updateAddresses();
handleModalClose();
setIsLoading(false);
})
.catch(error => {
setIsLoading(false);
setAddressErrors(error.errors);
console.error(error);
});
}
},
});
return (
<Modal open={isModalOpen} onClose={handleClose} classNames={{modal: 'addressform'}} showCloseIcon={false}>
{
(isLoading) ? <div className="epminiLoader --centered"/> : ('')
}
<div className={`addressform__content ${isLoading ? '--loading' : ''}`}>
<div className="addressform__header">
<h2 className="addressform__title">
{values.id ? t('edit-address') : t('new-address')}
</h2>
<button type="button" aria-label="close" onClick={handleModalClose}>
<CloseIcon/>
</button>
</div>
<div className="addressform__body">
<div className="addressform__feedback">
{addressErrors.length ? (
addressErrors.map(error => (
<div key={error.detail}>
{error.detail}
</div>
))
) : ('')}
</div>
<form className="epform --addressform" id="address_modal_form" onSubmit={handleSubmit}>
<div className={`epform__group ${errors.first_name ? '--error' : ''}`}>
<label className="epform__label" htmlFor="first_name">
<span className="required-label">
*
</span>
{t('first-name')}
</label>
<input className="epform__input" id="first_name" type="text" onChange={handleChange} value={values.first_name} />
<div className="epform__error">
{errors.first_name ? errors.first_name : null}
</div>
</div>
<div className={`epform__group ${errors.last_name ? '--error' : ''}`}>
<label className="epform__label" htmlFor="last_name">
<span className="required-label">
*
</span>
{t('last-name')}
</label>
<input className="epform__input" id="last_name" type="text" onChange={handleChange} value={values.last_name} />
<div className="epform__error">
{errors.last_name ? errors.last_name : null}
</div>
</div>
<div className={`epform__group ${errors.name ? '--error' : ''}`}>
<label className="epform__label" htmlFor="name">
{t('name')}
</label>
<input className="epform__input" id="name" type="text" onChange={handleChange} value={values.name} />
<div className="epform__error">
{errors.name ? errors.name : null}
</div>
</div>
<div className={`epform__group ${errors.company_name ? '--error' : ''}`}>
<label className="epform__label" htmlFor="company_name">
{t('company-name')}
</label>
<input className="epform__input" id="company_name" type="text" onChange={handleChange} value={values.company_name} />
<div className="epform__error">
{errors.company_name ? errors.company_name : null}
</div>
</div>
<div className={`epform__group ${errors.line_1 ? '--error' : ''}`}>
<label className="epform__label" htmlFor="line_1">
<span className="required-label">
*
</span>
{t('street-address')}
</label>
<input className="epform__input" id="line_1" type="text" onChange={handleChange} value={values.line_1} />
<div className="epform__error">
{errors.line_1 ? errors.line_1 : null}
</div>
</div>
<div className={`epform__group ${errors.line_2 ? '--error' : ''}`}>
<label className="epform__label" htmlFor="line_2">
{t('extended-address')}
</label>
<input className="epform__input" id="line_2" type="text" onChange={handleChange} value={values.line_2} />
<div className="epform__error">
{errors.line_2 ? errors.line_2 : null}
</div>
</div>
<div className={`epform__group ${errors.county ? '--error' : ''}`}>
<label className="epform__label" htmlFor="county">
<span className="required-label">
*
</span>
{t('county')}
</label>
<input className="epform__input" id="county" type="text" onChange={handleChange} value={values.county} />
<div className="epform__error">
{errors.county ? errors.county : null}
</div>
</div>
<div className={`epform__group ${errors.country ? '--error' : ''}`}>
<label className="epform__label" htmlFor="country">
<span className="required-label">
*
</span>
{t('country')}
</label>
<CountriesSelect value={values.country} onChange={handleChange} />
<div className="epform__error">
{errors.country ? errors.country : null}
</div>
</div>
<div className={`epform__group ${errors.phone_number ? '--error' : ''}`}>
<label className="epform__label" htmlFor="phone_number">
{t('phone-number')}
</label>
<input className="epform__input" id="phone_number" type="text" onChange={handleChange} value={values.phone_number} />
<div className="epform__error">
{errors.phone_number ? errors.phone_number : null}
</div>
</div>
<div className={`epform__group ${errors.city ? '--error' : ''}`}>
<label className="epform__label" htmlFor="city">
{t('city')}
</label>
<input className="epform__input" id="city" type="text" onChange={handleChange} value={values.city} />
<div className="epform__error">
{errors.city ? errors.city : null}
</div>
</div>
<div className={`epform__group ${errors.postcode ? '--error' : ''}`}>
<label className="epform__label" htmlFor="postcode">
<span className="required-label">
*
</span>
{t('postal-сode')}
</label>
<input className="epform__input" id="postcode" type="text" onChange={handleChange} value={values.postcode} />
<div className="epform__error">
{errors.postcode ? errors.postcode : null}
</div>
</div>
<div className={`epform__group ${errors.instructions ? '--error' : ''}`}>
<label className="epform__label" htmlFor="instructions">
{t('instructions')}
</label>
<input className="epform__input" id="instructions" type="text" onChange={handleChange} value={values.instructions} />
<div className="epform__error">
{errors.instructions ? errors.instructions : null}
</div>
</div>
<div className="epform__group --btncontainer">
<button className="epbtn --bordered" type="button" onClick={handleClose}>
{t('cancel')}
</button>
<button className="epbtn --secondary" type="submit">
{t('save')}
</button>
</div>
</form>
</div>
</div>
</Modal>
)
}