Approve tool use

This commit is contained in:
gpt-engineer-app[bot]
2025-10-10 22:41:12 +00:00
parent 303a338a99
commit 15babfa801
6 changed files with 649 additions and 132 deletions

View File

@@ -1,5 +1,7 @@
import { useState } from 'react';
import { useState, useEffect } from 'react';
import { useAuth } from '@/contexts/AuthContext';
import { reviewService, ReviewStats, type Review, type ReviewReply } from '@/services/reviewService';
import { useToast } from '@/hooks/use-toast';
import {
Star,
ThumbsUp,
@@ -23,114 +25,50 @@ import { Progress } from '@/components/ui/progress';
import { ReviewReplyDialog } from '@/components/dashboard/ReviewReplyDialog';
import { ReviewPhotoUpload } from '@/components/dashboard/ReviewPhotoUpload';
interface ReviewReply {
id: string;
authorId: string;
authorName: string;
authorAvatar: string;
content: string;
createdAt: string;
images?: string[];
}
interface Review {
id: string;
itemId: string;
userId: string;
userName: string;
userAvatar: string;
rating: number;
comment: string;
images: string[];
createdAt: string;
helpful: number;
isHelpful: boolean;
replies: ReviewReply[];
canReply: boolean;
}
const Reviews = () => {
const { user } = useAuth();
const { toast } = useToast();
const [loading, setLoading] = useState(false);
const [replyingToReview, setReplyingToReview] = useState<string | null>(null);
const [replyContent, setReplyContent] = useState('');
const [showPhotoUpload, setShowPhotoUpload] = useState(false);
const [selectedReviewForReply, setSelectedReviewForReply] = useState<Review | null>(null);
const [reviews, setReviews] = useState<Review[]>([]);
const [ratingStats, setRatingStats] = useState<ReviewStats>({
average: 0,
totalRatings: 0,
totalReviews: 0,
breakdown: {}
});
// Sample reviews data with full functionality
const [reviews, setReviews] = useState<Review[]>([
{
id: '1',
itemId: 'item_001',
userId: 'user_001',
userName: 'Ethan Blackwood',
userAvatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=100&h=100&fit=crop&crop=center',
rating: 3.5,
comment: 'There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour, or randomised words which.',
images: [
'https://images.unsplash.com/photo-1414235077428-338989a2e8c0?w=200&h=200&fit=crop',
'https://images.unsplash.com/photo-1517248135467-4c7edcad34c4?w=200&h=200&fit=crop',
'https://images.unsplash.com/photo-1551632811-561732d1e306?w=200&h=200&fit=crop'
],
createdAt: '25 Oct 2023 at 12:27 pm',
helpful: 16,
isHelpful: false,
canReply: true,
replies: [
{
id: 'reply_1',
authorId: 'owner_001',
authorName: 'Hotel Owner',
authorAvatar: 'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=100&h=100&fit=crop&crop=center',
content: 'Thank you for your feedback! We appreciate your honest review and are working to improve our services.',
createdAt: '26 Oct 2023 at 10:15 am',
images: []
}
]
},
{
id: '2',
itemId: 'item_001',
userId: 'user_002',
userName: 'Gabriel North',
userAvatar: 'https://images.unsplash.com/photo-1500648767791-00dcc994a43e?w=100&h=100&fit=crop&crop=center',
rating: 4.0,
comment: 'This is some content from a media component. You can replace this with any content and adjust it as needed.',
images: [],
createdAt: '25 Oct 2023 at 12:27 pm',
helpful: 16,
isHelpful: true,
canReply: true,
replies: []
},
{
id: '3',
itemId: 'item_001',
userId: 'user_003',
userName: 'Pranoti Deshpande',
userAvatar: 'https://images.unsplash.com/photo-1494790108755-2616b612b5bc?w=100&h=100&fit=crop&crop=center',
rating: 3.5,
comment: 'There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour, or randomised words which don\'t look even slightly believable.',
images: [],
createdAt: '25 Oct 2023 at 12:27 pm',
helpful: 8,
isHelpful: false,
canReply: true,
replies: []
useEffect(() => {
loadReviews();
loadStats();
}, []);
const loadReviews = async () => {
try {
setLoading(true);
const data = await reviewService.getReviews();
setReviews(data);
} catch (error) {
console.error('Error loading reviews:', error);
toast({
title: 'Error',
description: 'Failed to load reviews',
variant: 'destructive',
});
} finally {
setLoading(false);
}
]);
};
// Rating statistics
const ratingStats = {
average: 4.3,
totalRatings: 2525,
totalReviews: 293,
breakdown: {
5: { count: 1138, percentage: 45 },
4: { count: 883, percentage: 35 },
3: { count: 379, percentage: 15 },
2: { count: 808, percentage: 32 },
1: { count: 1742, percentage: 69 }
const loadStats = async () => {
try {
const stats = await reviewService.getReviewStats();
setRatingStats(stats);
} catch (error) {
console.error('Error loading stats:', error);
}
};
@@ -173,41 +111,57 @@ const Reviews = () => {
return stars;
};
const handleHelpfulClick = (reviewId: string) => {
setReviews(prevReviews =>
prevReviews.map(review =>
review.id === reviewId
? {
...review,
isHelpful: !review.isHelpful,
helpful: review.isHelpful ? review.helpful - 1 : review.helpful + 1
}
: review
)
);
const handleHelpfulClick = async (reviewId: string) => {
try {
await reviewService.markAsHelpful(reviewId);
setReviews(prevReviews =>
prevReviews.map(review =>
review.id === reviewId
? {
...review,
isHelpful: !review.isHelpful,
helpful: review.isHelpful ? review.helpful - 1 : review.helpful + 1
}
: review
)
);
} catch (error) {
console.error('Error marking review as helpful:', error);
toast({
title: 'Error',
description: 'Failed to mark review as helpful',
variant: 'destructive',
});
}
};
const handleReplySubmit = (reviewId: string, content: string, images: string[] = []) => {
const newReply: ReviewReply = {
id: `reply_${Date.now()}`,
authorId: user?.id || 'current_user',
authorName: user?.name || 'Business Owner',
authorAvatar: user?.avatar || 'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=100&h=100&fit=crop&crop=center',
content,
createdAt: new Date().toLocaleString(),
images
};
const handleReplySubmit = async (reviewId: string, content: string, images: string[] = []) => {
try {
const newReply = await reviewService.createReply(reviewId, content, images);
setReviews(prevReviews =>
prevReviews.map(review =>
review.id === reviewId
? { ...review, replies: [...review.replies, newReply] }
: review
)
);
setReviews(prevReviews =>
prevReviews.map(review =>
review.id === reviewId
? { ...review, replies: [...review.replies, newReply] }
: review
)
);
toast({
title: 'Success',
description: 'Reply posted successfully',
});
setReplyingToReview(null);
setReplyContent('');
setReplyingToReview(null);
setReplyContent('');
} catch (error) {
console.error('Error posting reply:', error);
toast({
title: 'Error',
description: 'Failed to post reply',
variant: 'destructive',
});
}
};
const openReplyDialog = (review: Review) => {