Skip to content

Commit 48ffca0

Browse files
committed
new feature, middleWayPoint
1 parent 740e30e commit 48ffca0

File tree

14 files changed

+409
-79
lines changed

14 files changed

+409
-79
lines changed

.env.example

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ PGHOST=xxxxxxx
22
PGDATABASE=xxxxxxx
33
PGUSERNAME=xxxxxxx
44
PGPASSWORD=xxxxxxx
5-
CSRF_SECRET=xxxxxxx
5+
CSRF_SECRET=xxxxxxx
6+
GOOGLE_MAPS_API_KEY=xxxxxxx

migrations/1635336745-create-users-table.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ exports.up = async function up(sql) {
88
password_hash VARCHAR(60) NOT NULL,
99
name VARCHAR(40) NOT NULL,
1010
mail VARCHAR(60) NOT NULL,
11-
address VARCHAR(60)
11+
address VARCHAR(60) NOT NULL,
12+
lat DECIMAL,
13+
lng DECIMAL
1214
);
1315
`;
1416
};

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"dependencies": {
1313
"@headlessui/react": "^1.4.1",
1414
"@heroicons/react": "^1.0.4",
15-
"@types/react-places-autocomplete": "^7.2.9",
15+
"@react-google-maps/api": "^2.7.0",
1616
"bcrypt": "^5.0.1",
1717
"camelcase-keys": "^7.0.1",
1818
"cookie": "^0.4.1",
@@ -36,10 +36,12 @@
3636
"@types/cookie": "^0.4.1",
3737
"@types/csrf": "^3.1.0",
3838
"@types/dotenv-safe": "^8.1.2",
39+
"@types/googlemaps": "^3.43.3",
3940
"@types/js-cookie": "^3.0.0",
4041
"@types/node": "^16.0.0",
4142
"@types/react": "^17.0.6",
4243
"@types/react-dom": "^17.0.5",
44+
"@types/react-places-autocomplete": "^7.2.9",
4345
"@types/sharp": "^0.29.2",
4446
"@typescript-eslint/eslint-plugin": "^4.21.0",
4547
"@typescript-eslint/parser": "^4.21.0",

pages/api/register.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ export type RegisterRequest = {
1717
name: string;
1818
mail: string;
1919
address: string;
20+
lat: number;
21+
lng: number;
2022
};
2123

2224
export type RegisterResponse = { errors: Errors } | { user: User };
@@ -29,13 +31,14 @@ export default async function registerHandler(
2931
!req.body.username ||
3032
!req.body.password ||
3133
!req.body.name ||
32-
!req.body.mail
34+
!req.body.mail ||
35+
!req.body.address
3336
) {
3437
res.status(400).send({
3538
errors: [
3639
{
3740
message:
38-
'Request does not contain username, password, name and E-Mail',
41+
'Request does not contain username, password, name, E-Mail and Address',
3942
},
4043
],
4144
});
@@ -51,6 +54,10 @@ export default async function registerHandler(
5154

5255
const address = req.body.address;
5356

57+
const lat = req.body.lat;
58+
59+
const lng = req.body.lng;
60+
5461
const existingUser = await getUserWithPasswordHashByUsername(username);
5562

5663
if (existingUser) {
@@ -68,6 +75,8 @@ export default async function registerHandler(
6875
passwordHash: passwordHash,
6976
mail: mail,
7077
address: address,
78+
lat: lat,
79+
lng: lng,
7180
});
7281

7382
// clean old sessions

pages/api/users/[userId].ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ export type RegisterRequest = {
1111
name: number;
1212
mail: string;
1313
address: string;
14+
lat: number;
15+
lng: number;
1416
};
1517

1618
export type User = {
@@ -19,6 +21,8 @@ export type User = {
1921
name: number;
2022
mail: string;
2123
address: string;
24+
lat: number;
25+
lng: number;
2226
};
2327

2428
export type RegisterResponse = { user: User };
@@ -41,6 +45,8 @@ export default async function handler(
4145
name: body.name,
4246
mail: body.mail,
4347
address: body.address,
48+
lat: body.lat,
49+
lng: body.lng,
4450
});
4551
return res.status(200).json(updatedUser);
4652
}

pages/editUser.tsx

Lines changed: 80 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1+
import { useLoadScript } from '@react-google-maps/api';
12
import { GetServerSidePropsContext } from 'next';
23
import { useRouter } from 'next/router';
34
import React, { useState } from 'react';
5+
import PlacesAutocomplete, {
6+
geocodeByAddress,
7+
getLatLng,
8+
} from 'react-places-autocomplete';
49
import Layout from '../components/Layout';
510
import { User } from '../util/database';
611
import { Errors } from '../util/types';
@@ -15,14 +20,40 @@ const EditUser = (props: Props) => {
1520
const [newMail, setNewMail] = useState('');
1621
const [newAddress, setNewAddress] = useState('');
1722
const [errors] = useState<Errors>([]);
23+
const [newLat, setNewLat] = useState(0);
24+
const [newLng, setNewLng] = useState(0);
1825

1926
const router = useRouter();
2027

28+
const libraries = ['places'];
29+
30+
const { isLoaded, loadError } = useLoadScript({
31+
googleMapsApiKey: 'AIzaSyDSSIEFPSWv8mx85eU7wqywyKB97k0Lsno',
32+
libraries,
33+
});
34+
35+
if (loadError) return 'Error loading maps';
36+
if (!isLoaded) return 'Loading Maps';
37+
38+
const handleChange = (value: string) => {
39+
setNewAddress(value);
40+
};
41+
42+
const handleSelect = async (value: string) => {
43+
const results = await geocodeByAddress(value);
44+
const latLng = await getLatLng(results[0]);
45+
setNewAddress(value);
46+
setNewLat(latLng.lat);
47+
setNewLng(latLng.lng);
48+
};
49+
2150
const updateUser = async (
2251
id: number,
2352
name: string,
2453
mail: string,
2554
address: string,
55+
lat: number,
56+
lng: number,
2657
) => {
2758
await fetch(`/api/users/${id}`, {
2859
method: 'PATCH',
@@ -31,6 +62,8 @@ const EditUser = (props: Props) => {
3162
name: name,
3263
mail: mail,
3364
address: address,
65+
lat: lat,
66+
lng: lng,
3467
}),
3568
});
3669
};
@@ -77,19 +110,46 @@ const EditUser = (props: Props) => {
77110
</div>
78111
</div>
79112
<div className="mb-10">
80-
<label
81-
htmlFor="address"
82-
className="block text-dark text-normal font-bold mb-2"
83-
>
113+
<div className="block text-dark text-normal font-bold mb-2">
84114
Address
85-
</label>
86-
<input
87-
className="shadow appearance-none border rounded w-full py-2 px-3 text-dark leading-tight focus:outline-none focus:shadow-outline"
88-
id="itemName"
89-
placeholder="Name"
90-
required
91-
onChange={(e) => setNewAddress(e.currentTarget.value)}
92-
/>
115+
<div>
116+
<PlacesAutocomplete
117+
value={newAddress}
118+
onChange={handleChange}
119+
onSelect={handleSelect}
120+
>
121+
{({
122+
getInputProps,
123+
suggestions,
124+
getSuggestionItemProps,
125+
loading,
126+
}) => (
127+
<div>
128+
<input
129+
className="shadow appearance-none border rounded w-full py-2 px-3 text-dark leading-tight focus:outline-none focus:shadow-outline"
130+
{...getInputProps({ placeholder: 'Address' })}
131+
/>
132+
<div>
133+
{loading && <div>Loading...</div>}
134+
{suggestions.map((suggestion) => {
135+
const style = suggestion.active
136+
? { backgroundColor: '#BBE1FA', cursor: 'pointer' }
137+
: { backgroundColor: '#fff', cursor: 'pointer' };
138+
return (
139+
<div
140+
key={`li-suggestion-${suggestion.placeId}`}
141+
{...getSuggestionItemProps(suggestion, { style })}
142+
>
143+
{suggestion.description}
144+
</div>
145+
);
146+
})}
147+
</div>
148+
</div>
149+
)}
150+
</PlacesAutocomplete>
151+
</div>
152+
</div>
93153
<div>
94154
Address now:{' '}
95155
<span className="font-bold">{props.user.address}</span>
@@ -106,7 +166,14 @@ const EditUser = (props: Props) => {
106166
{newName.length && newMail.length && newAddress.length > 0 ? (
107167
<button
108168
onClick={() =>
109-
updateUser(props.user.id, newName, newMail, newAddress)
169+
updateUser(
170+
props.user.id,
171+
newName,
172+
newMail,
173+
newAddress,
174+
newLat,
175+
newLng,
176+
)
110177
}
111178
className="w-full bg-blue shadow-lg text-bright text-xl font-bold py-2 px-10 rounded hover:bg-blue-light hover:text-dark"
112179
>

pages/itempage.js

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -53,15 +53,12 @@ const Itempage = (props) => {
5353
onClick={async (event) => {
5454
event.preventDefault();
5555

56-
await fetch(
57-
`http://localhost:3000/api/items/${item.id}`,
58-
{
59-
method: 'DELETE',
60-
headers: {
61-
'Content-Type': 'application/json',
62-
},
56+
await fetch(`/api/items/${item.id}`, {
57+
method: 'DELETE',
58+
headers: {
59+
'Content-Type': 'application/json',
6360
},
64-
);
61+
});
6562
router.push(`/itempage/`);
6663
}}
6764
className="w-auto shadow-lg bg-red text-bright text-xl font-bold py-2 mb-5 px-10 rounded hover:bg-red-light hover:text-dark"

pages/register.tsx

Lines changed: 67 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { useLoadScript } from '@react-google-maps/api';
12
import { GetServerSidePropsContext } from 'next';
23
import { useRouter } from 'next/router';
34
import React, { useState } from 'react';
@@ -18,9 +19,33 @@ const RegisterPage = (props: Props) => {
1819
const [mail, setMail] = useState('');
1920
const [address, setAddress] = useState('');
2021
const [errors, setErrors] = useState<Errors>([]);
22+
const [lat, setLat] = useState(0);
23+
const [lng, setLng] = useState(0);
2124

2225
const router = useRouter();
2326

27+
const libraries = ['places'];
28+
29+
const { isLoaded, loadError } = useLoadScript({
30+
googleMapsApiKey: 'AIzaSyDSSIEFPSWv8mx85eU7wqywyKB97k0Lsno',
31+
libraries,
32+
});
33+
34+
if (loadError) return 'Error loading maps';
35+
if (!isLoaded) return 'Loading Maps';
36+
37+
const handleChange = (value: string) => {
38+
setAddress(value);
39+
};
40+
41+
const handleSelect = async (value: string) => {
42+
const results = await geocodeByAddress(value);
43+
const latLng = await getLatLng(results[0]);
44+
setAddress(value);
45+
setLat(latLng.lat);
46+
setLng(latLng.lng);
47+
};
48+
2449
return (
2550
<LayoutBeforeLogin>
2651
<div className="max-w-7xl my-2 mx-auto px-4 py-5 text-xl lg:py-10 ">
@@ -42,6 +67,8 @@ const RegisterPage = (props: Props) => {
4267
name: name,
4368
mail: mail,
4469
address: address,
70+
lat: lat,
71+
lng: lng,
4572
csrfToken: props.csrfToken,
4673
}),
4774
});
@@ -110,19 +137,50 @@ const RegisterPage = (props: Props) => {
110137
value={mail}
111138
required
112139
onChange={(event) => setMail(event.currentTarget.value)}
113-
/>{' '}
140+
/>
114141
</label>
115142
</div>
116143
<div className="mb-10">
117-
<label className="block text-dark text-normal font-bold mb-2">
144+
<div className="block text-dark text-normal font-bold mb-2">
118145
Address
119-
<input
120-
className="shadow appearance-none border rounded w-full py-2 px-3 text-dark leading-tight focus:outline-none focus:shadow-outline"
121-
placeholder="Address"
122-
value={address}
123-
onChange={(event) => setAddress(event.currentTarget.value)}
124-
/>
125-
</label>
146+
<div>
147+
<PlacesAutocomplete
148+
value={address}
149+
onChange={handleChange}
150+
onSelect={handleSelect}
151+
>
152+
{({
153+
getInputProps,
154+
suggestions,
155+
getSuggestionItemProps,
156+
loading,
157+
}) => (
158+
<div>
159+
<input
160+
className="shadow appearance-none border rounded w-full py-2 px-3 text-dark leading-tight focus:outline-none focus:shadow-outline"
161+
{...getInputProps({ placeholder: 'Address' })}
162+
/>
163+
<div>
164+
{loading && <div>Loading...</div>}
165+
{suggestions.map((suggestion) => {
166+
const style = suggestion.active
167+
? { backgroundColor: '#BBE1FA', cursor: 'pointer' }
168+
: { backgroundColor: '#fff', cursor: 'pointer' };
169+
return (
170+
<div
171+
key={`li-suggestion-${suggestion.placeId}`}
172+
{...getSuggestionItemProps(suggestion, { style })}
173+
>
174+
{suggestion.description}
175+
</div>
176+
);
177+
})}
178+
</div>
179+
</div>
180+
)}
181+
</PlacesAutocomplete>
182+
</div>
183+
</div>
126184
</div>
127185
<div className="text-red mb-5">
128186
{errors.map((error) => (

0 commit comments

Comments
 (0)