@stanlemon/app-template 0.3.20 → 0.3.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app.js +22 -2
- package/package.json +11 -7
- package/src/App.test.tsx +65 -58
- package/src/App.tsx +60 -211
- package/src/Session.tsx +124 -49
- package/src/components/Column.tsx +24 -0
- package/src/components/ErrorMessage.tsx +16 -0
- package/src/components/Header.jsx +14 -0
- package/src/{Input.tsx → components/Input.tsx} +10 -7
- package/src/components/Row.tsx +24 -0
- package/src/components/Spacer.tsx +5 -0
- package/src/components/SuccessMessage.tsx +12 -0
- package/src/components/index.tsx +7 -0
- package/src/helpers/fetchApi.tsx +73 -0
- package/src/index.tsx +2 -0
- package/src/views/Account.test.tsx +87 -0
- package/src/views/Account.tsx +22 -0
- package/src/views/Items.test.tsx +53 -0
- package/src/views/Items.tsx +84 -0
- package/src/views/Login.tsx +57 -0
- package/src/views/Password.tsx +89 -0
- package/src/views/Profile.tsx +66 -0
- package/src/views/SignUp.tsx +82 -0
- package/src/views/Verify.tsx +24 -0
- package/src/views/index.tsx +7 -0
- package/src/Header.jsx +0 -4
- package/src/Login.tsx +0 -82
- package/src/Register.tsx +0 -82
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { useState, useEffect, useContext } from "react";
|
|
2
|
+
import { SessionContext } from "../Session";
|
|
3
|
+
import { Input, Row, Spacer } from "../components/";
|
|
4
|
+
import { fetchApi } from "../helpers/fetchApi";
|
|
5
|
+
|
|
6
|
+
export type ItemData = {
|
|
7
|
+
id: string;
|
|
8
|
+
item: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export function Items() {
|
|
12
|
+
const { token, setError } = useContext(SessionContext);
|
|
13
|
+
const [items, setItems] = useState<ItemData[]>([]);
|
|
14
|
+
const [value, setValue] = useState<string>("");
|
|
15
|
+
|
|
16
|
+
const catchError = (err: Error) => {
|
|
17
|
+
setError(err.message);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const saveItem = (item: string) => {
|
|
21
|
+
fetchApi<ItemData[], { item: string }>("/api/items", token, "post", {
|
|
22
|
+
item,
|
|
23
|
+
})
|
|
24
|
+
.then((items) => {
|
|
25
|
+
setItems(items);
|
|
26
|
+
})
|
|
27
|
+
.catch(catchError);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const deleteItem = (id: string) => {
|
|
31
|
+
fetchApi<ItemData[], string>(`/api/items/${id}`, token, "delete")
|
|
32
|
+
.then((items) => {
|
|
33
|
+
setItems(items);
|
|
34
|
+
})
|
|
35
|
+
.catch(catchError);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const addItem = () => {
|
|
39
|
+
if (value.trim() === "") {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
saveItem(value);
|
|
43
|
+
setValue("");
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
fetchApi<ItemData[], null>("/api/items", token)
|
|
48
|
+
.then((items) => {
|
|
49
|
+
setItems(items ?? []);
|
|
50
|
+
})
|
|
51
|
+
.catch(catchError);
|
|
52
|
+
}, []);
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<>
|
|
56
|
+
<h2>New Item</h2>
|
|
57
|
+
<Input
|
|
58
|
+
label="Item"
|
|
59
|
+
name="item"
|
|
60
|
+
value={value}
|
|
61
|
+
onChange={(value) => setValue(value)}
|
|
62
|
+
onEnter={addItem}
|
|
63
|
+
/>
|
|
64
|
+
<button onClick={addItem}>Add</button>
|
|
65
|
+
<Spacer />
|
|
66
|
+
<h2>My Items</h2>
|
|
67
|
+
<ul style={{ padding: 0 }}>
|
|
68
|
+
{items.map(({ item, id }, i) => (
|
|
69
|
+
<Row key={i} as="li">
|
|
70
|
+
<button
|
|
71
|
+
style={{ marginLeft: "auto", order: 2 }}
|
|
72
|
+
onClick={() => deleteItem(id)}
|
|
73
|
+
>
|
|
74
|
+
Delete
|
|
75
|
+
</button>
|
|
76
|
+
<div>{item}</div>
|
|
77
|
+
</Row>
|
|
78
|
+
))}
|
|
79
|
+
</ul>
|
|
80
|
+
</>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export default Items;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { useState, useContext } from "react";
|
|
2
|
+
import { Input, Spacer } from "../components/";
|
|
3
|
+
import { ProfileData, SessionContext, SessionData } from "../Session";
|
|
4
|
+
import fetchApi, { ApiError } from "../helpers/fetchApi";
|
|
5
|
+
|
|
6
|
+
export type LoginForm = {
|
|
7
|
+
username: string;
|
|
8
|
+
password: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export function Login() {
|
|
12
|
+
const [values, setValues] = useState<LoginForm>({
|
|
13
|
+
username: "",
|
|
14
|
+
password: "",
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
const { setToken, setUser, setError } = useContext(SessionContext);
|
|
18
|
+
|
|
19
|
+
const onSubmit = () => {
|
|
20
|
+
setError(null);
|
|
21
|
+
fetchApi<SessionData, LoginForm>("/auth/login", null, "post", values)
|
|
22
|
+
.then((session: SessionData) => {
|
|
23
|
+
setToken(session.token as string);
|
|
24
|
+
setUser(session.user as ProfileData);
|
|
25
|
+
setError(null);
|
|
26
|
+
})
|
|
27
|
+
.catch((err: ApiError) => {
|
|
28
|
+
if (err.message === "Unauthorized") {
|
|
29
|
+
setError(err.body.message as string);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<>
|
|
36
|
+
<Input
|
|
37
|
+
name="username"
|
|
38
|
+
label="Username"
|
|
39
|
+
value={values.username}
|
|
40
|
+
onChange={(value) => setValues({ ...values, username: value })}
|
|
41
|
+
autoCapitalize="off"
|
|
42
|
+
/>
|
|
43
|
+
<Input
|
|
44
|
+
name="password"
|
|
45
|
+
type="password"
|
|
46
|
+
label="Password"
|
|
47
|
+
value={values.password}
|
|
48
|
+
onChange={(value) => setValues({ ...values, password: value })}
|
|
49
|
+
onEnter={onSubmit}
|
|
50
|
+
/>
|
|
51
|
+
<Spacer />
|
|
52
|
+
<button onClick={onSubmit}>Login</button>
|
|
53
|
+
</>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export default Login;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { useContext, useState } from "react";
|
|
2
|
+
import { SessionContext } from "../Session";
|
|
3
|
+
import { Header, Input } from "../components";
|
|
4
|
+
import fetchApi, { ApiError } from "../helpers/fetchApi";
|
|
5
|
+
|
|
6
|
+
export type PasswordForm = {
|
|
7
|
+
current_password: string;
|
|
8
|
+
new_password1: string;
|
|
9
|
+
new_password2: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type PasswordRequest = {
|
|
13
|
+
current_password: string;
|
|
14
|
+
password: string;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const DEFAULT_PASSWORD_DATA: PasswordForm = {
|
|
18
|
+
current_password: "",
|
|
19
|
+
new_password1: "",
|
|
20
|
+
new_password2: "",
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export function Password() {
|
|
24
|
+
const { token, setMessage, setError } = useContext(SessionContext);
|
|
25
|
+
const [password, setPassword] = useState<PasswordForm>(DEFAULT_PASSWORD_DATA);
|
|
26
|
+
const [errors, setErrors] = useState<Partial<PasswordForm>>({});
|
|
27
|
+
|
|
28
|
+
const storePassword = (key: keyof PasswordForm, value: string) => {
|
|
29
|
+
setPassword({ ...password, [key]: value });
|
|
30
|
+
};
|
|
31
|
+
const savePassword = () => {
|
|
32
|
+
setErrors({});
|
|
33
|
+
|
|
34
|
+
if (password.new_password1 !== password.new_password2) {
|
|
35
|
+
setErrors({ new_password2: "Passwords do not match" });
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
fetchApi<null, PasswordRequest>("/auth/password", token, "post", {
|
|
40
|
+
current_password: password.current_password,
|
|
41
|
+
password: password.new_password1,
|
|
42
|
+
})
|
|
43
|
+
.then(() => {
|
|
44
|
+
setPassword(DEFAULT_PASSWORD_DATA);
|
|
45
|
+
setErrors({});
|
|
46
|
+
setMessage("Password updated.");
|
|
47
|
+
setError(null);
|
|
48
|
+
})
|
|
49
|
+
.catch((err: ApiError) => {
|
|
50
|
+
if (err.code === 400) {
|
|
51
|
+
setErrors({ ...(err.body.errors as PasswordForm) });
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<>
|
|
58
|
+
<Header level={2}>Password</Header>
|
|
59
|
+
<Input
|
|
60
|
+
name="current_password"
|
|
61
|
+
type="password"
|
|
62
|
+
label="Current Password"
|
|
63
|
+
value={password.current_password}
|
|
64
|
+
error={errors.current_password}
|
|
65
|
+
onChange={(value) => storePassword("current_password", value)}
|
|
66
|
+
/>
|
|
67
|
+
<Input
|
|
68
|
+
name="new_password1"
|
|
69
|
+
type="password"
|
|
70
|
+
label="New Password"
|
|
71
|
+
value={password.new_password1}
|
|
72
|
+
error={errors.new_password1}
|
|
73
|
+
onChange={(value) => storePassword("new_password1", value)}
|
|
74
|
+
/>
|
|
75
|
+
<Input
|
|
76
|
+
name="new_password2"
|
|
77
|
+
type="password"
|
|
78
|
+
label="Repeat New Password"
|
|
79
|
+
value={password.new_password2}
|
|
80
|
+
error={errors.new_password2}
|
|
81
|
+
onChange={(value) => storePassword("new_password2", value)}
|
|
82
|
+
/>
|
|
83
|
+
|
|
84
|
+
<button onClick={savePassword}>Update</button>
|
|
85
|
+
</>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export default Password;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { useContext, useState } from "react";
|
|
2
|
+
import { ProfileData, SessionContext } from "../Session";
|
|
3
|
+
import { Header, Input } from "../components";
|
|
4
|
+
import fetchApi, { ApiError } from "../helpers/fetchApi";
|
|
5
|
+
|
|
6
|
+
export type ProfileForm = {
|
|
7
|
+
name: string;
|
|
8
|
+
email: string;
|
|
9
|
+
};
|
|
10
|
+
export const DEFAULT_PROFILE_DATA: ProfileForm = {
|
|
11
|
+
name: "",
|
|
12
|
+
email: "",
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export function Profile() {
|
|
16
|
+
const { token, user, setUser, setMessage } = useContext(SessionContext);
|
|
17
|
+
const [errors, setErrors] = useState<Partial<ProfileForm>>({});
|
|
18
|
+
const [profile, setProfile] = useState<ProfileForm>({
|
|
19
|
+
...DEFAULT_PROFILE_DATA,
|
|
20
|
+
name: user?.name ?? "",
|
|
21
|
+
email: user?.email ?? "",
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const storeProfile = (key: keyof ProfileForm, value: string) => {
|
|
25
|
+
setProfile({ ...profile, [key]: value });
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const saveProfile = () => {
|
|
29
|
+
setErrors({});
|
|
30
|
+
fetchApi<ProfileData, ProfileForm>("/auth/user", token, "put", profile)
|
|
31
|
+
.then((newProfile) => {
|
|
32
|
+
setProfile(newProfile);
|
|
33
|
+
setUser(newProfile);
|
|
34
|
+
setErrors({});
|
|
35
|
+
setMessage("Profile saved.");
|
|
36
|
+
})
|
|
37
|
+
.catch((err: ApiError) => {
|
|
38
|
+
if (err.code === 400) {
|
|
39
|
+
setErrors({ ...(err.body.errors as ProfileForm) });
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<>
|
|
46
|
+
<Header level={2}>Profile</Header>
|
|
47
|
+
<Input
|
|
48
|
+
name="name"
|
|
49
|
+
label="Name"
|
|
50
|
+
value={profile.name}
|
|
51
|
+
error={errors.name}
|
|
52
|
+
onChange={(value) => storeProfile("name", value)}
|
|
53
|
+
/>
|
|
54
|
+
<Input
|
|
55
|
+
name="email"
|
|
56
|
+
label="Email"
|
|
57
|
+
value={profile.email}
|
|
58
|
+
error={errors.email}
|
|
59
|
+
onChange={(value) => storeProfile("email", value)}
|
|
60
|
+
/>
|
|
61
|
+
<button onClick={saveProfile}>Save</button>
|
|
62
|
+
</>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export default Profile;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { useState, useContext } from "react";
|
|
2
|
+
import { Input, Spacer } from "../components/";
|
|
3
|
+
import { SessionContext, ProfileData, SessionData } from "../Session";
|
|
4
|
+
import fetchApi, { ApiError } from "../helpers/fetchApi";
|
|
5
|
+
|
|
6
|
+
export type FormErrors = {
|
|
7
|
+
errors: Record<string, string>;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export type SignUpForm = {
|
|
11
|
+
name: string;
|
|
12
|
+
email: string;
|
|
13
|
+
username: string;
|
|
14
|
+
password: string;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export function SignUp() {
|
|
18
|
+
const [errors, setErrors] = useState<Record<string, string>>({});
|
|
19
|
+
const [values, setValues] = useState<SignUpForm>({
|
|
20
|
+
name: "",
|
|
21
|
+
email: "",
|
|
22
|
+
username: "",
|
|
23
|
+
password: "",
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const { setToken, setUser, setError } = useContext(SessionContext);
|
|
27
|
+
|
|
28
|
+
const onSubmit = () => {
|
|
29
|
+
setErrors({});
|
|
30
|
+
fetchApi<SessionData, SignUpForm>("/auth/signup", null, "post", values)
|
|
31
|
+
.then((session: SessionData) => {
|
|
32
|
+
setToken(session.token as string);
|
|
33
|
+
setUser(session.user as ProfileData);
|
|
34
|
+
})
|
|
35
|
+
.catch((err: ApiError) => {
|
|
36
|
+
if (err.message !== "Unauthorized") {
|
|
37
|
+
setError(err.body.message as string);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<>
|
|
44
|
+
<Input
|
|
45
|
+
name="username"
|
|
46
|
+
label="Username"
|
|
47
|
+
value={values.username}
|
|
48
|
+
onChange={(value) => setValues({ ...values, username: value })}
|
|
49
|
+
error={errors?.username}
|
|
50
|
+
autoCapitalize="off"
|
|
51
|
+
/>
|
|
52
|
+
<Input
|
|
53
|
+
name="name"
|
|
54
|
+
label="Name"
|
|
55
|
+
value={values.name}
|
|
56
|
+
onChange={(value) => setValues({ ...values, name: value })}
|
|
57
|
+
error={errors?.name}
|
|
58
|
+
autoCapitalize="off"
|
|
59
|
+
/>
|
|
60
|
+
<Input
|
|
61
|
+
name="email"
|
|
62
|
+
label="Email"
|
|
63
|
+
value={values.email}
|
|
64
|
+
onChange={(value) => setValues({ ...values, email: value })}
|
|
65
|
+
error={errors?.email}
|
|
66
|
+
autoCapitalize="off"
|
|
67
|
+
/>
|
|
68
|
+
<Input
|
|
69
|
+
name="password"
|
|
70
|
+
type="password"
|
|
71
|
+
label="Password"
|
|
72
|
+
value={values.password}
|
|
73
|
+
onChange={(value) => setValues({ ...values, password: value })}
|
|
74
|
+
error={errors?.password}
|
|
75
|
+
/>
|
|
76
|
+
<Spacer />
|
|
77
|
+
<button onClick={onSubmit}>Sign Up</button>
|
|
78
|
+
</>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export default SignUp;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { useContext, useEffect } from "react";
|
|
2
|
+
import fetchApi, { ApiError } from "../helpers/fetchApi";
|
|
3
|
+
import { SessionContext } from "../Session";
|
|
4
|
+
|
|
5
|
+
export function Verify({ token = null }: { token: string | null }) {
|
|
6
|
+
const { setError, setMessage } = useContext(SessionContext);
|
|
7
|
+
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
fetchApi(`/auth/verify/${token}`)
|
|
10
|
+
.then((res) => {
|
|
11
|
+
setMessage("Successfully verified your account!");
|
|
12
|
+
})
|
|
13
|
+
.catch((err: ApiError) => {
|
|
14
|
+
if (err.message === "Bad Request") {
|
|
15
|
+
setError(err.body.message as string);
|
|
16
|
+
console.error(err, err.message, err.body);
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
}, []);
|
|
20
|
+
|
|
21
|
+
return <></>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export default Verify;
|
package/src/Header.jsx
DELETED
package/src/Login.tsx
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import { useState, useContext } from "react";
|
|
2
|
-
import { ErrorResponse, Spacer } from "./App";
|
|
3
|
-
import { SessionContext, SessionData, UserData } from "./Session";
|
|
4
|
-
import Input from "./Input";
|
|
5
|
-
|
|
6
|
-
export default function Login() {
|
|
7
|
-
const [error, setError] = useState<string | null>(null);
|
|
8
|
-
const [values, setValues] = useState<UserData>({
|
|
9
|
-
name: "",
|
|
10
|
-
email: "",
|
|
11
|
-
username: "",
|
|
12
|
-
password: "",
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
const { setSession } = useContext(SessionContext);
|
|
16
|
-
|
|
17
|
-
const onSubmit = () => {
|
|
18
|
-
setError(null);
|
|
19
|
-
fetch("/auth/login", {
|
|
20
|
-
headers: {
|
|
21
|
-
Accept: "application/json",
|
|
22
|
-
"Content-Type": "application/json",
|
|
23
|
-
},
|
|
24
|
-
method: "POST",
|
|
25
|
-
body: JSON.stringify(values),
|
|
26
|
-
})
|
|
27
|
-
.then((response) =>
|
|
28
|
-
response.json().then((data: Record<string, unknown>) => ({
|
|
29
|
-
ok: response.ok,
|
|
30
|
-
status: response.status,
|
|
31
|
-
data,
|
|
32
|
-
}))
|
|
33
|
-
)
|
|
34
|
-
.then(
|
|
35
|
-
({
|
|
36
|
-
ok,
|
|
37
|
-
status,
|
|
38
|
-
data,
|
|
39
|
-
}: {
|
|
40
|
-
ok: boolean;
|
|
41
|
-
status: number;
|
|
42
|
-
data: Record<string, unknown>;
|
|
43
|
-
}) => {
|
|
44
|
-
if (ok) {
|
|
45
|
-
setSession(data as SessionData);
|
|
46
|
-
} else {
|
|
47
|
-
setError((data as ErrorResponse).message);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
)
|
|
51
|
-
.catch((err: Error) => {
|
|
52
|
-
setError(err.message);
|
|
53
|
-
});
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
return (
|
|
57
|
-
<>
|
|
58
|
-
{error && (
|
|
59
|
-
<div>
|
|
60
|
-
<strong>{error}</strong>
|
|
61
|
-
</div>
|
|
62
|
-
)}
|
|
63
|
-
<Input
|
|
64
|
-
name="username"
|
|
65
|
-
label="Username"
|
|
66
|
-
value={values.username}
|
|
67
|
-
onChange={(value) => setValues({ ...values, username: value })}
|
|
68
|
-
autoCapitalize="off"
|
|
69
|
-
/>
|
|
70
|
-
<Input
|
|
71
|
-
name="password"
|
|
72
|
-
type="password"
|
|
73
|
-
label="Password"
|
|
74
|
-
value={values.password}
|
|
75
|
-
onChange={(value) => setValues({ ...values, password: value })}
|
|
76
|
-
onEnter={onSubmit}
|
|
77
|
-
/>
|
|
78
|
-
<Spacer />
|
|
79
|
-
<button onClick={onSubmit}>Login</button>
|
|
80
|
-
</>
|
|
81
|
-
);
|
|
82
|
-
}
|
package/src/Register.tsx
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import { useState, useContext } from "react";
|
|
2
|
-
import { FormErrors, ErrorMessage, Spacer } from "./App";
|
|
3
|
-
import { SessionData, UserData, SessionContext } from "./Session";
|
|
4
|
-
import Input from "./Input";
|
|
5
|
-
|
|
6
|
-
// eslint-disable-next-line max-lines-per-function
|
|
7
|
-
export default function Register() {
|
|
8
|
-
const [error, setError] = useState<string | boolean>(false);
|
|
9
|
-
const [errors, setErrors] = useState<Record<string, string>>({});
|
|
10
|
-
const [values, setValues] = useState<UserData>({
|
|
11
|
-
name: "",
|
|
12
|
-
email: "",
|
|
13
|
-
username: "",
|
|
14
|
-
password: "",
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
const { setSession } = useContext(SessionContext);
|
|
18
|
-
|
|
19
|
-
const onSubmit = () => {
|
|
20
|
-
setErrors({});
|
|
21
|
-
fetch("/auth/register", {
|
|
22
|
-
headers: {
|
|
23
|
-
Accept: "application/json",
|
|
24
|
-
"Content-Type": "application/json",
|
|
25
|
-
},
|
|
26
|
-
method: "POST",
|
|
27
|
-
body: JSON.stringify(values),
|
|
28
|
-
})
|
|
29
|
-
.then((response) =>
|
|
30
|
-
response.json().then((data: Record<string, unknown>) => ({
|
|
31
|
-
ok: response.ok,
|
|
32
|
-
status: response.status,
|
|
33
|
-
data,
|
|
34
|
-
}))
|
|
35
|
-
)
|
|
36
|
-
.then(
|
|
37
|
-
({
|
|
38
|
-
ok,
|
|
39
|
-
status,
|
|
40
|
-
data,
|
|
41
|
-
}: {
|
|
42
|
-
ok: boolean;
|
|
43
|
-
status: number;
|
|
44
|
-
data: Record<string, unknown>;
|
|
45
|
-
}) => {
|
|
46
|
-
if (ok) {
|
|
47
|
-
setSession(data as SessionData);
|
|
48
|
-
} else {
|
|
49
|
-
setErrors((data as FormErrors).errors);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
)
|
|
53
|
-
.catch((err: Error) => {
|
|
54
|
-
// Put any generis error onto the username field
|
|
55
|
-
setError(err.message);
|
|
56
|
-
});
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
return (
|
|
60
|
-
<>
|
|
61
|
-
<ErrorMessage error={error} />
|
|
62
|
-
<Input
|
|
63
|
-
name="username"
|
|
64
|
-
label="Username"
|
|
65
|
-
value={values.username}
|
|
66
|
-
onChange={(value) => setValues({ ...values, username: value })}
|
|
67
|
-
error={errors.username}
|
|
68
|
-
autoCapitalize="off"
|
|
69
|
-
/>
|
|
70
|
-
<Input
|
|
71
|
-
name="password"
|
|
72
|
-
type="password"
|
|
73
|
-
label="Password"
|
|
74
|
-
value={values.password}
|
|
75
|
-
onChange={(value) => setValues({ ...values, password: value })}
|
|
76
|
-
error={errors.password}
|
|
77
|
-
/>
|
|
78
|
-
<Spacer />
|
|
79
|
-
<button onClick={onSubmit}>Register</button>
|
|
80
|
-
</>
|
|
81
|
-
);
|
|
82
|
-
}
|