Skip to content

Commit 7d12d4f

Browse files
authored
Presentation Ready!
Presentation ready update.
2 parents 842ed7c + 92432c4 commit 7d12d4f

17 files changed

Lines changed: 420 additions & 205 deletions

File tree

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,6 @@ Docker configuration files are provided to guarantee a uniform development envir
9393
- Jack Turner
9494
- Luke Pring
9595
- Alec Thompson
96-
- Sujal Shah
9796
- Victor Tepeniuc
9897

9998
---

app/app.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ const apiRoutes = require("./models/api");
4141
function requireLogin(req, res, next) {
4242
if (!req.session.user_id) {
4343
if (req.xhr || (req.headers.accept && req.headers.accept.indexOf('json') > -1)) {
44-
return res.status(401).json({ error: "You must be logged in" });
44+
return res.status(401).json({ error: "You must be logged in" });
4545
}
4646
return res.redirect(`/?login=true&continue=${encodeURIComponent(req.originalUrl)}`);
4747
}
@@ -64,10 +64,12 @@ function testDBConnection(retries = 30) {
6464
if (retries > 0) {
6565
setTimeout(() => testDBConnection(retries - 1), 2000);
6666
} else {
67-
console.error("DB connection failed");
67+
console.error(" ❌ DB connection failed");
68+
process.exit(1);
6869
}
6970
} else {
70-
console.log("DB ready");
71+
console.log(" ✅ DB is ready");
72+
console.log(" 🍺 TapThat is running!");
7173
}
7274
});
7375
}

app/models/api.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ const router = express.Router();
33
const db = require("../services/db");
44

55
router.get("/pubs", (req, res) => {
6-
const sql = `
6+
const search = req.query.search;
7+
let sql = `
78
SELECT
89
pubs.id,
910
pubs.name,
@@ -25,7 +26,14 @@ router.get("/pubs", (req, res) => {
2526
)
2627
`;
2728

28-
db.query(sql, (err, results) => {
29+
const params = [];
30+
if (search) {
31+
sql += ` WHERE pubs.name LIKE ? OR pubs.address LIKE ?`;
32+
const searchPattern = `%${search}%`;
33+
params.push(searchPattern, searchPattern);
34+
}
35+
36+
db.query(sql, params, (err, results) => {
2937
if (err) {
3038
return res.status(500).json({ error: err });
3139
}

app/models/index.js

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,30 @@ const db = require("../services/db");
44
const bcrypt = require("bcrypt");
55

66
router.get("/", (req, res) => {
7-
res.render("index", {
8-
user_email: req.session.user_email,
9-
first_name: req.session.first_name
7+
const sql = `
8+
SELECT pubs.id, pubs.name, pubs.address, COALESCE(AVG(reviews.rating), 0) AS average_rating
9+
FROM pubs
10+
LEFT JOIN reviews ON pubs.id = reviews.pub_id
11+
GROUP BY pubs.id
12+
ORDER BY average_rating DESC, pubs.name ASC
13+
LIMIT 3
14+
`;
15+
16+
db.query(sql, (err, results) => {
17+
if (err) {
18+
console.error(err);
19+
return res.render("index", {
20+
user_email: req.session.user_email,
21+
first_name: req.session.first_name,
22+
featuredPubs: []
23+
});
24+
}
25+
26+
res.render("index", {
27+
user_email: req.session.user_email,
28+
first_name: req.session.first_name,
29+
featuredPubs: results
30+
});
1031
});
1132
});
1233

app/models/users.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ router.get("/:id", (req, res) => {
4141
JOIN pubs ON reviews.pub_id = pubs.id
4242
JOIN beers ON reviews.beer_id = beers.id
4343
WHERE reviews.user_id = ?
44+
ORDER BY reviews.created_at DESC
4445
`;
4546

4647
db.query(userQuery, [userId], (err, userResults) => {

app/views/index.pug

Lines changed: 49 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,54 @@ extends layout
22

33
block content
44

5-
main
6-
section.hero
7-
if first_name
8-
h1 Ready for your next pint, #{first_name}?
9-
else
10-
h1 Ready for your next pint?
11-
p Discover pubs, rate pints, and see what others recommend.
5+
section.hero
6+
if first_name
7+
h1 Ready for your next pint, #{first_name}?
8+
.search-container
9+
form.search-form(method="GET" action="/map")
10+
input.search-input(type="text" name="search" placeholder="Search for a pub near you..." value=search autocomplete="off")
11+
button.search-button(type="submit") #[i.fa-solid.fa-magnifying-glass]
12+
else
13+
h1(style="margin-bottom: 30px;") Discover pubs, rate pints, and see what others recommend.
14+
a(href=`/login`).btn Log in to get started
1215

13-
section
14-
h2 Quick Actions
15-
ul
16-
li
17-
a(href="/pubs") Find a pub near you
18-
li
19-
a(href="/users") Browse users and reviews
16+
main.container(style="margin-top: 40px; margin-bottom: 60px;")
17+
section.remove-styling
18+
h2(style="text-align: left; margin-bottom: 30px; font-size: 2.5em;") Featured Pubs
19+
if featuredPubs && featuredPubs.length > 0
20+
.section-row
21+
each pub in featuredPubs
22+
section.w-third.remove-styling
23+
div.beer-item(style="text-align: left; padding: 0; display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; height: 100%; box-sizing: border-box; overflow: hidden;")
24+
- var lightness1 = 35 + (pub.id * 17) % 25
25+
- var lightness2 = 20 + (pub.id * 13) % 20
26+
- var bgStyle = `background: linear-gradient(135deg, hsl(29, 60%, ${lightness1}%), hsl(29, 70%, ${lightness2}%));`
27+
div(style=`${bgStyle} width: 100%; height: 180px; display: flex; align-items: center; justify-content: center;`)
28+
i.fa-solid.fa-beer-mug-empty(style="font-size: 5em; color: rgba(255, 255, 255, 0.5);")
29+
div(style="padding: 20px; display: flex; flex-direction: column; align-items: flex-start; flex-grow: 1; width: 100%; box-sizing: border-box;")
30+
strong(style="display: block; font-size: 1.3em; margin-bottom: 10px;") #{pub.name}
31+
p(style="margin: 5px 0 10px 0; color: #555;") #[i.fa-solid.fa-location-dot(style="margin-right: 6px;")] #{pub.address}
32+
- var avgRating = parseFloat(pub.average_rating) || 0
33+
p(style="margin: 5px 0 20px 0; color: #555; font-size: 1.1em;") #{avgRating.toFixed(1)} #[i.fa-solid.fa-star(style="color: #FFCC00;")]
34+
a.view-profile-btn(href=`/pubs/${pub.id}`, style="margin-top: auto;") View Pub
35+
else
36+
p(style="text-align: left;") No featured pubs available.
2037

21-
section
22-
h2 About
23-
p TapThat helps you discover pubs, review pints, and see what others recommend. Use the map to find nearby pubs and check ratings before you go.
38+
section.remove-styling(style="margin-top: 60px;")
39+
h2(style="text-align: left; margin-bottom: 30px; font-size: 2.5em;") Quick Actions
40+
.section-row
41+
section.w-third.remove-styling
42+
div.beer-item(style="text-align: left; padding: 30px 20px;")
43+
i.fa-solid.fa-map-location-dot(style="font-size: 2.5em; color: #B87333; margin-bottom: 15px;")
44+
strong(style="display: block; font-size: 1.2em; margin-bottom: 15px;") Interactive Map
45+
a.view-profile-btn(href="/map") Explore Map
46+
section.w-third.remove-styling
47+
div.beer-item(style="text-align: left; padding: 30px 20px;")
48+
i.fa-solid.fa-beer-mug-empty(style="font-size: 2.5em; color: #B87333; margin-bottom: 15px;")
49+
strong(style="display: block; font-size: 1.2em; margin-bottom: 15px;") Browse Pubs
50+
a.view-profile-btn(href="/pubs") View Directory
51+
section.w-third.remove-styling
52+
div.beer-item(style="text-align: left; padding: 30px 20px;")
53+
i.fa-solid.fa-users(style="font-size: 2.5em; color: #B87333; margin-bottom: 15px;")
54+
strong(style="display: block; font-size: 1.2em; margin-bottom: 15px;") Community
55+
a.view-profile-btn(href="/users") View Users

app/views/layout.pug

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,9 @@ html
4141

4242
block content
4343

44-
footer
45-
p © 2026 TapThat
44+
block footer
45+
footer
46+
p © 2026 TapThat - Created by Luke Pring, Jack Turner, Alec Thompson, and Victor Tepeniuc - University of Roehampton London - #[a(href="https://github.com/jackoturner/SoftwareEngineeringModuleCoursework" target="_blank") View Project on GitHub]
4647

4748
script(src="https://cdn.jsdelivr.net/npm/particles.js@2.0.0/particles.min.js")
4849
script(src="/static/bubbles.js")
@@ -101,6 +102,7 @@ html
101102
script.
102103
function showLoginModal() {
103104
document.getElementById('loginModal').classList.add('show');
105+
setTimeout(() => window.dispatchEvent(new Event('resize')), 50);
104106
}
105107

106108
document.addEventListener('DOMContentLoaded', () => {

app/views/map.pug

Lines changed: 82 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,93 @@
11
extends layout
22

33
block content
4-
main.container
4+
style.
5+
body {
6+
display: flex;
7+
flex-direction: column;
8+
height: 100vh;
9+
margin: 0;
10+
overflow: hidden;
11+
}
12+
main {
13+
flex: 1;
14+
margin: 0 !important;
15+
padding: 0 !important;
16+
max-width: 100% !important;
17+
}
18+
.map-wrapper {
19+
height: 100% !important;
20+
width: 100%;
21+
}
22+
.map-search-overlay {
23+
position: absolute;
24+
top: 20px;
25+
left: 50%;
26+
transform: translateX(-50%);
27+
background: white;
28+
padding: 10px 20px;
29+
border-radius: 20px;
30+
box-shadow: 0 4px 15px rgba(0,0,0,0.2);
31+
z-index: 1000;
32+
display: flex;
33+
align-items: center;
34+
gap: 10px;
35+
font-weight: 600;
36+
color: #333;
37+
}
38+
.clear-search-btn {
39+
color: #dc3545;
40+
text-decoration: none;
41+
font-weight: bold;
42+
font-size: 1.1em;
43+
}
44+
.map-search-form {
45+
position: absolute;
46+
top: 20px;
47+
left: 20px;
48+
background: white;
49+
padding: 5px 15px;
50+
border-radius: 20px;
51+
box-shadow: 0 4px 15px rgba(0,0,0,0.2);
52+
z-index: 1000;
53+
display: flex;
54+
align-items: center;
55+
gap: 5px;
56+
}
57+
.map-search-form input {
58+
border: none;
59+
outline: none;
60+
font-size: 16px;
61+
background: transparent;
62+
width: 200px;
63+
padding: 5px 0;
64+
}
65+
.map-search-form button {
66+
background: none;
67+
border: none;
68+
cursor: pointer;
69+
font-size: 18px;
70+
}
71+
main
572
.map-wrapper
673
#map
74+
75+
form(action="/map" method="GET").map-search-form
76+
input#mapSearchInput(type="text" name="search" placeholder="Search...")
77+
button(type="submit") #[i.fa-solid.fa-magnifying-glass]
778

8-
button#addReviewBtn.map-btn.add-btn
9-
button#directionsBtn.map-btn.dir-btn
79+
#searchOverlay.map-search-overlay(style="display: none;")
80+
span#searchOverlayText
81+
a(href="/map" class="clear-search-btn")
82+
83+
button#addReviewBtn.map-btn.add-btn #[i.fa-solid.fa-plus]
84+
button#directionsBtn.map-btn.dir-btn #[i.fa-solid.fa-location-arrow]
1085

1186
link(rel="stylesheet", href="https://unpkg.com/leaflet/dist/leaflet.css")
1287
script(src="https://unpkg.com/leaflet/dist/leaflet.js")
1388
script(src="https://unpkg.com/leaflet-routing-machine/dist/leaflet-routing-machine.js")
1489
link(rel="stylesheet", href="https://unpkg.com/leaflet-routing-machine/dist/leaflet-routing-machine.css")
15-
script(src="/static/maps.js")
90+
script(src="/static/maps.js")
91+
92+
block footer
93+
// Footer omitted on this page

app/views/pub.pug

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ block content
66
p(style="margin-bottom: 30px;")
77
img(src="/static/images/pin.png", alt="Location", width="24", height="24", style="vertical-align: middle; margin-right: 10px;")
88
| #{pub.address}
9-
a(href="/map").btn Get Directions
9+
a(href=`/map?pub_id=${pub.id}`).btn Get Directions
1010
main.container
1111

1212
.section-row

app/views/user.pug

Lines changed: 47 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,52 @@
11
extends layout
22

33
block content
4-
.container
5-
.profile-card
6-
.profile-avatar
7-
.avatar-large #{user.first_name.charAt(0)}#{user.last_name.charAt(0)}
8-
.profile-details
9-
h1 #{user.first_name} #{user.last_name}
10-
p.email #{user.email}
11-
- var joinDate = user.created_at ? new Date(user.created_at).toDateString() : 'Unknown'
12-
p.joined Joined: #{joinDate}
13-
p.stats Total reviews: #{reviews.length}
4+
section.hero.pub-hero
5+
.container(style="display: flex; align-items: center; justify-content: flex-start; gap: 20px;")
6+
.avatar-large(style="box-shadow: 0 4px 10px rgba(0,0,0,0.3);") #{user.first_name.charAt(0)}#{user.last_name.charAt(0)}
7+
div(style="text-align: left;")
8+
h1(style="margin: 0; text-shadow: none;") #{user.first_name} #{user.last_name}
9+
p(style="margin: 10px 0 0 0;")
10+
- var joinDate = user.created_at ? new Date(user.created_at).toDateString() : 'Unknown'
11+
if user.id == user_id
12+
| #[i.fa-solid.fa-envelope(style="margin-right: 8px;")] #{user.email}
1413

15-
h2.reviews-title Reviews by #{user.first_name}
16-
.reviews-grid
17-
each review in reviews
18-
.review-card
19-
.review-header
20-
span.pub-name 📍 #{review.pub_name}
21-
span.beer-name 🍺 #{review.beer_name}
22-
.review-rating
23-
- var rating = parseInt(review.rating) || 0
24-
- for (var i = 1; i <= 5; i++)
25-
if i <= rating
26-
span.star-filled
27-
else
28-
span.star-empty
29-
span.rating-value (#{rating}/5)
30-
if review.ai_pour_score
31-
- var aiScore = parseFloat(review.ai_pour_score) || 0
32-
.ai-score AI Pour Score: #{aiScore.toFixed(1)}/5
33-
.review-comment "#{review.comment}"
34-
- var reviewDate = review.created_at ? new Date(review.created_at).toLocaleDateString() : 'Unknown'
35-
.review-date #{reviewDate}
14+
main.container
15+
.section-row
16+
section.w-two-thirds.remove-styling
17+
h2(style="margin-top: 0;") Reviews by #{user.first_name}
18+
.section-row
19+
each review in reviews
20+
section.w-third
21+
.review-header(style="margin-bottom: 8px;")
22+
p(style="margin: 0; font-weight: bold;") #[i.fa-solid.fa-location-dot(style="margin-right: 6px;")] #{review.pub_name}
23+
.review-rating(style="margin-bottom: 8px;")
24+
- var rating = parseInt(review.rating) || 0
25+
- for (var i = 1; i <= 5; i++)
26+
if i <= rating
27+
span.star-filled(style="color: #FFCC00;") #[i.fa-solid.fa-star]
28+
else
29+
span.star-empty(style="color: #ccc;") #[i.fa-regular.fa-star]
30+
span.rating-value(style="margin-left: 5px; font-size: 0.9em;") (#{rating}/5)
31+
p(style="margin: 0 0 8px 0; color: #555;") #[i.fa-solid.fa-beer-mug-empty(style="margin-right: 4px;")] #{review.beer_name}
32+
if review.ai_pour_score
33+
- var aiScore = parseFloat(review.ai_pour_score) || 0
34+
p(style="margin: 0 0 8px 0; font-size: 0.9em; color: #777;") AI Pour Score: #{aiScore.toFixed(1)}/5
35+
p(style="margin: 0 0 10px 0; font-style: italic;") "#{review.comment}"
36+
- var reviewDate = review.created_at ? new Date(review.created_at).toLocaleDateString() : 'Unknown'
37+
p(style="margin: 0; font-size: 0.8em; color: #999; text-align: right;") #{reviewDate}
3638

37-
if reviews.length === 0
38-
p.no-reviews No reviews yet.
39+
if reviews.length === 0
40+
p.no-reviews(style="width: 100%;") No reviews yet.
41+
42+
section.w-third.remove-styling(style="padding-left: 30px;")
43+
h2(style="margin-top: 0;") User Stats
44+
div.beer-item(style="margin-bottom: 15px; text-align: center;")
45+
strong Total Reviews
46+
p(style="margin: 5px 0 0 0; font-size: 1.5em; color: #B87333;") #{reviews.length}
47+
div.beer-item(style="margin-bottom: 15px; text-align: center;")
48+
strong Average rating
49+
p(style="margin: 5px 0 0 0; font-size: 1.5em; color: #B87333;") #{reviews.length > 0 ? (reviews.reduce((acc, review) => acc + review.rating, 0) / reviews.length).toFixed(1) : 0}
50+
div.beer-item(style="margin-bottom: 15px; text-align: center;")
51+
strong Date joined
52+
p(style="margin: 5px 0 0 0; font-size: 1.5em; color: #B87333;") #{joinDate}

0 commit comments

Comments
 (0)