Repository: remla25-team21/app
Pull request #12: Update frontend for monitoring
Author: login: raghavtalwar7
Created: 2025-05-15T20:03:18Z | Merged: 2025-05-16T18:08:37Z

Added frontend changes so that monitoring can be done


Diff:
diff --git a/README.md b/README.md
index 01bcb18..cf87a3b 100644
--- a/README.md
+++ b/README.md
@@ -37,7 +37,7 @@ npm install -g serve
 3. **Serve the static files**
 
 ```bash
-serve -s . -l 3000
+serve . -l 3000
 ```
 
 4. **Access the frontend**
diff --git a/app-frontend/Dockerfile b/app-frontend/Dockerfile
index b900b35..98ae518 100644
--- a/app-frontend/Dockerfile
+++ b/app-frontend/Dockerfile
@@ -14,4 +14,4 @@ RUN npm install -g serve
 EXPOSE 3000
 
 # Use 'serve' to serve the static files from the current directory
-CMD ["serve", "-s", ".", "-l", "3000"]
+CMD ["serve", ".", "-l", "3000"]
diff --git a/app-frontend/index.html b/app-frontend/index.html
index 20ded69..1c80ac4 100644
--- a/app-frontend/index.html
+++ b/app-frontend/index.html
@@ -3,42 +3,39 @@
 
 <head>
     <meta charset="UTF-8" />
-    <title>Restaurant Sentiment</title>
+    <title>Restaurant Review Portal</title>
     <link rel="stylesheet" href="styles.css">
 </head>
 
 <body>
     <div class="container">
-        <h1>Restaurant Sentiment</h1>
-        <h2>Work In Progress</h2>
+        <h1>Restaurant Review Portal</h1>
+        <h2>Choose a restaurant to review</h2>
 
-        <div class="restaurant-name">
-            <p><strong>🍕 Pizza Planet</strong></p>
-        </div>
-
-        <div class="review-card">
-            <p><strong>Enter your review:</strong></p>
-            <form onsubmit="event.preventDefault(); send_review();">
-                <textarea id="review" name="review" rows="3"
-                    placeholder="Write your thoughts about Pizza Planet..."></textarea>
-                <button type="submit">Send Review</button>
-            </form>
-            <div id="prediction-box" class="prediction-box" style="display: none;">
-                This review is: <span id="prediction"></span>
+        <div class="restaurant-selection">
+            <div class="restaurant-card" onclick="selectRestaurant('McDonald\'s')">
+                <div class="restaurant-logo">🍔</div>
+                <h3>McDonald's</h3>
+            </div>
+            
+            <div class="restaurant-card" onclick="selectRestaurant('KFC')">
+                <div class="restaurant-logo">🍗</div>
+                <h3>KFC</h3>
+            </div>
+            
+            <div class="restaurant-card" onclick="selectRestaurant('Burger King')">
+                <div class="restaurant-logo">👑</div>
+                <h3>Burger King</h3>
+            </div>
+            
+            <div class="restaurant-card" onclick="selectRestaurant('Wendy\'s')">
+                <div class="restaurant-logo">🥪</div>
+                <h3>Wendy's</h3>
             </div>
         </div>
-
-        <div class="review-card">
-            <p><strong>User1:</strong> ☹️ This place is amazing!</p>
-        </div>
-        <div class="review-card">
-            <p><strong>User2:</strong> 😄 Worst restaurant in town!</p>
-        </div>
-
     </div>
 
-    <script src="predict.js"></script>
-
+    <script src="navigation.js"></script>
 </body>
 
 </html>
\ No newline at end of file
diff --git a/app-frontend/navigation.js b/app-frontend/navigation.js
new file mode 100644
index 0000000..59504ea
--- /dev/null
+++ b/app-frontend/navigation.js
@@ -0,0 +1,7 @@
+function selectRestaurant(restaurantName) {
+    // Store the selected restaurant name
+    localStorage.setItem('selectedRestaurant', restaurantName);
+
+    // Redirect to the review page
+    window.location.href = 'review.html';
+}
\ No newline at end of file
diff --git a/app-frontend/predict.js b/app-frontend/predict.js
index 26e27a1..545ed33 100644
--- a/app-frontend/predict.js
+++ b/app-frontend/predict.js
@@ -17,10 +17,14 @@ async function send_review() {
     // This works in both development and production environments
     const apiUrl =
       window.location.hostname === "localhost" ||
-      window.location.hostname === "127.0.0.1"
+        window.location.hostname === "127.0.0.1"
         ? "http://localhost:5000/predict" // For local development
         : "http://app-service:5000/predict"; // For docker network
 
+    // Show loading state
+    predictionBox.style.display = "block";
+    predictionSpan.textContent = "Analyzing...";
+
     const response = await fetch(apiUrl, {
       method: "POST",
       headers: {
@@ -47,6 +51,9 @@ async function send_review() {
       } else {
         predictionBox.className = "prediction-box"; // Default style if result is not 0 or 1
       }
+
+      // Show feedback options after getting prediction
+      showFeedbackOptions();
     } else {
       throw new Error(result.error || "Unknown prediction error");
     }
@@ -57,3 +64,161 @@ async function send_review() {
     predictionSpan.textContent = "Error: " + error.message;
   }
 }
+
+// Get the selected restaurant from localStorage
+const selectedRestaurant = localStorage.getItem('selectedRestaurant') || 'Pizza Planet';
+
+// Update the restaurant name display
+document.getElementById('restaurant-name-display').innerHTML = `🍽️ ${selectedRestaurant}`;
+document.title = `${selectedRestaurant} - Sentiment Review`;
+
+// Sample review data (in a real app, this would come from a database)
+const sampleReviews = {
+  "McDonald's": [
+    { user: "User1", review: "Great value meals and fast service!", sentiment: "positive" },
+    { user: "User2", review: "The fries were cold when I got them.", sentiment: "negative" }
+  ],
+  "KFC": [
+    { user: "User3", review: "Best fried chicken in town!", sentiment: "positive" },
+    { user: "User4", review: "Too greasy and the wait was too long.", sentiment: "negative" }
+  ],
+  "Burger King": [
+    { user: "User5", review: "The Whopper is still my favorite burger.", sentiment: "positive" },
+    { user: "User6", review: "Service was slow during lunch hour.", sentiment: "negative" }
+  ],
+  "Wendy's": [
+    { user: "User7", review: "Fresh ingredients and great salad options.", sentiment: "positive" },
+    { user: "User8", review: "The restaurant was not clean.", sentiment: "negative" }
+  ],
+  "Pizza Planet": [
+    { user: "User1", review: "This place is amazing!", sentiment: "positive" },
+    { user: "User2", review: "Worst restaurant in town!", sentiment: "negative" }
+  ]
+};
+
+// Load sample reviews for the selected restaurant
+
+function loadReviews() {
+  const reviews = sampleReviews[selectedRestaurant] || [];
+  const container = document.getElementById('reviews-container');
+
+  container.innerHTML = '';
+
+  reviews.forEach((item, index) => {
+    const emoji = item.sentiment === 'positive' ? '😄' : '☹️';
+    const reviewCard = document.createElement('div');
+    reviewCard.className = 'review-card';
+    reviewCard.innerHTML = `<p><strong>User${index + 1}:</strong> ${emoji} ${item.review}</p>`;
+    container.appendChild(reviewCard);
+  });
+}
+
+// Load reviews when the page loads
+window.onload = loadReviews;
+
+// Function to show feedback options after prediction
+function showFeedbackOptions() {
+  // Check if feedback options already exist, create if they don't
+  let feedbackDiv = document.getElementById("feedback-options");
+
+  if (!feedbackDiv) {
+    // Create feedback options container
+    feedbackDiv = document.createElement("div");
+    feedbackDiv.id = "feedback-options";
+    feedbackDiv.className = "feedback-options";
+
+    // Create question text
+    const questionPara = document.createElement("p");
+    questionPara.textContent = "Is this prediction correct?";
+
+    // Create yes button
+    const yesButton = document.createElement("button");
+    yesButton.className = "feedback-btn";
+    yesButton.textContent = "Yes ✓";
+    yesButton.onclick = function () { provideFeedback(true); };
+
+    // Create no button
+    const noButton = document.createElement("button");
+    noButton.className = "feedback-btn";
+    noButton.textContent = "No ✗";
+    noButton.onclick = function () { provideFeedback(false); };
+
+    // Add all elements to the feedback div
+    feedbackDiv.appendChild(questionPara);
+    feedbackDiv.appendChild(yesButton);
+    feedbackDiv.appendChild(noButton);
+
+    // Add feedback div to the prediction box
+    document.getElementById("prediction-box").appendChild(feedbackDiv);
+  } else {
+    // If it already exists, just make it visible
+    feedbackDiv.style.display = "block";
+  }
+
+  // Hide any previous feedback response
+  const feedbackResponse = document.getElementById("feedback-response");
+  if (feedbackResponse) {
+    feedbackResponse.style.display = "none";
+  }
+}
+
+// Handle user feedback on prediction
+function provideFeedback(isCorrect) {
+  // Create or get the feedback response element
+  let feedbackResponse = document.getElementById("feedback-response");
+
+  if (!feedbackResponse) {
+    feedbackResponse = document.createElement("div");
+    feedbackResponse.id = "feedback-response";
+    document.getElementById("prediction-box").appendChild(feedbackResponse);
+  }
+
+  const reviewText = document.getElementById("review").value.trim();
+  const sentiment = document.getElementById("prediction").textContent;
+
+  if (isCorrect) {
+    feedbackResponse.innerHTML = '<p class="feedback-correct">Thank you for confirming! Your feedback helps improve our system.</p>';
+
+    // Add the review to the list of reviews
+    addReviewToList(reviewText, sentiment === "Positive");
+  } else {
+    feedbackResponse.innerHTML = '<p class="feedback-incorrect">Thanks for letting us know! We\'ll use this feedback to improve our predictions.</p>';
+
+    // In a real system, you would log this misclassification for model improvement
+  }
+
+  feedbackResponse.style.display = "block";
+
+  // Hide the feedback options
+  document.getElementById("feedback-options").style.display = "none";
+
+  // Clear the review input after some time
+  setTimeout(() => {
+    document.getElementById("review").value = "";
+  }, 1500);
+}
+
+// Add a new review to the list of previous reviews
+function addReviewToList(reviewText, isPositive) {
+  const reviewsContainer = document.getElementById("reviews-container");
+  if (!reviewsContainer) return; // Exit if container doesn't exist
+
+  const emoji = isPositive ? "😄" : "☹️";
+
+  // Create new review element
+  const reviewCard = document.createElement("div");
+  reviewCard.className = "review-card";
+  reviewCard.innerHTML = `<p><strong>You:</strong> ${emoji} ${reviewText}</p>`;
+
+  // Add it to the top of the list
+  if (reviewsContainer.firstChild) {
+    reviewsContainer.insertBefore(reviewCard, reviewsContainer.firstChild);
+  } else {
+    reviewsContainer.appendChild(reviewCard);
+  }
+}
+
+// Function to go back to restaurant selection
+function goBack() {
+  window.location.href = "index.html";
+}
\ No newline at end of file
diff --git a/app-frontend/review.html b/app-frontend/review.html
new file mode 100644
index 0000000..02df602
--- /dev/null
+++ b/app-frontend/review.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+    <meta charset="UTF-8" />
+    <title>Restaurant Review Portal</title>
+    <link rel="stylesheet" href="styles.css">
+</head>
+
+<body>
+    <div class="container">
+        <h1>Restaurant Review Portal</h1>
+        <div id="restaurant-display" class="restaurant-name">
+            <p><strong id="restaurant-name-display">Loading restaurant...</strong></p>
+        </div>
+
+        <div class="review-card">
+            <p><strong>Enter your review:</strong></p>
+            <form onsubmit="event.preventDefault(); send_review();">
+                <textarea id="review" name="review" rows="3"
+                    placeholder="Write your thoughts about this restaurant..."></textarea>
+                <button type="submit">Send Review</button>
+            </form>
+            <div id="prediction-box" class="prediction-box" style="display: none;">
+                This review is: <span id="prediction"></span>
+                <!-- Feedback options will be added dynamically by predict.js -->
+            </div>
+            <!-- Feedback response will be added dynamically -->
+        </div>
+
+        <div class="previous-reviews">
+            <h3>Previous Reviews</h3>
+            <div id="reviews-container">
+                <!-- Reviews will be loaded here -->
+            </div>
+        </div>
+
+        <div class="navigation">
+            <button class="secondary-btn" onclick="goBack()">Choose Different Restaurant</button>
+        </div>
+    </div>
+
+    <script src="predict.js"></script>
+</body>
+
+</html>
\ No newline at end of file
diff --git a/app-frontend/styles.css b/app-frontend/styles.css
index 4d6e8ce..1e36eff 100644
--- a/app-frontend/styles.css
+++ b/app-frontend/styles.css
@@ -9,6 +9,7 @@ body {
     padding: 40px 20px;
     display: flex;
     justify-content: center;
+    min-height: 100vh;
 }
 
 .container {
@@ -20,8 +21,7 @@ body {
     box-shadow: 0 8px 20px rgba(0, 0, 0, 0.15);
 }
 
-h1,
-h2 {
+h1, h2, h3 {
     text-align: center;
     margin: 0;
     color: #333;
@@ -30,8 +30,50 @@ h2 {
 h2 {
     margin-bottom: 30px;
     font-weight: normal;
+    color: #666;
+}
+
+h3 {
+    margin: 20px 0;
+    font-size: 1.3rem;
+}
+
+/* Restaurant Selection Page Styles */
+.restaurant-selection {
+    display: grid;
+    grid-template-columns: repeat(2, 1fr);
+    gap: 20px;
+    margin-top: 30px;
 }
 
+.restaurant-card {
+    background-color: #f8f9fa;
+    border-radius: 12px;
+    padding: 20px;
+    text-align: center;
+    cursor: pointer;
+    transition: all 0.3s ease;
+    border: 2px solid transparent;
+    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.05);
+}
+
+.restaurant-card:hover {
+    transform: translateY(-5px);
+    border-color: #007bff;
+    box-shadow: 0 6px 12px rgba(0, 0, 0, 0.1);
+}
+
+.restaurant-logo {
+    font-size: 3rem;
+    margin-bottom: 10px;
+}
+
+.restaurant-card h3 {
+    margin: 0;
+    color: #444;
+}
+
+/* Review Page Styles */
 .restaurant-name p {
     font-size: 1.5rem;
     text-align: center;
@@ -78,31 +120,101 @@ button:hover {
     background-color: #0056b3;
 }
 
+.secondary-btn {
+    background-color: #6c757d;
+    margin-top: 20px;
+}
+
+.secondary-btn:hover {
+    background-color: #5a6268;
+}
+
 #prediction {
     font-weight: bold;
-    color: #28a745;
 }
 
 form {
     margin-bottom: 20px;
 }
 
+.prediction-box {
+    padding: 15px;
+    border-radius: 8px;
+    margin-top: 15px;
+    font-size: 1rem;
+    transition: all 0.3s ease;
+}
+
 .prediction-positive {
     background: #e9f7ef;
-    padding: 10px 15px;
-    border-radius: 8px;
     border-left: 4px solid #28a745;
-    margin-top: 10px;
-    font-size: 1rem;
     color: #155724;
 }
 
 .prediction-negative {
     background: #f8d7da;
-    padding: 10px 15px;
-    border-radius: 8px;
     border-left: 4px solid #dc3545;
-    margin-top: 10px;
-    font-size: 1rem;
     color: #721c24;
+}
+
+.feedback-options {
+    margin-top: 15px;
+    text-align: center;
+}
+
+.feedback-options p {
+    margin-bottom: 10px;
+    font-weight: bold;
+}
+
+.feedback-btn {
+    padding: 8px 15px;
+    margin: 0 5px;
+    font-size: 0.9rem;
+}
+
+#feedback-response {
+    margin-top: 15px;
+    padding: 10px;
+    border-radius: 8px;
+    text-align: center;
+}
+
+.feedback-correct {
+    color: #28a745;
+    font-weight: bold;
+}
+
+.feedback-incorrect {
+    color: #dc3545;
+    font-weight: bold;
+}
+
+.previous-reviews {
+    margin-top: 30px;
+}
+
+.navigation {
+    display: flex;
+    justify-content: center;
+    margin-top: 20px;
+}
+
+/* Responsive adjustments */
+@media (max-width: 600px) {
+    .restaurant-selection {
+        grid-template-columns: 1fr;
+    }
+    
+    .container {
+        padding: 20px;
+    }
+    
+    h1 {
+        font-size: 1.8rem;
+    }
+    
+    h2 {
+        font-size: 1.2rem;
+    }
 }
\ No newline at end of file


Reviews:
user: login: github-bowen
body: I clicked the McDonald's icon and it redirected to itself (nothing changed) with the following logs: 
```js
 HTTP  5/15/2025 10:29:05 PM ::1 GET /review
 HTTP  5/15/2025 10:29:05 PM ::1 Returned 304 in 16 ms
 HTTP  5/15/2025 10:29:05 PM ::1 GET /styles.css
 HTTP  5/15/2025 10:29:05 PM ::1 GET /navigation.js
 HTTP  5/15/2025 10:29:05 PM ::1 Returned 304 in 6 ms
 HTTP  5/15/2025 10:29:05 PM ::1 Returned 304 in 3 ms
 HTTP  5/15/2025 10:29:06 PM ::1 GET /favicon.ico
 HTTP  5/15/2025 10:29:06 PM ::1 Returned 304 in 2 ms
 ```
state: CHANGES_REQUESTED
author_association: MEMBER
submitted_at: 2025-05-15T20:32:13Z
user: login: kanta8864
body: I have the same issue as Bowen. I am running it with docker compose but clicking on buttons does nothing. 
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T21:20:29Z
user: login: kanta8864
body: I confirm that it is working now! well done!
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T22:45:28Z
user: login: cactusutcac
state: DISMISSED
author_association: MEMBER
submitted_at: 2025-05-16T12:05:22Z
user: login: github-bowen
state: CHANGES_REQUESTED
author_association: MEMBER
submitted_at: 2025-05-16T12:22:28Z
user: login: github-bowen
state: DISMISSED
author_association: MEMBER
submitted_at: 2025-05-16T12:31:37Z
user: login: raghavtalwar7
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-16T13:17:26Z
user: login: github-bowen
body: looks good now. I will add a tag to activate docker images building
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-16T18:08:25Z

Review comments:
diff_hunk: @@ -14,4 +14,4 @@ RUN npm install -g serve
 EXPOSE 3000
 
 # Use 'serve' to serve the static files from the current directory
-CMD ["serve", "-s", ".", "-l", "3000"]
path: app-frontend/Dockerfile
user: login: github-bowen
body: The corresponding part in README should also be changed 
created_at: 2025-05-16T12:22:02Z
author_association: MEMBER
diff_hunk: @@ -14,4 +14,4 @@ RUN npm install -g serve
 EXPOSE 3000
 
 # Use 'serve' to serve the static files from the current directory
-CMD ["serve", "-s", ".", "-l", "3000"]
path: app-frontend/Dockerfile
user: login: raghavtalwar7
body: Done
created_at: 2025-05-16T13:17:26Z
author_association: MEMBER

PR comments:
user: login: raghavtalwar7
created_at: 2025-05-15T22:24:54Z
body: > I clicked the McDonald's icon and it redirected to itself (nothing changed) with the following logs:
> 
> ```js
>  HTTP  5/15/2025 10:29:05 PM ::1 GET /review
>  HTTP  5/15/2025 10:29:05 PM ::1 Returned 304 in 16 ms
>  HTTP  5/15/2025 10:29:05 PM ::1 GET /styles.css
>  HTTP  5/15/2025 10:29:05 PM ::1 GET /navigation.js
>  HTTP  5/15/2025 10:29:05 PM ::1 Returned 304 in 6 ms
>  HTTP  5/15/2025 10:29:05 PM ::1 Returned 304 in 3 ms
>  HTTP  5/15/2025 10:29:06 PM ::1 GET /favicon.ico
>  HTTP  5/15/2025 10:29:06 PM ::1 Returned 304 in 2 ms
> ```

Hmm Surprising for me it was working though i did local dev not through docker
author_association: MEMBER
user: login: raghavtalwar7
created_at: 2025-05-15T22:36:09Z
body: Had to fix the docker file it was taking wrong path. Remove -s which treats app as single page application 
author_association: MEMBER