115 lines
5.7 KiB
TypeScript
115 lines
5.7 KiB
TypeScript
'use client'
|
|
|
|
import { useState } from 'react'
|
|
import { Star, Trash2, Calendar, User, MessageCircle } from 'lucide-react'
|
|
import { deleteReview } from './action-admin-review'
|
|
import { showSwal } from '@/lib/swal'
|
|
|
|
interface Review {
|
|
id: string
|
|
rating: number
|
|
ulasan: string
|
|
nama_pengulas: string
|
|
created_at: string
|
|
}
|
|
|
|
interface Props {
|
|
posyanduId: string
|
|
initialReviews: Review[]
|
|
}
|
|
|
|
export function AdminReviewList({ posyanduId, initialReviews }: Props) {
|
|
const [isDeleting, setIsDeleting] = useState<string | null>(null)
|
|
|
|
const handleDelete = async (reviewId: string) => {
|
|
const result = await showSwal.confirm(
|
|
'Hapus Ulasan?',
|
|
'Apakah Anda yakin ingin menghapus ulasan ini? Tindakan ini tidak dapat dibatalkan.'
|
|
)
|
|
|
|
if (!result.isConfirmed) return
|
|
|
|
setIsDeleting(reviewId)
|
|
try {
|
|
const res = await deleteReview(reviewId, posyanduId)
|
|
if (res.success) {
|
|
showSwal.success('Berhasil!', 'Ulasan telah dihapus.')
|
|
} else {
|
|
showSwal.error('Gagal!', res.error || 'Gagal menghapus ulasan.')
|
|
}
|
|
} catch (err) {
|
|
showSwal.error('Gagal!', 'Terjadi kesalahan sistem.')
|
|
} finally {
|
|
setIsDeleting(null)
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div className="bg-white rounded-3xl border-2 border-black shadow-[8px_8px_0px_0px_rgba(0,0,0,1)] overflow-hidden">
|
|
<div className="p-6 border-b-2 border-black bg-purple-600 flex items-center justify-between">
|
|
<h3 className="text-lg font-black text-white flex items-center gap-3 uppercase">
|
|
<MessageCircle className="w-6 h-6" />
|
|
Ulasan Masyarakat
|
|
</h3>
|
|
<span className="bg-white text-purple-600 px-3 py-1 rounded-full text-xs font-black border-2 border-black">
|
|
{initialReviews.length} TOTAL
|
|
</span>
|
|
</div>
|
|
<div className="divide-y divide-gray-100">
|
|
{initialReviews.length > 0 ? (
|
|
initialReviews.map((rev) => (
|
|
<div key={rev.id} className="p-8 hover:bg-gray-50/50 transition-all flex flex-col gap-4">
|
|
<div className="flex justify-between items-start">
|
|
<div className="flex items-center gap-4">
|
|
<div className="w-12 h-12 bg-purple-100 text-purple-600 rounded-2xl flex items-center justify-center font-black text-xl border-2 border-purple-200">
|
|
{rev.nama_pengulas?.[0] || 'A'}
|
|
</div>
|
|
<div>
|
|
<p className="font-bold text-gray-900 text-lg">{rev.nama_pengulas || 'Orang Tua'}</p>
|
|
<div className="flex items-center gap-3 mt-1">
|
|
<div className="flex gap-0.5">
|
|
{[...Array(5)].map((_, i) => (
|
|
<Star
|
|
key={i}
|
|
className={`w-3.5 h-3.5 ${i < rev.rating ? 'text-yellow-400 fill-current' : 'text-gray-200'}`}
|
|
/>
|
|
))}
|
|
</div>
|
|
<div className="w-1 h-1 bg-gray-300 rounded-full"></div>
|
|
<div className="flex items-center gap-1.5 text-[10px] text-gray-400 font-bold uppercase tracking-widest">
|
|
<Calendar className="w-3 h-3" />
|
|
{new Date(rev.created_at).toLocaleDateString('id-ID', { day: 'numeric', month: 'long', year: 'numeric' })}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<button
|
|
onClick={() => handleDelete(rev.id)}
|
|
disabled={isDeleting === rev.id}
|
|
className="p-3 text-gray-400 hover:text-red-600 hover:bg-red-50 rounded-xl transition-all border border-transparent hover:border-red-100 disabled:opacity-50"
|
|
title="Hapus Ulasan"
|
|
>
|
|
<Trash2 className={`w-5 h-5 ${isDeleting === rev.id ? 'animate-pulse' : ''}`} />
|
|
</button>
|
|
</div>
|
|
<div className="bg-gray-50 rounded-2xl p-5 border-2 border-gray-100 relative">
|
|
<p className="text-gray-700 font-semibold italic leading-relaxed text-sm">
|
|
"{rev.ulasan}"
|
|
</p>
|
|
</div>
|
|
</div>
|
|
))
|
|
) : (
|
|
<div className="p-20 text-center flex flex-col items-center gap-4 text-gray-300">
|
|
<MessageCircle className="w-16 h-16 opacity-10" />
|
|
<div className="flex flex-col gap-1">
|
|
<p className="font-black text-xl">Belum Ada Ulasan</p>
|
|
<p className="text-sm font-semibold opacity-60">Posyandu ini belum memiliki riwayat ulasan dari masyarakat.</p>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|