ritesh19180's picture
Upload folder using huggingface_hub
d96d2a2 verified
Raw
History Blame Contribute Delete
7.56 kB
import React, { useState } from 'react';
import { Star, CheckCircle2, X, Loader2, MessageSquare } from 'lucide-react';
import { supabase } from '../../lib/supabaseClient';
/**
* CSATModal — shown when a ticket is resolved and no rating has been given yet.
*
* Props:
* - ticketId: string
* - onSubmit: () => void — called after a rating is saved successfully
* - onDismiss: () => void — called if the user closes without rating
*/
export default function CSATModal({ ticketId, onSubmit, onDismiss }) {
const [hovered, setHovered] = useState(0);
const [selected, setSelected] = useState(0);
const [comment, setComment] = useState('');
const [submitted, setSubmitted] = useState(false);
const [loading, setLoading] = useState(false);
const [error, setError] = useState('');
const ratingLabels = {
1: 'Very Dissatisfied',
2: 'Dissatisfied',
3: 'Neutral',
4: 'Satisfied',
5: 'Very Satisfied',
};
const handleSubmit = async () => {
if (!selected) { setError('Please select a rating.'); return; }
setLoading(true);
setError('');
try {
const { error: upError } = await supabase
.from('tickets')
.update({
csat_rating: selected,
csat_comment: comment.trim() || null,
})
.eq('id', ticketId);
if (upError) throw upError;
setSubmitted(true);
setTimeout(() => { onSubmit?.(selected); }, 1800);
} catch (err) {
setError('Failed to submit. Please try again.');
console.error(err);
} finally {
setLoading(false);
}
};
if (submitted) {
return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/40 backdrop-blur-sm px-4">
<div className="bg-white rounded-3xl shadow-2xl p-10 text-center w-full max-w-sm border border-gray-100">
<div className="w-16 h-16 bg-emerald-100 rounded-full flex items-center justify-center mx-auto mb-4">
<CheckCircle2 className="w-8 h-8 text-emerald-600" />
</div>
<h3 className="text-xl font-bold text-gray-900 mb-2">Thank you!</h3>
<p className="text-gray-500 text-sm">Your feedback helps us improve.</p>
</div>
</div>
);
}
return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/40 backdrop-blur-sm px-4">
<div className="bg-white rounded-3xl shadow-2xl w-full max-w-md border border-gray-100 overflow-hidden">
{/* Header */}
<div className="bg-gradient-to-br from-emerald-900 to-emerald-700 p-6 text-white relative">
<button
onClick={onDismiss}
className="absolute top-4 right-4 p-1.5 hover:bg-white/10 rounded-full transition-colors"
>
<X className="w-4 h-4" />
</button>
<div className="w-12 h-12 bg-white/10 rounded-2xl flex items-center justify-center mb-3 backdrop-blur-sm border border-white/20">
<Star className="w-6 h-6 text-yellow-300 fill-yellow-300" />
</div>
<h3 className="text-lg font-bold mb-1">How was your resolution?</h3>
<p className="text-emerald-100/80 text-sm">Your ticket has been resolved. Please rate our support.</p>
</div>
{/* Body */}
<div className="p-6 space-y-6">
{/* Star Rating */}
<div className="flex flex-col items-center gap-3">
<div className="flex gap-2">
{[1, 2, 3, 4, 5].map((star) => (
<button
key={star}
onMouseEnter={() => setHovered(star)}
onMouseLeave={() => setHovered(0)}
onClick={() => { setSelected(star); setError(''); }}
className="transition-all duration-200"
style={{ transform: hovered === star ? 'scale(1.2)' : 'scale(1)' }}
>
<Star
className={`w-9 h-9 transition-all duration-300 ${star <= (hovered || selected)
? 'text-yellow-400 fill-yellow-400 drop-shadow-[0_0_8px_rgba(250,204,21,0.4)]'
: 'text-gray-200 fill-gray-200'
}`}
/>
</button>
))}
</div>
{(hovered || selected) > 0 && (
<p className="text-sm font-semibold text-gray-700 transition-all">
{ratingLabels[hovered || selected]}
</p>
)}
</div>
{/* Optional Comment */}
<div>
<label className="block text-sm font-semibold text-gray-700 mb-2 flex items-center gap-1.5">
<MessageSquare className="w-4 h-4" />
Leave a comment <span className="text-gray-400 font-normal">(optional)</span>
</label>
<textarea
rows={3}
placeholder="What went well? What could be better?"
value={comment}
onChange={(e) => setComment(e.target.value)}
className="w-full px-4 py-3 text-sm border border-gray-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-emerald-500/20 focus:border-emerald-500 resize-none text-gray-800 placeholder:text-gray-400 transition-all"
/>
</div>
{error && (
<p className="text-red-500 text-sm font-medium">{error}</p>
)}
{/* Actions */}
<div className="flex gap-3">
<button
onClick={onDismiss}
className="flex-1 py-3 border border-gray-200 text-gray-600 text-sm font-semibold rounded-xl hover:bg-gray-50 transition-colors"
>
Skip for now
</button>
<button
onClick={handleSubmit}
disabled={loading}
className="flex-2 flex-grow py-3 bg-emerald-900 text-white text-sm font-bold rounded-xl hover:bg-emerald-800 transition-colors shadow-lg shadow-emerald-900/20 disabled:opacity-70 flex items-center justify-center gap-2"
>
{loading ? <Loader2 className="w-4 h-4 animate-spin" /> : null}
Submit Feedback
</button>
</div>
</div>
</div>
</div>
);
}