mirror of
https://github.com/myronblair/tomsjavajive
synced 2026-06-30 17:50:32 -05:00
5637b6d7f5
Extract account/cart/checkout styles into dedicated CSS files; remove inline styles and orphaned style blocks from HTML. Wire $extraHead on all account pages, cart.php, and checkout.php. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
291 lines
13 KiB
PHP
291 lines
13 KiB
PHP
<?php
|
|
/**
|
|
* Tom's Java Jive - Customer Reviews
|
|
*/
|
|
|
|
$pageTitle = "My Reviews - Tom's Java Jive";
|
|
require_once __DIR__ . '/../includes/functions.php';
|
|
require_once __DIR__ . '/../includes/auth.php';
|
|
|
|
CustomerAuth::require();
|
|
$customer = CustomerAuth::getFullUser();
|
|
$currentPage = 'reviews';
|
|
|
|
// Handle delete action
|
|
if (isset($_POST['delete_review'])) {
|
|
$reviewId = $_POST['review_id'] ?? '';
|
|
db()->query(
|
|
"DELETE FROM reviews WHERE review_id = :rid AND customer_id = :cid",
|
|
['rid' => $reviewId, 'cid' => $customer['customer_id']]
|
|
);
|
|
setFlash('success', 'Review deleted');
|
|
redirect('/account/reviews.php');
|
|
}
|
|
|
|
// Get customer's reviews with product info
|
|
$reviews = db()->fetchAll(
|
|
"SELECT r.*, p.name as product_name, p.product_id as product_pid, p.images as product_images
|
|
FROM reviews r
|
|
LEFT JOIN products p ON r.product_id = p.product_id
|
|
WHERE r.customer_id = :id
|
|
ORDER BY r.created_at DESC",
|
|
['id' => $customer['customer_id']]
|
|
);
|
|
|
|
// Get products eligible for review (purchased but not reviewed)
|
|
$eligibleProducts = db()->fetchAll(
|
|
"SELECT DISTINCT p.product_id, p.name, p.images
|
|
FROM orders o
|
|
JOIN order_items oi ON o.order_id = oi.order_id
|
|
JOIN products p ON oi.product_id = p.product_id
|
|
WHERE o.customer_id = :cid
|
|
AND o.order_status = 'delivered'
|
|
AND p.product_id NOT IN (SELECT product_id FROM reviews WHERE customer_id = :cid2)
|
|
LIMIT 10",
|
|
['cid' => $customer['customer_id'], 'cid2' => $customer['customer_id']]
|
|
);
|
|
|
|
$extraHead = '<link rel="stylesheet" href="/assets/css/account.css?v='. filemtime(__DIR__ . '/../assets/css/account.css') .'">';
|
|
require_once __DIR__ . '/../includes/header.php';
|
|
require_once __DIR__ . '/includes/sidebar.php';
|
|
?>
|
|
|
|
<div class="account-header">
|
|
<h1>My Reviews</h1>
|
|
<p class="text-muted">Manage your product reviews</p>
|
|
</div>
|
|
|
|
<?php if (hasFlash('success')): ?>
|
|
<div class="alert alert-success mb-2">
|
|
<i class="fas fa-check-circle"></i> <?= getFlash('success') ?>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<!-- Products to Review -->
|
|
<?php if (!empty($eligibleProducts)): ?>
|
|
<div class="section-card">
|
|
<div class="section-card-header">
|
|
<h3><i class="fas fa-edit"></i> Products to Review</h3>
|
|
</div>
|
|
<div class="section-card-body">
|
|
<p class="text-muted" style="margin-bottom: 1rem;">You've purchased these products. Share your experience!</p>
|
|
<div style="display: flex; gap: 1rem; flex-wrap: wrap;">
|
|
<?php foreach ($eligibleProducts as $product):
|
|
$images = json_decode($product['images'] ?? '[]', true);
|
|
$imageUrl = !empty($images) ? $images[0] : '/assets/images/placeholder-product.svg';
|
|
?>
|
|
<div style="display: flex; align-items: center; gap: 0.75rem; background: var(--color-background); padding: 0.75rem 1rem; border-radius: var(--radius-md);">
|
|
<img src="<?= htmlspecialchars($imageUrl) ?>" alt="" style="width: 40px; height: 40px; border-radius: 4px; object-fit: cover;" onerror="this.src='/assets/images/placeholder-product.svg'">
|
|
<span style="font-size: 0.875rem;"><?= htmlspecialchars(truncate($product['name'], 25)) ?></span>
|
|
<button class="btn btn-sm btn-primary" onclick="openReviewModal('<?= $product['product_id'] ?>', '<?= htmlspecialchars(addslashes($product['name'])) ?>')">
|
|
Review
|
|
</button>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<!-- My Reviews -->
|
|
<div class="section-card">
|
|
<div class="section-card-header">
|
|
<h3><i class="fas fa-star"></i> My Reviews (<?= count($reviews) ?>)</h3>
|
|
</div>
|
|
<div class="section-card-body" style="padding: 0;">
|
|
<?php if (empty($reviews)): ?>
|
|
<div class="text-center" style="padding: 3rem;">
|
|
<i class="fas fa-star" style="font-size: 3rem; color: var(--color-text-muted); margin-bottom: 1rem;"></i>
|
|
<h3>No reviews yet</h3>
|
|
<p class="text-muted">Purchase products and share your thoughts!</p>
|
|
<a href="/shop.php" class="btn btn-primary mt-1">Browse Products</a>
|
|
</div>
|
|
<?php else: ?>
|
|
<?php foreach ($reviews as $review):
|
|
$images = json_decode($review['product_images'] ?? '[]', true);
|
|
$imageUrl = !empty($images) ? $images[0] : '/assets/images/placeholder-product.svg';
|
|
?>
|
|
<div style="padding: 1.5rem; border-bottom: 1px solid var(--color-border);">
|
|
<div style="display: flex; gap: 1rem;">
|
|
<img src="<?= htmlspecialchars($imageUrl) ?>" alt="" style="width: 80px; height: 80px; border-radius: var(--radius-md); object-fit: cover;" onerror="this.src='/assets/images/placeholder-product.svg'">
|
|
|
|
<div style="flex: 1;">
|
|
<div style="display: flex; justify-content: space-between; align-items: start; margin-bottom: 0.5rem;">
|
|
<div>
|
|
<a href="/product.php?id=<?= htmlspecialchars($review['product_pid']) ?>" style="font-weight: 600; color: inherit;">
|
|
<?= htmlspecialchars($review['product_name'] ?? 'Product') ?>
|
|
</a>
|
|
<div style="margin-top: 0.25rem;">
|
|
<?php for ($i = 1; $i <= 5; $i++): ?>
|
|
<i class="fas fa-star" style="color: <?= $i <= $review['rating'] ? 'var(--color-warning)' : 'var(--color-border)' ?>; font-size: 0.875rem;"></i>
|
|
<?php endfor; ?>
|
|
<span class="text-muted" style="margin-left: 0.5rem; font-size: 0.875rem;">
|
|
<?= formatDate($review['created_at']) ?>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div style="display: flex; align-items: center; gap: 0.5rem;">
|
|
<?php
|
|
$statusBadge = $review['is_approved'] ? ['success', 'Published'] : ['warning', 'Pending'];
|
|
?>
|
|
<span class="badge badge-<?= $statusBadge[0] ?>"><?= $statusBadge[1] ?></span>
|
|
|
|
<form method="POST" style="display: inline;" onsubmit="return confirm('Delete this review?')">
|
|
<input type="hidden" name="review_id" value="<?= $review['review_id'] ?>">
|
|
<button type="submit" name="delete_review" class="btn btn-sm btn-danger">
|
|
<i class="fas fa-trash"></i>
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<?php if ($review['title']): ?>
|
|
<h4 style="margin: 0 0 0.5rem; font-size: 1rem;"><?= htmlspecialchars($review['title']) ?></h4>
|
|
<?php endif; ?>
|
|
|
|
<p style="margin: 0; color: var(--color-text-muted); font-size: 0.9375rem;">
|
|
<?= nl2br(htmlspecialchars($review['comment'] ?? '')) ?>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Review Modal -->
|
|
<div class="modal-overlay" id="reviewModal">
|
|
<div class="modal">
|
|
<div class="modal-header">
|
|
<h3 class="modal-title">Write a Review</h3>
|
|
<button type="button" class="modal-close" onclick="Modal.close('reviewModal')">×</button>
|
|
</div>
|
|
<form action="/api/submit-review.php" method="POST" id="reviewForm">
|
|
<input type="hidden" name="product_id" id="review_product_id">
|
|
<div class="modal-body">
|
|
<p style="margin-bottom: 1rem;"><strong id="review_product_name"></strong></p>
|
|
|
|
<div class="form-group">
|
|
<label class="form-label">Rating *</label>
|
|
<div id="rating-stars" style="font-size: 2rem;">
|
|
<?php for ($i = 1; $i <= 5; $i++): ?>
|
|
<i class="far fa-star rating-star" data-rating="<?= $i ?>" style="cursor: pointer; color: var(--color-warning);"></i>
|
|
<?php endfor; ?>
|
|
</div>
|
|
<input type="hidden" name="rating" id="rating_value" required>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label class="form-label">Review Title</label>
|
|
<input type="text" name="title" class="form-input" placeholder="Summarize your review">
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label class="form-label">Your Review *</label>
|
|
<textarea name="content" class="form-input" rows="4" required placeholder="Share your experience with this product"></textarea>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" onclick="Modal.close('reviewModal')">Cancel</button>
|
|
<button type="submit" class="btn btn-primary">
|
|
<i class="fas fa-paper-plane"></i> Submit Review
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
function openReviewModal(productId, productName) {
|
|
document.getElementById('review_product_id').value = productId;
|
|
document.getElementById('review_product_name').textContent = productName;
|
|
document.getElementById('rating_value').value = '';
|
|
document.getElementById('reviewForm').reset();
|
|
|
|
// Reset stars
|
|
document.querySelectorAll('.rating-star').forEach(star => {
|
|
star.classList.remove('fas');
|
|
star.classList.add('far');
|
|
});
|
|
|
|
Modal.open('reviewModal');
|
|
}
|
|
|
|
// Star rating interaction
|
|
document.querySelectorAll('.rating-star').forEach(star => {
|
|
star.addEventListener('click', function() {
|
|
const rating = parseInt(this.dataset.rating);
|
|
document.getElementById('rating_value').value = rating;
|
|
|
|
document.querySelectorAll('.rating-star').forEach((s, index) => {
|
|
if (index < rating) {
|
|
s.classList.remove('far');
|
|
s.classList.add('fas');
|
|
} else {
|
|
s.classList.remove('fas');
|
|
s.classList.add('far');
|
|
}
|
|
});
|
|
});
|
|
|
|
star.addEventListener('mouseenter', function() {
|
|
const rating = parseInt(this.dataset.rating);
|
|
document.querySelectorAll('.rating-star').forEach((s, index) => {
|
|
if (index < rating) {
|
|
s.classList.remove('far');
|
|
s.classList.add('fas');
|
|
}
|
|
});
|
|
});
|
|
|
|
star.addEventListener('mouseleave', function() {
|
|
const currentRating = parseInt(document.getElementById('rating_value').value) || 0;
|
|
document.querySelectorAll('.rating-star').forEach((s, index) => {
|
|
if (index < currentRating) {
|
|
s.classList.remove('far');
|
|
s.classList.add('fas');
|
|
} else {
|
|
s.classList.remove('fas');
|
|
s.classList.add('far');
|
|
}
|
|
});
|
|
});
|
|
});
|
|
|
|
// Form submission
|
|
document.getElementById('reviewForm').addEventListener('submit', async function(e) {
|
|
e.preventDefault();
|
|
|
|
const rating = document.getElementById('rating_value').value;
|
|
if (!rating) {
|
|
alert('Please select a rating');
|
|
return;
|
|
}
|
|
|
|
const formData = new FormData(this);
|
|
const data = Object.fromEntries(formData.entries());
|
|
|
|
try {
|
|
const response = await fetch('/api/submit-review.php', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(data)
|
|
});
|
|
const result = await response.json();
|
|
|
|
if (result.error) {
|
|
alert(result.error);
|
|
} else {
|
|
alert('Thank you! Your review has been submitted and is pending approval.');
|
|
window.location.reload();
|
|
}
|
|
} catch (err) {
|
|
alert('Failed to submit review. Please try again.');
|
|
}
|
|
});
|
|
</script>
|
|
|
|
<?php require_once __DIR__ . '/includes/footer.php'; ?>
|