Skip to content

Latest commit

 

History

History
786 lines (640 loc) · 21.2 KB

File metadata and controls

786 lines (640 loc) · 21.2 KB

Smart Review Portal Interface - Implementation

Overview

Customer-facing review portal with intelligent funnel logic that routes happy customers to Google reviews and unhappy customers to internal support.

Date Created: October 22, 2025 Status: ✅ Complete and Ready for Production


Smart Funnel Logic

High Ratings (4-5 Stars) 🌟

Customer submits 4-5 star review
  ↓
Optional feedback text
  ↓
Mark google_review_requested = true
  ↓
Show Google review CTA
  ↓
Track Google link click
  ↓
Redirect to Google review page
  ↓
Send thank you email

Low Ratings (1-3 Stars) ⚠️

Customer submits 1-3 star review
  ↓
Required detailed feedback
  ↓
Optional resolution request
  ↓
Create internal support ticket
  ↓
Mark google_review_requested = false
  ↓
Send "we'll make it right" email
  ↓
Management notified within 24 hours

Files Created

1. Portal Page

File: src/app/(dashboard)/portal/review/[requestId]/page.tsx

URL: /portal/review/[requestId]

Features:

  • Star Rating Selector: Interactive 1-5 star rating with hover effects
  • Service Details Card: Shows service type, date, location
  • Conditional UI: Changes based on selected rating
  • Real-time Validation: Prevents submission without required fields
  • Success State: Post-submission thank you with Google review option
  • Responsive Design: Mobile-friendly layout

UI Flow:

Initial State:
┌─────────────────────────────────┐
  How was your experience?       
                                 
                           
                                 
  [Service Details Card]         
└─────────────────────────────────┘

After Selecting 5 Stars:
┌─────────────────────────────────┐
        Excellent!        
                                 
   We're thrilled you're       
     satisfied!                  
                                 
  [Optional feedback textarea]   
                                 
  💡 After submitting, you'll    │
     have the option to share    
     on Google                   
                                 
  [Submit Review Button]         
└─────────────────────────────────┘

After Selecting 2 Stars:
┌─────────────────────────────────┐
        Below Expectations 
                                 
  ⚠️ We're sorry we didn't       
     meet expectations          
                                 
  What went wrong? *             
  [Required feedback textarea]   
                                 
  What can we do to make this    
  right?                         
  [Optional resolution input]    
                                 
  💡 We'll contact you within    │
     24 hours                    
                                 
  [Submit Review Button]         
└─────────────────────────────────┘

After Submission (High Rating):
┌─────────────────────────────────┐
   Thank You!                  
                                 
  We appreciate your feedback!   
                                 
  ┌───────────────────────────┐  
   Love what we did?           
   Share it with others!       
                               
   [Leave Google Review]       
                               
   Opens in new tab            
  └───────────────────────────┘  
                                 
  [Back to Portal]               
└─────────────────────────────────┘

After Submission (Low Rating):
┌─────────────────────────────────┐
   Thank You!                  
                                 
  We appreciate your feedback!   
                                 
  ┌───────────────────────────┐  
   ⚠️ We're committed to     │  │
      making this right        
                               
   Our team will review your   
   feedback and reach out      
   within 24 hours.            
  └───────────────────────────┘  
                                 
  [Back to Portal]               
└─────────────────────────────────┘

Component State:

const [rating, setRating] = useState(0)
const [hoveredRating, setHoveredRating] = useState(0)
const [feedback, setFeedback] = useState('')
const [resolutionRequest, setResolutionRequest] = useState('')
const [submitted, setSubmitted] = useState(false)

2. Get Review Request API

File: src/app/api/portal/reviews/[id]/route.ts

Endpoint: GET /api/portal/reviews/[id]

Purpose: Fetch specific review request for customer

Security:

  • Verifies customer authentication
  • Validates review request ownership
  • Returns 403 if not owned by customer

Response:

{
  "success": true,
  "data": {
    "reviewRequest": {
      "id": "uuid",
      "customer_id": "uuid",
      "job_id": "uuid",
      "portal_review_completed": false,
      "status": "pending",
      "jobs": {
        "service_type": "Carpet Cleaning",
        "completed_at": "2025-10-20T10:00:00Z",
        "service_address": "123 Main St",
        "total_amount": 150.00
      }
    }
  }
}

3. Submit Review API

File: src/app/api/portal/reviews/[id]/submit/route.ts

Endpoint: POST /api/portal/reviews/[id]/submit

Request Body:

{
  "rating": 5,
  "feedback": "Excellent service! Very professional.",
  "resolutionRequest": "" // Only for low ratings
}

Validation:

const SubmitSchema = z.object({
  rating: z.number().int().min(1).max(5),
  feedback: z.string().max(2000).optional(),
  resolutionRequest: z.string().max(500).optional(),
})

Processing Logic:

For High Ratings (4-5 stars):

// 1. Update review request
UPDATE review_requests SET
  portal_review_completed = true,
  portal_review_rating = rating,
  portal_review_text = feedback,
  portal_review_submitted_at = NOW(),
  google_review_requested = true  // ← Key difference
WHERE id = reviewRequestId

// 2. Send thank you email with Google review option

// 3. Return success with googleReviewRequested = true

For Low Ratings (1-3 stars):

// 1. Update review request
UPDATE review_requests SET
  portal_review_completed = true,
  portal_review_rating = rating,
  portal_review_text = feedback,
  portal_review_submitted_at = NOW(),
  google_review_requested = false  // ← No Google request
WHERE id = reviewRequestId

// 2. Create support ticket
INSERT INTO support_tickets (
  customer_id,
  job_id,
  title: "Low Review Rating (${rating}/5)",
  description: feedback,
  category: "service_quality",
  priority: rating === 1 ? "high" : "medium",
  status: "open",
  source: "review_system"
)

// 3. Send "we'll make it right" email

// 4. Return success with supportTicketCreated = true

Response (High Rating):

{
  "success": true,
  "data": {
    "message": "Review submitted successfully",
    "googleReviewRequested": true
  }
}

Response (Low Rating):

{
  "success": true,
  "data": {
    "message": "Review submitted successfully",
    "googleReviewRequested": false,
    "supportTicketCreated": true,
    "supportTicketId": "uuid"
  }
}

4. Google Click Tracking API

File: src/app/api/portal/reviews/[id]/google-click/route.ts

Endpoint: POST /api/portal/reviews/[id]/google-click

Purpose: Track when customer clicks Google review link

Processing:

UPDATE review_requests SET
  google_review_link_clicked = true,
  google_review_clicked_at = NOW()
WHERE id = reviewRequestId

Response:

{
  "success": true,
  "data": {
    "message": "Google review click tracked successfully"
  }
}

Usage in Frontend:

const handleGoogleReviewClick = async () => {
  // Track the click
  await fetch(`/api/portal/reviews/${requestId}/google-click`, {
    method: 'POST',
  })

  // Open Google review page
  window.open(`/api/reviews/google/redirect/${requestId}`, '_blank')
}

Support Ticket Creation

Ticket Details

Title Format:

Low Review Rating (2/5) - Carpet Cleaning

Description Format:

Customer left a 2/5 star review with the following feedback:

The cleaning wasn't as thorough as expected. Some stains were still visible.

Resolution Request: Please re-clean the living room area.

Fields:

{
  customer_id: "uuid",
  job_id: "uuid",
  title: "Low Review Rating (X/5) - Service Type",
  description: "Feedback + Resolution Request",
  category: "service_quality",
  priority: rating === 1 ? "high" : "medium",
  status: "open",
  source: "review_system",
  metadata: {
    review_request_id: "uuid",
    rating: 2,
    feedback: "...",
    resolution_request: "...",
    auto_created: true
  }
}

Priority Logic:

  • 1 star = high priority
  • 2-3 stars = medium priority

Email Templates

High Rating Thank You Email

Subject: "Thank you for your positive feedback! ⭐"

Content:

<!DOCTYPE html>
<html>
<head>
  <title>Thank You!</title>
</head>
<body>
  <div style="background-color: #14213d; color: white; padding: 30px;">
    <h1>Thank You!</h1>
  </div>

  <div style="padding: 30px;">
    <p>Hi [Customer Name],</p>

    <p>Thank you for taking the time to share your positive feedback!
       We're thrilled to hear that you're satisfied with our service.</p>

    <div style="background-color: #eef2ff; padding: 20px; margin: 20px 0;">
      <h3>Help Others Find Quality Service</h3>
      <p>If you have a moment, we'd greatly appreciate if you could share
         your experience on Google.</p>
      <p style="text-align: center;">
        <a href="[Google Review Link]"
           style="background-color: #14213d; color: white; padding: 12px 24px;">
          Leave a Google Review
        </a>
      </p>
    </div>

    <p>We appreciate your business!</p>
    <p>Best regards,<br><strong>The Dirt Free Team</strong></p>
  </div>
</body>
</html>

Low Rating Resolution Email

Subject: "We're committed to making this right"

Content:

<!DOCTYPE html>
<html>
<head>
  <title>We're Here to Help</title>
</head>
<body>
  <div style="background-color: #14213d; color: white; padding: 30px;">
    <h1>We're Committed to Making This Right</h1>
  </div>

  <div style="padding: 30px;">
    <p>Hi [Customer Name],</p>

    <p>Thank you for sharing your honest feedback. We're sorry that your
       experience didn't meet your expectations.</p>

    <p><strong>We take your feedback seriously.</strong> Our team has been
       notified and will reach out to you within 24 hours.</p>

    <div style="background-color: #fffbeb; border-left: 4px solid #fbbf24; padding: 15px;">
      <p><strong>What happens next:</strong></p>
      <ul>
        <li>A member of our management team will contact you</li>
        <li>We'll listen to your concerns and work on a solution</li>
        <li>We'll take action to prevent similar issues</li>
      </ul>
    </div>

    <p>We value your business and the opportunity to earn back your trust.</p>
    <p>Best regards,<br><strong>The Dirt Free Team</strong></p>
  </div>
</body>
</html>

Complete User Journey

Scenario 1: Happy Customer (5 Stars)

  1. Receives Email:

    • "We'd love your feedback! ⭐"
    • Contains link to /portal/review/[requestId]
  2. Opens Review Page:

    • Sees service details
    • Selects 5 stars
    • UI shows positive message
    • Optional feedback textarea appears
  3. Submits Review:

    • Clicks "Submit Review"
    • Success message appears
    • Google review CTA shown
  4. Clicks Google Review:

    • Click is tracked (google_review_link_clicked = true)
    • Opens Google review page in new tab
    • Can leave public review
  5. Receives Thank You Email:

    • "Thank you for your positive feedback! ⭐"
    • Includes another Google review link

Scenario 2: Unhappy Customer (2 Stars)

  1. Receives Email:

    • "We'd love your feedback! ⭐"
    • Contains link to /portal/review/[requestId]
  2. Opens Review Page:

    • Sees service details
    • Selects 2 stars
    • UI shows apologetic message
    • Required feedback textarea appears
    • Optional resolution request appears
  3. Provides Feedback:

    • Types: "Service wasn't thorough, stains still visible"
    • Resolution: "Please re-clean living room"
  4. Submits Review:

    • Clicks "Submit Review"
    • Success message appears
    • No Google review option (stays internal)
  5. Support Ticket Created:

    • Title: "Low Review Rating (2/5) - Carpet Cleaning"
    • Priority: Medium
    • Assigned to management team
  6. Receives Email:

    • "We're committed to making this right"
    • Explains 24-hour response timeline
  7. Management Follow-up:

    • Receives call within 24 hours
    • Issue is discussed
    • Resolution is implemented
    • Follow-up review request (optional)

Database Impact

review_requests Table Updates

After High Rating Submission:

UPDATE review_requests SET
  portal_review_completed = true,
  portal_review_rating = 5,
  portal_review_text = "Great service!",
  portal_review_submitted_at = '2025-10-22 14:30:00',
  google_review_requested = true  -- Eligible for Google
WHERE id = 'review-uuid'

After Low Rating Submission:

UPDATE review_requests SET
  portal_review_completed = true,
  portal_review_rating = 2,
  portal_review_text = "Service wasn't thorough",
  portal_review_submitted_at = '2025-10-22 14:30:00',
  google_review_requested = false  -- Keep internal
WHERE id = 'review-uuid'

After Google Click:

UPDATE review_requests SET
  google_review_link_clicked = true,
  google_review_clicked_at = '2025-10-22 14:32:00'
WHERE id = 'review-uuid'

Metrics & Analytics

Tracked Metrics

  1. Submission Metrics:

    • Total reviews submitted
    • Reviews by rating (1-5 distribution)
    • Average rating
    • Completion rate
  2. Google Review Metrics:

    • Google reviews requested (4-5 stars)
    • Google review link clicks
    • Click-through rate
    • Estimated Google reviews completed
  3. Support Ticket Metrics:

    • Low ratings (1-3 stars)
    • Support tickets created
    • Ticket resolution time
    • Customer satisfaction after resolution
  4. Email Metrics:

    • Thank you emails sent
    • Resolution emails sent
    • Email open rates (if tracking enabled)

Example Analytics Query

-- Review funnel by rating
SELECT
  portal_review_rating,
  COUNT(*) as total_reviews,
  COUNT(*) FILTER (WHERE google_review_requested) as google_requested,
  COUNT(*) FILTER (WHERE google_review_link_clicked) as google_clicked,
  ROUND(
    COUNT(*) FILTER (WHERE google_review_link_clicked)::numeric /
    NULLIF(COUNT(*) FILTER (WHERE google_review_requested), 0) * 100,
    2
  ) as click_through_rate
FROM review_requests
WHERE portal_review_completed = true
GROUP BY portal_review_rating
ORDER BY portal_review_rating DESC

Expected Output:

rating | total | requested | clicked | ctr
-------|-------|-----------|---------|-------
5      | 45    | 45        | 32      | 71.11%
4      | 30    | 30        | 18      | 60.00%
3      | 10    | 0         | 0       | 0.00%
2      | 8     | 0         | 0       | 0.00%
1      | 7     | 0         | 0       | 0.00%

Testing Checklist

Portal Page Tests

  • Page loads with review request details
  • Star rating is interactive (hover + click)
  • Rating selection updates UI appropriately
  • High rating (4-5) shows optional feedback
  • Low rating (1-3) requires feedback
  • Low rating shows resolution request field
  • Submit button disabled until valid
  • Already-submitted reviews show completion message
  • Success state displays correctly
  • Google review button works (high ratings)
  • Mobile responsive layout

API Tests

  • GET request returns correct review data
  • GET request enforces customer ownership
  • Submit validates rating range (1-5)
  • Submit requires feedback for low ratings
  • High rating creates Google review request
  • Low rating creates support ticket
  • Thank you email sent after submission
  • Google click tracking works
  • Duplicate submissions prevented

Support Ticket Tests

  • Ticket created for 1-3 star ratings
  • Ticket has correct priority (1 star = high, 2-3 = medium)
  • Ticket includes all feedback
  • Ticket metadata includes review_request_id
  • Ticket assigned to correct team
  • Management notified of new tickets

Email Tests

  • High rating email includes Google review link
  • Low rating email explains resolution process
  • Emails are mobile-responsive
  • Links in emails work correctly
  • Unsubscribe link included (if required)

Security Considerations

  1. Authentication:

    • All endpoints require customer authentication
    • Review requests verified against customer ownership
  2. Authorization:

    • Customer can only access their own review requests
    • Returns 403 if attempting to access others' reviews
  3. Input Validation:

    • Zod schema validation on all inputs
    • Rating range enforced (1-5)
    • Text length limits (feedback: 2000, resolution: 500)
  4. Rate Limiting:

    • Consider implementing rate limits on submission endpoint
    • Prevent spam submissions
  5. CSRF Protection:

    • Next.js built-in CSRF protection
    • API routes use POST with JSON body

Performance Optimization

  1. Database Indexes:

    CREATE INDEX idx_review_requests_customer ON review_requests(customer_id);
    CREATE INDEX idx_review_requests_portal_rating ON review_requests(portal_review_rating)
      WHERE portal_review_rating IS NOT NULL;
  2. Caching:

    • Review request data can be cached for 5 minutes
    • Clear cache on submission
  3. Email Queue:

    • Thank you emails sent asynchronously
    • Don't block submission response
  4. Support Ticket Creation:

    • Create ticket asynchronously if slow
    • Log but don't fail if ticket creation fails

Future Enhancements

1. Follow-up for Low Ratings

  • After issue resolution, send follow-up review request
  • "Have we made things right?" second chance

2. Review Incentives

  • Offer discount code for completing review
  • Loyalty points for feedback

3. Multi-step Review

  • Split into multiple pages
  • Rating → Feedback → Google (for high) or Resolution (for low)

4. Photo Upload

  • Allow customers to upload photos with feedback
  • Especially useful for low ratings (evidence)

5. SMS Review Option

  • Send review link via SMS
  • Shorter, mobile-optimized form

6. Social Sharing

  • Share positive reviews on social media
  • Facebook, Twitter, Instagram options

7. Review Response Templates

  • Pre-written responses for staff
  • Quick replies to common feedback

Summary

Complete Smart Review Portal Interface

Files Created: 4

  1. src/app/(dashboard)/portal/review/[requestId]/page.tsx - Review submission UI
  2. src/app/api/portal/reviews/[id]/route.ts - Get review request
  3. src/app/api/portal/reviews/[id]/submit/route.ts - Submit review with smart logic
  4. src/app/api/portal/reviews/[id]/google-click/route.ts - Track Google clicks

Key Features:

  • ✅ Interactive 1-5 star rating
  • ✅ Conditional UI based on rating
  • ✅ Smart funnel (4-5 → Google, 1-3 → Support)
  • ✅ Support ticket auto-creation for low ratings
  • ✅ Google review click tracking
  • ✅ Professional email templates
  • ✅ Mobile-responsive design
  • ✅ Complete error handling
  • ✅ Customer ownership verification

Smart Routing:

  • High (4-5 stars): Optional feedback → Google review → Public visibility
  • Low (1-3 stars): Required feedback → Support ticket → Private resolution

Production Ready: ✅ Yes

  • TypeScript validation complete (except pre-existing toast hook issue)
  • Security: Customer ownership verified
  • Validation: Zod schemas on all inputs
  • Error handling: Comprehensive try-catch blocks
  • Email delivery: Async, non-blocking
  • Support integration: Auto-ticket creation

The smart review portal interface is complete and ready to filter positive reviews to Google while handling negative reviews internally! 🎯