human (Feb 15, 2025, 06:54 PM)
I want to add a sidebar of the 5 most recent reviews. Clicking on a place should show the corresponding popup on the map.
Here's my HTML
paste.txt
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Boba Quest 2025 🧋</title>
<link rel="stylesheet" href="static/leaflet-1.9.4.css" />
<style>
body {
margin: 0;
padding: 20px;
font-family: sans-serif;
}
.header {
text-align: center;
margin-bottom: 20px;
}
.header h1 {
font-size: 2.5rem;
margin: 0 0 10px 0;
}
.nav-links {
margin-bottom: 20px;
}
.nav-links a {
color: #2B6CB0;
cursor: pointer;
text-decoration: none;
margin: 0 15px;
font-size: 1.2rem;
}
.nav-links a:hover {
text-decoration: underline;
}
.modal-overlay {
display: none;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
z-index: 1000;
}
.modal {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: white;
padding: 30px;
border-radius: 8px;
max-width: 500px;
width: 90%;
z-index: 1001;
}
.modal h2 {
margin-top: 0;
font-size: 1.8rem;
color: #2B6CB0;
}
.modal p {
line-height: 1.6;
font-size: 1.1rem;
}
.close-modal {
position: absolute;
top: 15px;
right: 15px;
font-size: 24px;
cursor: pointer;
color: #666;
border: none;
background: none;
padding: 0;
}
.close-modal:hover {
color: #333;
}
.container {
max-width: 80%;
margin: 0 auto;
}
#map {
height: 80vh;
width: 100%;
border-radius: 8px;
border: 2px solid #ddd;
}
.popup-content {
padding: 12px;
min-width: 200px;
font-size: 1.5rem;
}
.popup-content h3 {
text-align: center;
margin-bottom: 12px;
margin-top: 0;
font-size: 1.8rem;
}
.ratings-table {
width: 100%;
border-spacing: 0;
margin-bottom: 12px;
border-collapse: collapse;
}
.ratings-table tr td {
padding: 4px 2px;
}
.ratings-table tr td:first-child {
padding-right: 4px;
}
.ratings-table tr td:last-child {
text-align: right;
}
.total-row {
font-weight: bold;
border-top: 1px solid #ddd;
margin-top: 4px;
}
.review-link-container {
text-align: center;
}
.review-link {
display: inline-block;
padding: 8px 20px;
color: #2B6CB0;
text-decoration: none;
border-radius: 4px;
font-size: 1.3rem;
border: 2px solid #2B6CB0;
transition: all 0.2s ease;
}
.review-link:hover {
background-color: #2B6CB0;
color: white;
}
.emoji-marker {
font-size: 2rem;
text-align: center;
line-height: 1;
}
.leaflet-marker-icon {
filter: none !important;
}
/* Make the popup wider to accommodate larger text */
.leaflet-popup-content {
margin: 8px;
}
.subway {
height: 1.5em;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>Boba Quest 2025 🧋</h1>
<div class="nav-links">
<a href="https://blog.legoktm.com/2025/01/07/boba-quest-2025-.html">About</a>
<a href="https://blog.legoktm.com/category/bobaquest.html">Reviews</a>
<a class="modal-trigger" data-modal="subscribe">Subscribe</a>
<a class="modal-trigger" data-modal="tips">Tips</a>
</div>
</div>
<div id="map"></div>
</div>
<div id="subscribe-modal" class="modal-overlay">
<div class="modal">
<button class="close-modal" data-modal="subscribe">×</button>
<h2>Subscribe</h2>
<p>
You can subscribe to Boba Quest 2025 🧋 by using <a href="https://en.wikipedia.org/wiki/RSS">RSS</a>.
You'll need a feed reader, if you don't have one yet, look at some recommendations from
<a href="https://opensource.com/article/17/3/rss-feed-readers">opensource.com</a>, <a
href="https://www.theverge.com/24036427/rss-feed-reader-best">The Verge</a>,
and <a href="https://lifehacker.com/tech/best-rss-readers">LifeHacker</a>.
</p>
<p>Use the URL <kbd>https://blog.legoktm.com/feeds/bobaquest.atom.xml</kbd> in your feed reader to
subscribe.</p>
</div>
</div>
<div id="tips-modal" class="modal-overlay">
<div class="modal">
<button class="close-modal" data-modal="tips">×</button>
<h2>Tips</h2>
<p>
I am interested in suggestions for more boba shops to review, as well as any other feedback.
I can be reached securely using <a href="https://signal.org/">Signal</a>; my username is
<kbd>@legoktm.12345</kbd>
</p>
</div>
</div>
<script src="static/leaflet-1.9.4.js"></script>
<script>
// Initialize the map centered on a specific location (this example uses New York City)
const map = L.map('map').setView([40.7128, -74.0060], 12);
map.attributionControl.setPrefix('<a href="https://leafletjs.com/">Leaflet</a>');
const divIcon = L.divIcon({
html: '🧋',
className: 'emoji-marker',
iconSize: [32, 32],
iconAnchor: [16, 32],
popupAnchor: [0, -32]
});
// Add the OpenStreetMap tiles
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: '© OpenStreetMap contributors'
}).addTo(map);
const shops = [{ "name": "I'Milky", "lat": "40.745819", "lng": "-73.946220", "boba": 3, "tea": 4, "bonus": 0, "reviewLink": "/2025/01/12/boba-quest-imilky.html", "trains": ["7", "E", "M", "G"] }, { "name": "Bobba Hubba", "lat": "40.746248", "lng": "-73.890490", "boba": 2, "tea": 4, "bonus": 0, "reviewLink": "/2025/01/15/boba-quest-bobba-hubba.html", "trains": ["7", "E", "F", "M", "R"] }, { "name": "Chun Yang Tea", "lat": "40.743176", "lng": "-73.954315", "boba": 1, "tea": 2, "bonus": 0, "reviewLink": "/2025/01/22/boba-quest-chun-yang-tea.html", "trains": ["7"] }, { "name": "M\u00f6ge Tea", "lat": "40.749738", "lng": "-73.941435", "boba": 3, "tea": 3, "bonus": 0, "reviewLink": "/2025/01/29/boba-quest-m\u00f6ge-tea.html", "trains": ["7"] }, { "name": "AKIHI Bubble Tea", "lat": "40.741877", "lng": "-74.173377", "boba": 3, "tea": 2, "bonus": 1, "reviewLink": "/2025/02/05/boba-quest-akihi-bubble-tea.html", "trains": ["PATH", "NJ Transit", "Amtrak"] }, { "name": "D\u00e9butea", "lat": "40.729175", "lng": "-73.9993244", "boba": 4, "tea": 4, "bonus": 1, "reviewLink": "/2025/02/12/boba-quest-d\u00e9butea.html", "trains": ["A", "C", "E", "B", "D", "F", "M"] }];
// Add markers for each shop
shops.forEach(shop => {
const marker = L.marker([shop.lat, shop.lng], { icon: divIcon }).addTo(map);
let trains = "";
shop.trains.forEach(train => {
trains += `<img class="subway" src="icons/${train}.svg" alt="${train} train" title="${train} train">`;
})
// Create popup content
const popupContent = `
<div class="popup-content">
<h3>${shop.name}</h3>
<table class="ratings-table">
<tr>
<td>Boba:</td>
<td>${shop.boba}/4</td>
</tr>
<tr>
<td>Tea:</td>
<td>${shop.tea}/4</td>
</tr>
<tr>
<td>Bonus:</td>
<td>${shop.bonus}/1</td>
</tr>
<tr class="total-row">
<td>Total:</td>
<td>${shop.boba + shop.tea + shop.bonus}/10</td>
</tr>
</table>
<div class="review-link-container">
<a href="https://blog.legoktm.com${shop.reviewLink}" class="review-link" target="_blank">Full Review</a>
<div>${trains}</div>
</div>
</div>
`;
// Bind popup to marker
marker.bindPopup(popupContent);
});
document.addEventListener('DOMContentLoaded', () => {
// Modal handlers
const openModal = (type) => {
document.getElementById(`${type}-modal`).style.display = 'block';
document.body.style.overflow = 'hidden';
};
const closeModal = (type) => {
document.getElementById(`${type}-modal`).style.display = 'none';
document.body.style.overflow = 'auto';
};
// Add click listeners for modal triggers
document.querySelectorAll('.modal-trigger').forEach(trigger => {
trigger.addEventListener('click', (e) => {
const modalType = e.target.dataset.modal;
openModal(modalType);
});
});
// Add click listeners for close buttons
document.querySelectorAll('.close-modal').forEach(button => {
button.addEventListener('click', (e) => {
const modalType = e.target.dataset.modal;
closeModal(modalType);
});
});
// Close modal when clicking overlay
document.querySelectorAll('.modal-overlay').forEach(overlay => {
overlay.addEventListener('click', (e) => {
if (e.target === e.currentTarget) {
closeModal(e.target.id.replace('-modal', ''));
}
});
});
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
closeModal("tips");
closeModal("subscribe");
}
});
});
</script>
</body>
</html>
assistant (Feb 15, 2025, 06:54 PM)
I'll help you create a sidebar that shows the 5 most recent reviews and interacts with the map. We'll need to modify your HTML and CSS, and add some JavaScript to handle the interactions.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Boba Quest 2025 🧋</title>
<link rel="stylesheet" href="static/leaflet-1.9.4.css" />
<style>
/* Existing styles */
body {
margin: 0;
padding: 20px;
font-family: sans-serif;
}
.header {
text-align: center;
margin-bottom: 20px;
}
.header h1 {
font-size: 2.5rem;
margin: 0 0 10px 0;
}
/* New layout styles */
.main-content {
display: flex;
gap: 20px;
max-width: 95%;
margin: 0 auto;
}
#map {
flex: 1;
height: 80vh;
border-radius: 8px;
border: 2px solid #ddd;
}
.sidebar {
width: 300px;
background: #f8f9fa;
padding: 15px;
border-radius: 8px;
border: 2px solid #ddd;
}
.sidebar h2 {
margin-top: 0;
color: #2B6CB0;
font-size: 1.5rem;
margin-bottom: 15px;
}
.recent-reviews {
display: flex;
flex-direction: column;
gap: 12px;
}
.review-card {
background: white;
padding: 12px;
border-radius: 6px;
border: 1px solid #ddd;
cursor: pointer;
transition: all 0.2s ease;
}
.review-card:hover {
background: #f0f4f8;
transform: translateX(2px);
}
.review-card h3 {
margin: 0 0 8px 0;
color: #2B6CB0;
font-size: 1.2rem;
}
.review-card .rating {
font-weight: bold;
margin-bottom: 8px;
}
.review-card .trains {
margin-top: 8px;
}
.subway {
height: 1.5em;
}
/* Existing modal styles remain the same */
/* ... */
</style>
</head>
<body>
<div class="header">
<h1>Boba Quest 2025 🧋</h1>
<div class="nav-links">
<a href="https://blog.legoktm.com/2025/01/07/boba-quest-2025-.html">About</a>
<a href="https://blog.legoktm.com/category/bobaquest.html">Reviews</a>
<a class="modal-trigger" data-modal="subscribe">Subscribe</a>
<a class="modal-trigger" data-modal="tips">Tips</a>
</div>
</div>
<div class="main-content">
<div id="map"></div>
<div class="sidebar">
<h2>Recent Reviews</h2>
<div class="recent-reviews" id="recent-reviews">
<!-- Review cards will be populated by JavaScript -->
</div>
</div>
</div>
<!-- Existing modals remain the same -->
<!-- ... -->
<script src="static/leaflet-1.9.4.js"></script>
<script>
// Initialize the map
const map = L.map('map').setView([40.7128, -74.0060], 12);
map.attributionControl.setPrefix('<a href="https://leafletjs.com/">Leaflet</a>');
const divIcon = L.divIcon({
html: '🧋',
className: 'emoji-marker',
iconSize: [32, 32],
iconAnchor: [16, 32],
popupAnchor: [0, -32]
});
// Add the OpenStreetMap tiles
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: '© OpenStreetMap contributors'
}).addTo(map);
const shops = [/* Your existing shops array */];
const markers = new Map(); // Store markers by shop name
// Add markers for each shop
shops.forEach(shop => {
const marker = L.marker([shop.lat, shop.lng], { icon: divIcon }).addTo(map);
markers.set(shop.name, marker); // Store marker reference
let trains = "";
shop.trains.forEach(train => {
trains += `<img class="subway" src="icons/${train}.svg" alt="${train} train" title="${train} train">`;
});
// Create popup content
const popupContent = `
<div class="popup-content">
<h3>${shop.name}</h3>
<table class="ratings-table">
<tr>
<td>Boba:</td>
<td>${shop.boba}/4</td>
</tr>
<tr>
<td>Tea:</td>
<td>${shop.tea}/4</td>
</tr>
<tr>
<td>Bonus:</td>
<td>${shop.bonus}/1</td>
</tr>
<tr class="total-row">
<td>Total:</td>
<td>${shop.boba + shop.tea + shop.bonus}/10</td>
</tr>
</table>
<div class="review-link-container">
<a href="https://blog.legoktm.com${shop.reviewLink}" class="review-link" target="_blank">Full Review</a>
<div>${trains}</div>
</div>
</div>
`;
marker.bindPopup(popupContent);
});
// Populate recent reviews
function populateRecentReviews() {
const recentReviews = document.getElementById('recent-reviews');
// Sort shops by review link date (newest first)
const sortedShops = [...shops].sort((a, b) => b.reviewLink.localeCompare(a.reviewLink));
// Take only the 5 most recent
const recentShops = sortedShops.slice(0, 5);
recentShops.forEach(shop => {
let trains = "";
shop.trains.forEach(train => {
trains += `<img class="subway" src="icons/${train}.svg" alt="${train} train" title="${train} train">`;
});
const reviewCard = document.createElement('div');
reviewCard.className = 'review-card';
reviewCard.innerHTML = `
<h3>${shop.name}</h3>
<div class="rating">Rating: ${shop.boba + shop.tea + shop.bonus}/10</div>
<div class="trains">${trains}</div>
`;
// Add click handler to show popup
reviewCard.addEventListener('click', () => {
const marker = markers.get(shop.name);
if (marker) {
marker.openPopup();
map.setView([shop.lat, shop.lng], 15);
}
});
recentReviews.appendChild(reviewCard);
});
}
// Initialize everything when DOM is loaded
document.addEventListener('DOMContentLoaded', () => {
populateRecentReviews();
// Your existing modal handlers remain the same
// ...
});
</script>
</body>
</html>I've made the following changes to your code:
- Added a sidebar layout using flexbox
- Created a new "Recent Reviews" section that shows the 5 most recent reviews based on the review link dates
- Added interactive review cards that show:
- Shop name
- Total rating
- Available trains
- Made the cards clickable - clicking a card will:
- Center the map on that location
- Open the corresponding marker popup
- Zoom in to level 15
The sidebar has a clean, minimal design that matches your existing aesthetic. The review cards have a subtle hover effect and clear visual hierarchy.
Would you like me to modify anything about the design or functionality? For example, we could:
- Add more details to the review cards
- Change the styling or layout
- Adjust the interaction behavior
human (Feb 15, 2025, 07:03 PM)
Don't omit any code so I can see the full page with your modifications
assistant (Feb 15, 2025, 07:04 PM)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Boba Quest 2025 🧋</title>
<link rel="stylesheet" href="static/leaflet-1.9.4.css" />
<style>
body {
margin: 0;
padding: 20px;
font-family: sans-serif;
}
.header {
text-align: center;
margin-bottom: 20px;
}
.header h1 {
font-size: 2.5rem;
margin: 0 0 10px 0;
}
.nav-links {
margin-bottom: 20px;
}
.nav-links a {
color: #2B6CB0;
cursor: pointer;
text-decoration: none;
margin: 0 15px;
font-size: 1.2rem;
}
.nav-links a:hover {
text-decoration: underline;
}
.modal-overlay {
display: none;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
z-index: 1000;
}
.modal {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: white;
padding: 30px;
border-radius: 8px;
max-width: 500px;
width: 90%;
z-index: 1001;
}
.modal h2 {
margin-top: 0;
font-size: 1.8rem;
color: #2B6CB0;
}
.modal p {
line-height: 1.6;
font-size: 1.1rem;
}
.close-modal {
position: absolute;
top: 15px;
right: 15px;
font-size: 24px;
cursor: pointer;
color: #666;
border: none;
background: none;
padding: 0;
}
.close-modal:hover {
color: #333;
}
/* New layout styles */
.main-content {
display: flex;
gap: 20px;
max-width: 95%;
margin: 0 auto;
}
#map {
flex: 1;
height: 80vh;
border-radius: 8px;
border: 2px solid #ddd;
}
.sidebar {
width: 300px;
background: #f8f9fa;
padding: 15px;
border-radius: 8px;
border: 2px solid #ddd;
}
.sidebar h2 {
margin-top: 0;
color: #2B6CB0;
font-size: 1.5rem;
margin-bottom: 15px;
}
.recent-reviews {
display: flex;
flex-direction: column;
gap: 12px;
}
.review-card {
background: white;
padding: 12px;
border-radius: 6px;
border: 1px solid #ddd;
cursor: pointer;
transition: all 0.2s ease;
}
.review-card:hover {
background: #f0f4f8;
transform: translateX(2px);
}
.review-card h3 {
margin: 0 0 8px 0;
color: #2B6CB0;
font-size: 1.2rem;
}
.review-card .rating {
font-weight: bold;
margin-bottom: 8px;
}
.review-card .trains {
margin-top: 8px;
}
.popup-content {
padding: 12px;
min-width: 200px;
font-size: 1.5rem;
}
.popup-content h3 {
text-align: center;
margin-bottom: 12px;
margin-top: 0;
font-size: 1.8rem;
}
.ratings-table {
width: 100%;
border-spacing: 0;
margin-bottom: 12px;
border-collapse: collapse;
}
.ratings-table tr td {
padding: 4px 2px;
}
.ratings-table tr td:first-child {
padding-right: 4px;
}
.ratings-table tr td:last-child {
text-align: right;
}
.total-row {
font-weight: bold;
border-top: 1px solid #ddd;
margin-top: 4px;
}
.review-link-container {
text-align: center;
}
.review-link {
display: inline-block;
padding: 8px 20px;
color: #2B6CB0;
text-decoration: none;
border-radius: 4px;
font-size: 1.3rem;
border: 2px solid #2B6CB0;
transition: all 0.2s ease;
}
.review-link:hover {
background-color: #2B6CB0;
color: white;
}
.emoji-marker {
font-size: 2rem;
text-align: center;
line-height: 1;
}
.leaflet-marker-icon {
filter: none !important;
}
.leaflet-popup-content {
margin: 8px;
}
.subway {
height: 1.5em;
}
</style>
</head>
<body>
<div class="header">
<h1>Boba Quest 2025 🧋</h1>
<div class="nav-links">
<a href="https://blog.legoktm.com/2025/01/07/boba-quest-2025-.html">About</a>
<a href="https://blog.legoktm.com/category/bobaquest.html">Reviews</a>
<a class="modal-trigger" data-modal="subscribe">Subscribe</a>
<a class="modal-trigger" data-modal="tips">Tips</a>
</div>
</div>
<div class="main-content">
<div id="map"></div>
<div class="sidebar">
<h2>Recent Reviews</h2>
<div class="recent-reviews" id="recent-reviews">
<!-- Review cards will be populated by JavaScript -->
</div>
</div>
</div>
<div id="subscribe-modal" class="modal-overlay">
<div class="modal">
<button class="close-modal" data-modal="subscribe">×</button>
<h2>Subscribe</h2>
<p>
You can subscribe to Boba Quest 2025 🧋 by using <a href="https://en.wikipedia.org/wiki/RSS">RSS</a>.
You'll need a feed reader, if you don't have one yet, look at some recommendations from
<a href="https://opensource.com/article/17/3/rss-feed-readers">opensource.com</a>, <a
href="https://www.theverge.com/24036427/rss-feed-reader-best">The Verge</a>,
and <a href="https://lifehacker.com/tech/best-rss-readers">LifeHacker</a>.
</p>
<p>Use the URL <kbd>https://blog.legoktm.com/feeds/bobaquest.atom.xml</kbd> in your feed reader to
subscribe.</p>
</div>
</div>
<div id="tips-modal" class="modal-overlay">
<div class="modal">
<button class="close-modal" data-modal="tips">×</button>
<h2>Tips</h2>
<p>
I am interested in suggestions for more boba shops to review, as well as any other feedback.
I can be reached securely using <a href="https://signal.org/">Signal</a>; my username is
<kbd>@legoktm.12345</kbd>
</p>
</div>
</div>
<script src="static/leaflet-1.9.4.js"></script>
<script>
// Initialize the map
const map = L.map('map').setView([40.7128, -74.0060], 12);
map.attributionControl.setPrefix('<a href="https://leafletjs.com/">Leaflet</a>');
const divIcon = L.divIcon({
html: '🧋',
className: 'emoji-marker',
iconSize: [32, 32],
iconAnchor: [16, 32],
popupAnchor: [0, -32]
});
// Add the OpenStreetMap tiles
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: '© OpenStreetMap contributors'
}).addTo(map);
const shops = [
{
"name": "I'Milky",
"lat": "40.745819",
"lng": "-73.946220",
"boba": 3,
"tea": 4,
"bonus": 0,
"reviewLink": "/2025/01/12/boba-quest-imilky.html",
"trains": ["7", "E", "M", "G"]
},
{
"name": "Bobba Hubba",
"lat": "40.746248",
"lng": "-73.890490",
"boba": 2,
"tea": 4,
"bonus": 0,
"reviewLink": "/2025/01/15/boba-quest-bobba-hubba.html",
"trains": ["7", "E", "F", "M", "R"]
},
{
"name": "Chun Yang Tea",
"lat": "40.743176",
"lng": "-73.954315",
"boba": 1,
"tea": 2,
"bonus": 0,
"reviewLink": "/2025/01/22/boba-quest-chun-yang-tea.html",
"trains": ["7"]
},
{
"name": "Möge Tea",
"lat": "40.749738",
"lng": "-73.941435",
"boba": 3,
"tea": 3,
"bonus": 0,
"reviewLink": "/2025/01/29/boba-quest-möge-tea.html",
"trains": ["7"]
},
{
"name": "AKIHI Bubble Tea",
"lat": "40.741877",
"lng": "-74.173377",
"boba": 3,
"tea": 2,
"bonus": 1,
"reviewLink": "/2025/02/05/boba-quest-akihi-bubble-tea.html",
"trains": ["PATH", "NJ Transit", "Amtrak"]
},
{
"name": "Débutea",
"lat": "40.729175",
"lng": "-73.9993244",
"boba": 4,
"tea": 4,
"bonus": 1,
"reviewLink": "/2025/02/12/boba-quest-débutea.html",
"trains": ["A", "C", "E", "B", "D", "F", "M"]
}
];
const markers = new Map(); // Store markers by shop name
// Add markers for each shop
shops.forEach(shop => {
const marker = L.marker([shop.lat, shop.lng], { icon: divIcon }).addTo(map);
markers.set(shop.name, marker); // Store marker reference
let trains = "";
shop.trains.forEach(train => {
trains += `<img class="subway" src="icons/${train}.svg" alt="${train} train" title="${train} train">`;
});
// Create popup content
const popupContent = `
<div class="popup-content">
<h3>${shop.name}</h3>
<table class="ratings-table">
<tr>
<td>Boba:</td>
<td>${shop.boba}/4</td>
</tr>
<tr>
<td>Tea:</td>
<td>${shop.tea}/4</td>
</tr>
<tr>
<td>Bonus:</td>
<td>${shop.bonus}/1</td>
</tr>
<tr class="total-row">
<td>Total:</td>
<td>${shop.boba + shop.tea + shop.bonus}/10</td>
</tr>
</table>
<div class="review-link-container">
<a href="https://blog.legoktm.com${shop.reviewLink}" class="review-link" target="_blank">Full Review</a>
<div>${trains}</div>
</div>
</div>
`;
marker.bindPopup(popupContent);
});
// Populate recent reviews
function populateRecentReviews() {
const recentReviews = document.getElementById('recent-reviews');
// Sort shops by review link date (newest first)
const sortedShops = [...shops].sort((a, b) => b.reviewLink.localeCompare(a.reviewLink));
// Take only the 5 most recent
const recentShops = sortedShops.slice(0, 5);
recentShops.forEach(shop => {
let trains = "";
shop.trains.forEach(train => {
trains += `<img class="subway" src="icons/${train}.svg" alt="${train} train" title="${train} train">`;
});
const reviewCard = document.createElement('div');
reviewCard.className = 'review-card';
reviewCard.innerHTML = `
<h3>${shop.name}</h3>
<div class="rating">Rating: ${shop.boba + shop.tea + shop.bonus}/10</div>
<div class="trains">${trains}</div>
`;
// Add click handler to show popup
reviewCard.addEventListener('click', () => {
const marker = markers.get(shop.name);
if (marker) {
marker.openPopup();
map.setView([shop.lat, shop.lng], 15);
}
});
recentReviews.appendChild(reviewCard);
});
}
// Initialize everything when DOM is loaded
document.addEventListener('DOMContentLoaded', () => {
populateRecentReviews();
// Modal handlers
const openModal = (type)