index.tsx 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. import { useQueryClient } from "@tanstack/react-query";
  2. import cn from "classnames";
  3. import { Field, Form, Formik } from "formik";
  4. import { useState } from "react";
  5. import { Alert } from "react-bootstrap";
  6. import { createUser } from "src/api/backend";
  7. import { Button, LocalePicker, Page, ThemeSwitcher } from "src/components";
  8. import { useAuthState } from "src/context";
  9. import { intl } from "src/locale";
  10. import { validateEmail, validateString } from "src/modules/Validations";
  11. import styles from "./index.module.css";
  12. interface Payload {
  13. name: string;
  14. email: string;
  15. password: string;
  16. }
  17. export default function Setup() {
  18. const queryClient = useQueryClient();
  19. const { login } = useAuthState();
  20. const [errorMsg, setErrorMsg] = useState<string | null>(null);
  21. const onSubmit = async (values: Payload, { setSubmitting }: any) => {
  22. setErrorMsg(null);
  23. // Set a nickname, which is the first word of the name
  24. const nickname = values.name.split(" ")[0];
  25. const { password, ...payload } = {
  26. ...values,
  27. ...{
  28. nickname,
  29. auth: {
  30. type: "password",
  31. secret: values.password,
  32. },
  33. },
  34. };
  35. try {
  36. const user = await createUser(payload, true);
  37. if (user && typeof user.id !== "undefined" && user.id) {
  38. try {
  39. await login(user.email, password);
  40. // Trigger a Health change
  41. await queryClient.refetchQueries({ queryKey: ["health"] });
  42. // window.location.reload();
  43. } catch (err: any) {
  44. setErrorMsg(err.message);
  45. }
  46. } else {
  47. setErrorMsg("cannot_create_user");
  48. }
  49. } catch (err: any) {
  50. setErrorMsg(err.message);
  51. }
  52. setSubmitting(false);
  53. };
  54. return (
  55. <Page className="page page-center">
  56. <div className={cn("d-none", "d-md-flex", styles.helperBtns)}>
  57. <LocalePicker />
  58. <ThemeSwitcher />
  59. </div>
  60. <div className="container container-tight py-4">
  61. <div className="text-center mb-4">
  62. <img
  63. className={styles.logo}
  64. src="/images/logo-text-horizontal-grey.png"
  65. alt="Nginx Proxy Manager"
  66. />
  67. </div>
  68. <div className="card card-md">
  69. <Alert variant="danger" show={!!errorMsg} onClose={() => setErrorMsg(null)} dismissible>
  70. {errorMsg}
  71. </Alert>
  72. <Formik
  73. initialValues={
  74. {
  75. name: "",
  76. email: "",
  77. password: "",
  78. } as any
  79. }
  80. onSubmit={onSubmit}
  81. >
  82. {({ isSubmitting }) => (
  83. <Form>
  84. <div className="card-body text-center py-4 p-sm-5">
  85. <h1 className="mt-5">{intl.formatMessage({ id: "setup.title" })}</h1>
  86. <p className="text-secondary">{intl.formatMessage({ id: "setup.preamble" })}</p>
  87. </div>
  88. <hr />
  89. <div className="card-body">
  90. <div className="mb-3">
  91. <Field name="name" validate={validateString(1, 50)}>
  92. {({ field, form }: any) => (
  93. <div className="form-floating mb-3">
  94. <input
  95. id="name"
  96. className={`form-control ${form.errors.name && form.touched.name ? "is-invalid" : ""}`}
  97. placeholder={intl.formatMessage({ id: "user.full-name" })}
  98. {...field}
  99. />
  100. <label htmlFor="name">
  101. {intl.formatMessage({ id: "user.full-name" })}
  102. </label>
  103. {form.errors.name ? (
  104. <div className="invalid-feedback">
  105. {form.errors.name && form.touched.name
  106. ? form.errors.name
  107. : null}
  108. </div>
  109. ) : null}
  110. </div>
  111. )}
  112. </Field>
  113. </div>
  114. <div className="mb-3">
  115. <Field name="email" validate={validateEmail()}>
  116. {({ field, form }: any) => (
  117. <div className="form-floating mb-3">
  118. <input
  119. id="email"
  120. type="email"
  121. className={`form-control ${form.errors.email && form.touched.email ? "is-invalid" : ""}`}
  122. placeholder={intl.formatMessage({ id: "email-address" })}
  123. {...field}
  124. />
  125. <label htmlFor="email">
  126. {intl.formatMessage({ id: "email-address" })}
  127. </label>
  128. {form.errors.email ? (
  129. <div className="invalid-feedback">
  130. {form.errors.email && form.touched.email
  131. ? form.errors.email
  132. : null}
  133. </div>
  134. ) : null}
  135. </div>
  136. )}
  137. </Field>
  138. </div>
  139. <div className="mb-3">
  140. <Field name="password" validate={validateString(8, 100)}>
  141. {({ field, form }: any) => (
  142. <div className="form-floating mb-3">
  143. <input
  144. id="password"
  145. type="password"
  146. className={`form-control ${form.errors.password && form.touched.password ? "is-invalid" : ""}`}
  147. placeholder={intl.formatMessage({ id: "user.new-password" })}
  148. {...field}
  149. />
  150. <label htmlFor="password">
  151. {intl.formatMessage({ id: "user.new-password" })}
  152. </label>
  153. {form.errors.password ? (
  154. <div className="invalid-feedback">
  155. {form.errors.password && form.touched.password
  156. ? form.errors.password
  157. : null}
  158. </div>
  159. ) : null}
  160. </div>
  161. )}
  162. </Field>
  163. </div>
  164. </div>
  165. <div className="text-center my-3 mx-3">
  166. <Button
  167. type="submit"
  168. actionType="primary"
  169. data-bs-dismiss="modal"
  170. isLoading={isSubmitting}
  171. disabled={isSubmitting}
  172. className="w-100"
  173. >
  174. {intl.formatMessage({ id: "save" })}
  175. </Button>
  176. </div>
  177. </Form>
  178. )}
  179. </Formik>
  180. </div>
  181. </div>
  182. </Page>
  183. );
  184. }