Skip to content

Commit f6ce7c7

Browse files
committed
chore: clean codes
1 parent 6eb9125 commit f6ce7c7

15 files changed

+689
-0
lines changed

ui/src/ErrorPage.tsx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { useRouteError } from "react-router-dom";
2+
3+
const ErrorPage = () => {
4+
const error = useRouteError() as any;
5+
console.error(error);
6+
7+
return (
8+
<div id="error-page">
9+
<h1>Oops!</h1>
10+
<p>Sorry, an unexpected error has occurred.</p>
11+
<p>
12+
<i>{error.statusText || error.message}</i>
13+
</p>
14+
</div>
15+
);
16+
};
17+
18+
export default ErrorPage;

ui/src/contacts/AppNavbar.tsx

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { useState } from 'react';
2+
import { Link } from 'react-router-dom';
3+
import { Collapse, Nav, Navbar, NavbarBrand, NavbarToggler, NavItem, NavLink } from 'reactstrap';
4+
5+
const AppNavbar = () => {
6+
7+
const [isOpen, setIsOpen] = useState(false);
8+
9+
return (
10+
<Navbar color="dark" dark expand="md" className='mb-3'>
11+
<NavbarBrand tag={Link} to="/">Address Book</NavbarBrand>
12+
<NavbarToggler onClick={() => { setIsOpen(!isOpen) }}/>
13+
<Collapse isOpen={isOpen} navbar>
14+
<Nav className="justify-content-end" style={{width: "100%"}} navbar>
15+
<NavItem>
16+
<NavLink href="https://twitter.com/hantsy">@hantsy</NavLink>
17+
</NavItem>
18+
<NavItem>
19+
<NavLink href="https://github.com/hantsy/react-spring-mongo-kotlin">GitHub</NavLink>
20+
</NavItem>
21+
</Nav>
22+
</Collapse>
23+
</Navbar>
24+
);
25+
};
26+
27+
export default AppNavbar;
Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
import { useEffect, useState } from "react";
2+
import { Link, useNavigate, useParams } from "react-router-dom";
3+
import { Button, Form, FormGroup, Input, Label } from "reactstrap";
4+
5+
const ContactEditPage = () => {
6+
type AddressForm = {
7+
line1: string;
8+
line2: string;
9+
street: string;
10+
city: string;
11+
zipCode: string;
12+
};
13+
type PersonForm = {
14+
id: string;
15+
firstName: string;
16+
lastName: string;
17+
birthOfDate: string;
18+
email: string;
19+
phoneNumber: string;
20+
address: AddressForm;
21+
};
22+
23+
const initialFormState: PersonForm = {
24+
id: "",
25+
firstName: "",
26+
lastName: "",
27+
email: "",
28+
birthOfDate: "",
29+
phoneNumber: "",
30+
address: {
31+
line1: "",
32+
line2: "",
33+
street: "",
34+
city: "",
35+
zipCode: "",
36+
},
37+
};
38+
const [person, setPerson] = useState<PersonForm>(initialFormState);
39+
const navigate = useNavigate();
40+
const { id } = useParams();
41+
42+
useEffect(() => {
43+
if (id !== "new") {
44+
fetch(`/persons/${id}`)
45+
.then((response) => response.json())
46+
.then((data) => setPerson(data));
47+
}
48+
}, [id, setPerson]);
49+
50+
const handleChange = (event: any) => {
51+
const { name, value } = event.target;
52+
console.log(`handling event:${name}->${value}`);
53+
setPerson({ ...person, [name]: value });
54+
};
55+
56+
const handleAddressLine1 = (event: any) => {
57+
const line1 = {
58+
address: { ...person.address, line1: event.target.value },
59+
} as PersonForm;
60+
setPerson({ ...person, ...line1 });
61+
};
62+
63+
const handleAddressLine2 = (event: any) => {
64+
const line2 = {
65+
address: { ...person.address, line2: event.target.value },
66+
} as PersonForm;
67+
setPerson({ ...person, ...line2 });
68+
};
69+
const handleAddressStreet = (event: any) => {
70+
const street = {
71+
address: { ...person.address, street: event.target.value },
72+
} as PersonForm;
73+
setPerson({ ...person, ...street });
74+
};
75+
76+
const handleAddressCity = (event: any) => {
77+
const city = {
78+
address: { ...person.address, city: event.target.value },
79+
} as PersonForm;
80+
setPerson({ ...person, ...city });
81+
};
82+
83+
const handleAddressZipCode = (event: any) => {
84+
const zipCode = {
85+
address: { ...person.address, zipCode: event.target.value },
86+
} as PersonForm;
87+
setPerson({ ...person, ...zipCode });
88+
};
89+
90+
const handleSubmit = async (event: any) => {
91+
event.preventDefault();
92+
93+
const url = person.id ? `/persons/${person.id}` : "/persons";
94+
await fetch(url, {
95+
method: person.id ? "PUT" : "POST",
96+
headers: {
97+
Accept: "application/json",
98+
"Content-Type": "application/json",
99+
},
100+
body: JSON.stringify(person),
101+
});
102+
103+
setPerson(initialFormState);
104+
navigate("/persons");
105+
};
106+
107+
const title = <h2>{person.id ? "Edit Contact" : "Add Contact"}</h2>;
108+
109+
return (
110+
<>
111+
<h2>{title}</h2>
112+
<Form autoComplete="false" onSubmit={handleSubmit}>
113+
<div className="row mb-1">
114+
<FormGroup className="col-md-6 col-12">
115+
<Label for="lastName">First Name</Label>
116+
<Input
117+
type="text"
118+
name="firstName"
119+
id="firstName"
120+
required={true}
121+
value={person.firstName || ""}
122+
onChange={handleChange}
123+
/>
124+
</FormGroup>
125+
<FormGroup className="col-md-6 col-12">
126+
<Label for="lastName">Last Name</Label>
127+
<Input
128+
type="text"
129+
name="lastName"
130+
id="lastName"
131+
required={true}
132+
value={person.lastName || ""}
133+
onChange={handleChange}
134+
/>
135+
</FormGroup>
136+
</div>
137+
<div className="row mb-3">
138+
<FormGroup className="col-md-4 col-12">
139+
<Label for="birthOfDate">Birth Of Date</Label>
140+
<Input
141+
type="date"
142+
name="birthOfDate"
143+
id="birthOfDate"
144+
value={person.birthOfDate || ""}
145+
onChange={handleChange}
146+
/>
147+
</FormGroup>
148+
<FormGroup className="col-md-5 col-12">
149+
<Label for="email">Email</Label>
150+
<Input
151+
type="email"
152+
name="email"
153+
id="email"
154+
value={person.email || ""}
155+
onChange={handleChange}
156+
/>
157+
</FormGroup>
158+
<FormGroup className="col-md-3 col-12">
159+
<Label for="phoneNumber">PhoneNumber</Label>
160+
<Input
161+
type="text"
162+
name="phoneNumber"
163+
id="phoneNumber"
164+
value={person.phoneNumber || ""}
165+
onChange={handleChange}
166+
/>
167+
</FormGroup>
168+
</div>
169+
<FormGroup>
170+
<Label for="line1">Address Line1</Label>
171+
<Input
172+
type="text"
173+
name="address.line1"
174+
id="line1"
175+
value={person.address.line1}
176+
onChange={handleAddressLine1}
177+
/>
178+
</FormGroup>
179+
<FormGroup>
180+
<Label for="line2">Address Line2</Label>
181+
<Input
182+
type="text"
183+
name="address.line2"
184+
id="line2"
185+
value={person.address.line2 || ""}
186+
onChange={handleAddressLine2}
187+
/>
188+
</FormGroup>
189+
<div className="row mb-3">
190+
<FormGroup className="col-md-4 col-12">
191+
<Label for="street">Street</Label>
192+
<Input
193+
type="text"
194+
name="address.street"
195+
id="street"
196+
value={person.address.street || ""}
197+
onChange={handleAddressStreet}
198+
/>
199+
</FormGroup>
200+
<FormGroup className="col-md-5 col-12">
201+
<Label for="city">City</Label>
202+
<Input
203+
type="text"
204+
name="address.city"
205+
id="city"
206+
value={person.address.city || ""}
207+
onChange={handleAddressCity}
208+
/>
209+
</FormGroup>
210+
<FormGroup className="col-md-3 col-12">
211+
<Label for="country">Zip Code</Label>
212+
<Input
213+
type="text"
214+
name="address.zipCode"
215+
id="zipCode"
216+
value={person.address.zipCode || ""}
217+
onChange={handleAddressZipCode}
218+
/>
219+
</FormGroup>
220+
</div>
221+
<FormGroup>
222+
<Button color="primary" type="submit">
223+
Save
224+
</Button>{" "}
225+
<Button color="secondary" tag={Link} to="/groups">
226+
Cancel
227+
</Button>
228+
</FormGroup>
229+
</Form>
230+
</>
231+
);
232+
};
233+
234+
export default ContactEditPage;

ui/src/contacts/ContactList.tsx

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { Button, ButtonGroup, Table } from "reactstrap";
2+
import { PersonSummary } from "./Model";
3+
import { Link } from "react-router-dom";
4+
import moment from "moment";
5+
6+
interface ContactListProps {
7+
persons: PersonSummary[];
8+
setPersons: (data: PersonSummary[]) => void;
9+
}
10+
11+
const ContactListLines = ({ persons, setPersons }: ContactListProps) => {
12+
const remove = async (id: string) => {
13+
await fetch(`/persons/${id}`, {
14+
method: "DELETE",
15+
headers: {
16+
Accept: "application/json",
17+
"Content-Type": "application/json",
18+
},
19+
}).then(() => {
20+
let updatedGroups = [...persons].filter((i) => i.id !== id);
21+
setPersons(updatedGroups);
22+
});
23+
};
24+
25+
return persons.map((person) => {
26+
return (
27+
<tr key={person.id}>
28+
<td style={{ whiteSpace: "nowrap" }}>{person.name}</td>
29+
<td>{person.email}</td>
30+
<td>{moment(person.birthOfDate).format("YYYY/MM/DD")}</td>
31+
<td>
32+
<ButtonGroup>
33+
<Button
34+
size="sm"
35+
color="primary"
36+
tag={Link}
37+
to={"/contacts/" + person.id}
38+
>
39+
Edit
40+
</Button>
41+
<Button size="sm" color="danger" onClick={() => remove(person.id)}>
42+
Delete
43+
</Button>
44+
</ButtonGroup>
45+
</td>
46+
</tr>
47+
);
48+
});
49+
};
50+
51+
export const ContactList: React.FC<ContactListProps> = ({
52+
persons,
53+
setPersons,
54+
}: ContactListProps) => {
55+
return (
56+
<Table className="mt-4">
57+
<thead>
58+
<tr>
59+
<th>Name</th>
60+
<th>Email</th>
61+
<th>Birth Of Date</th>
62+
<th>Actions</th>
63+
</tr>
64+
</thead>
65+
<tbody>{ContactListLines({persons, setPersons})}</tbody>
66+
</Table>
67+
);
68+
};

0 commit comments

Comments
 (0)