Skip to content

Commit 8db403e

Browse files
Adding some react frontend to showcase app
1 parent 7a876cf commit 8db403e

6 files changed

Lines changed: 146 additions & 4 deletions

File tree

google_contributors/src/App.jsx

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,25 @@ const API_KEY = import.meta.env.VITE_API_KEY
66

77
const REVIEWS_PER_PAGE = 4
88

9+
function calculateDistance(lat1, lon1, lat2, lon2) {
10+
const R = 6371 // Earth's radius in km
11+
const dLat = (lat2 - lat1) * Math.PI / 180
12+
const dLon = (lon2 - lon1) * Math.PI / 180
13+
const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
14+
Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) *
15+
Math.sin(dLon / 2) * Math.sin(dLon / 2)
16+
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a))
17+
return R * c // Distance in km
18+
}
19+
920
function App() {
1021
const [reviews, setReviews] = useState([])
1122
const [sortBy, setSortBy] = useState('date')
1223
const [loading, setLoading] = useState(true)
1324
const [error, setError] = useState(null)
1425
const [contributorInfo, setContributorInfo] = useState(null)
1526
const [currentPage, setCurrentPage] = useState(1)
27+
const [userLocation, setUserLocation] = useState(null)
1628

1729
useEffect(() => {
1830
async function fetchReviews() {
@@ -39,6 +51,24 @@ function App() {
3951
fetchReviews()
4052
}, [])
4153

54+
useEffect(() => {
55+
async function fetchUserLocation() {
56+
try {
57+
const response = await fetch('http://ip-api.com/json/')
58+
if (response.ok) {
59+
const data = await response.json()
60+
if (data.status === 'success') {
61+
setUserLocation({ lat: data.lat, lon: data.lon })
62+
}
63+
}
64+
} catch (err) {
65+
console.error('Failed to fetch user location:', err)
66+
}
67+
}
68+
69+
fetchUserLocation()
70+
}, [])
71+
4272
const parseDate = (dateStr) => {
4373
if (!dateStr) return 0
4474
const match = dateStr.match(/(\d+)\s+(day|week|month|year)s?\s+ago/)
@@ -50,10 +80,21 @@ function App() {
5080
return num * (multipliers[unit] || 1)
5181
}
5282

83+
const getDistance = (review) => {
84+
if (!userLocation || !review.place_info?.gps_coordinates) {
85+
return Infinity
86+
}
87+
const { latitude, longitude } = review.place_info.gps_coordinates
88+
return calculateDistance(userLocation.lat, userLocation.lon, latitude, longitude)
89+
}
90+
5391
const sortedReviews = [...reviews].sort((a, b) => {
5492
if (sortBy === 'rating') {
5593
return (b.rating || 0) - (a.rating || 0)
5694
}
95+
if (sortBy === 'distance') {
96+
return getDistance(a) - getDistance(b)
97+
}
5798
return parseDate(a.date) - parseDate(b.date)
5899
})
59100

@@ -108,13 +149,14 @@ function App() {
108149
>
109150
<option value="date">Date</option>
110151
<option value="rating">Rating</option>
152+
<option value="distance">Distance</option>
111153
</select>
112154
</div>
113155
</header>
114156

115157
<main className="reviews-grid">
116158
{paginatedReviews.map((review, index) => (
117-
<ReviewCard key={review.review_id || index} review={review} />
159+
<ReviewCard key={review.review_id || index} review={review} distance={getDistance(review)} />
118160
))}
119161
</main>
120162

google_contributors/src/components/ReviewCard.css

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,23 @@
100100
object-fit: cover;
101101
}
102102

103+
.review-footer {
104+
display: flex;
105+
justify-content: space-between;
106+
align-items: center;
107+
}
108+
103109
.review-date {
104110
margin: 0;
105111
font-size: 0.8rem;
106112
color: #888;
107-
text-align: right;
113+
}
114+
115+
.review-distance {
116+
margin: 0;
117+
font-size: 0.8rem;
118+
color: #1a73e8;
119+
font-weight: 500;
108120
}
109121

110122
.image-modal {

google_contributors/src/components/ReviewCard.jsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ function StarRating({ rating }) {
1313
return <div className="star-rating">{stars}</div>
1414
}
1515

16-
function ReviewCard({ review }) {
16+
function ReviewCard({ review, distance }) {
1717
const { place_info, snippet, rating, images, date } = review
1818
const displayImages = images?.slice(0, 2) || []
1919
const [expanded, setExpanded] = useState(false)
@@ -71,7 +71,12 @@ function ReviewCard({ review }) {
7171
</div>
7272
))}
7373
</div>
74-
<p className="review-date">{date}</p>
74+
<div className="review-footer">
75+
<p className="review-date">{date}</p>
76+
{distance !== Infinity && (
77+
<p className="review-distance">{distance.toFixed(1)} km away</p>
78+
)}
79+
</div>
7580

7681
{enlargedImage && (
7782
<div className="image-modal" onClick={() => setEnlargedImage(null)}>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import dotenv from "dotenv";
2+
import { getJson } from "serpapi";
3+
import { colorize } from "json-colorizer";
4+
5+
dotenv.config();
6+
const apiKey = process.env.API_KEY;
7+
8+
const data = await getJson({
9+
api_key: apiKey,
10+
engine: "google_maps_contributor_reviews",
11+
contributor_id: "114293782448752999823",
12+
})
13+
14+
console.log(colorize(data))

google_maps_contrib_reviews/package-lock.json

Lines changed: 51 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"name": "google_maps_contrib_reviews",
3+
"version": "1.0.0",
4+
"description": "",
5+
"main": "index.js",
6+
"scripts": {
7+
"test": "echo \"Error: no test specified\" && exit 1"
8+
},
9+
"keywords": [],
10+
"author": "",
11+
"license": "ISC",
12+
"type": "module",
13+
"dependencies": {
14+
"dotenv": "^17.2.3",
15+
"json-colorizer": "^3.0.1",
16+
"serpapi": "^2.2.1"
17+
}
18+
}

0 commit comments

Comments
 (0)