import { useEffect, useState } from 'react' import { Check, Star, ThumbsUp, Trash2, XCircle } from 'lucide-react' import { useLocale } from '@meshkee/dashboard-ui' import { formatCommentDate } from '../data/productComments' import { useT } from '../i18n/useT' import { ApiError } from '../lib/api' import { deleteComment, listProductComments, updateCommentApproval, } from '../services/commentService' import { deleteExpertReview, formatReviewDate, listProductExpertReviews, updateExpertReviewApproval, } from '../services/expertReviewService' import { getProductTechnicalInfo } from '../services/productTechnicalInfoService' import type { ProductComment } from '../types/comment' import type { ProductExpertReview } from '../types/expertReview' import type { ProductTechnicalValue } from '../types/technicalForm' import styles from './ProductDetailsTabs.module.css' type DetailTab = 'technical' | 'comments' | 'reviews' type TechnicalEmptyReason = 'no-category' | 'no-form' | 'no-data' interface ProductDetailsTabsProps { productId: string commentCount: number } function formatTechnicalValue(item: ProductTechnicalValue): string { if (item.type === 'multi_select') { const labels = item.optionLabels ?? (Array.isArray(item.value) ? item.value : []) return labels.length ? labels.join(', ') : '—' } if (item.type === 'multi_value') { const items = Array.isArray(item.value) ? item.value.map((entry) => String(entry).trim()).filter(Boolean) : [] return items.length ? items.join('، ') : '—' } if (item.type === 'select') { return item.optionLabel ?? (item.value ? String(item.value) : '—') } return item.value ? String(item.value) : '—' } export function ProductDetailsTabs({ productId, commentCount }: ProductDetailsTabsProps) { const t = useT() const { locale } = useLocale() const dateLocale = locale === 'fa' ? 'fa' : 'en' const [activeTab, setActiveTab] = useState('technical') const [technicalValues, setTechnicalValues] = useState([]) const [technicalEmptyReason, setTechnicalEmptyReason] = useState( null, ) const [technicalLoading, setTechnicalLoading] = useState(false) const [technicalError, setTechnicalError] = useState('') const [comments, setComments] = useState([]) const [commentsLoading, setCommentsLoading] = useState(false) const [commentsError, setCommentsError] = useState('') const [reviews, setReviews] = useState([]) const [reviewsLoading, setReviewsLoading] = useState(false) const [reviewsError, setReviewsError] = useState('') const [actionId, setActionId] = useState(null) useEffect(() => { if (activeTab !== 'technical') return const controller = new AbortController() void loadTechnicalInfo(controller.signal) return () => controller.abort() }, [activeTab, productId]) useEffect(() => { if (activeTab !== 'comments') return const controller = new AbortController() void loadComments(controller.signal) return () => controller.abort() }, [activeTab, productId]) useEffect(() => { if (activeTab !== 'reviews') return const controller = new AbortController() void loadReviews(controller.signal) return () => controller.abort() }, [activeTab, productId]) async function loadComments(signal?: AbortSignal) { setCommentsLoading(true) setCommentsError('') try { const data = await listProductComments(productId, 1, 100, signal) setComments(data.items) } catch (err) { if (err instanceof DOMException && err.name === 'AbortError') return if (err instanceof ApiError) { setCommentsError(err.message) } else { setCommentsError(t('products.details.comments.errorLoad')) } } finally { setCommentsLoading(false) } } async function loadReviews(signal?: AbortSignal) { setReviewsLoading(true) setReviewsError('') try { const data = await listProductExpertReviews(productId, 1, 100, signal) setReviews(data.items) } catch (err) { if (err instanceof DOMException && err.name === 'AbortError') return if (err instanceof ApiError) { setReviewsError(err.message) } else { setReviewsError(t('products.details.reviews.errorLoad')) } } finally { setReviewsLoading(false) } } async function loadTechnicalInfo(signal?: AbortSignal) { setTechnicalLoading(true) setTechnicalError('') setTechnicalEmptyReason(null) try { const data = await getProductTechnicalInfo(productId, signal) setTechnicalValues(data.values) if (data.message === 'Product has no category assigned') { setTechnicalEmptyReason('no-category') } else if (!data.form) { setTechnicalEmptyReason('no-form') } else if (!data.values.some((item) => formatTechnicalValue(item) !== '—')) { setTechnicalEmptyReason('no-data') } } catch (err) { if (err instanceof DOMException && err.name === 'AbortError') return if (err instanceof ApiError) { setTechnicalError(err.message) } else { setTechnicalError(t('products.details.technical.errorLoad')) } } finally { setTechnicalLoading(false) } } async function toggleCommentApproval(comment: ProductComment) { setActionId(comment.id) setCommentsError('') try { const updated = await updateCommentApproval(comment.id, !comment.approved) setComments((prev) => prev.map((item) => (item.id === comment.id ? updated : item))) } catch (err) { if (err instanceof ApiError) { setCommentsError(err.message) } else { setCommentsError(t('products.details.comments.errorApprove')) } } finally { setActionId(null) } } async function removeComment(commentId: string) { setActionId(commentId) setCommentsError('') try { await deleteComment(commentId) setComments((prev) => prev.filter((item) => item.id !== commentId)) } catch (err) { if (err instanceof ApiError) { setCommentsError(err.message) } else { setCommentsError(t('products.details.comments.errorDelete')) } } finally { setActionId(null) } } async function toggleReviewApproval(review: ProductExpertReview) { setActionId(review.id) setReviewsError('') try { const updated = await updateExpertReviewApproval(review.id, !review.approved) setReviews((prev) => prev.map((item) => (item.id === review.id ? updated : item))) } catch (err) { if (err instanceof ApiError) { setReviewsError(err.message) } else { setReviewsError(t('products.details.reviews.errorApprove')) } } finally { setActionId(null) } } async function removeReview(reviewId: string) { setActionId(reviewId) setReviewsError('') try { await deleteExpertReview(reviewId) setReviews((prev) => prev.filter((item) => item.id !== reviewId)) } catch (err) { if (err instanceof ApiError) { setReviewsError(err.message) } else { setReviewsError(t('products.details.reviews.errorDelete')) } } finally { setActionId(null) } } const displayedCommentCount = comments.length > 0 ? comments.length : commentCount const technicalEmptyMessage = technicalEmptyReason === 'no-category' ? t('products.details.technical.noCategory') : technicalEmptyReason === 'no-form' ? t('products.details.technical.noForm') : technicalEmptyReason === 'no-data' ? t('products.details.technical.noData') : '' return (
{activeTab === 'technical' && ( <> {technicalLoading ? (

{t('products.details.technical.loading')}

) : technicalError ? (

{technicalError}

) : technicalEmptyMessage && technicalValues.length === 0 ? (

{technicalEmptyMessage}

) : technicalValues.length === 0 ? (

{t('products.details.technical.empty')}

) : (
{technicalValues.map((item) => (
{item.fieldLabel}
{formatTechnicalValue(item)}
))}
)} {technicalEmptyMessage && technicalValues.length > 0 && (

{technicalEmptyMessage}

)} )} {activeTab === 'comments' && ( <> {commentsError &&

{commentsError}

} {commentsLoading ? (

{t('products.details.comments.loading')}

) : comments.length === 0 ? (

{t('products.details.comments.empty')}

) : (
{comments.map((comment) => (
{comment.author}
{formatCommentDate(comment.createdAt, dateLocale)}
{comment.likesCount}

{comment.text}

{!comment.approved && ( {t('products.details.pending')} )}
{comment.approved ? ( ) : ( )}
))}
)} )} {activeTab === 'reviews' && ( <> {reviewsError &&

{reviewsError}

} {reviewsLoading ? (

{t('products.details.reviews.loading')}

) : reviews.length === 0 ? (

{t('products.details.reviews.empty')}

) : (
{reviews.map((review) => (
{review.authorName}
{formatReviewDate(review.createdAt, dateLocale)}
{review.rate}/10
{(review.positivePoints.length > 0 || review.negativePoints.length > 0) && (
{review.positivePoints.length > 0 && (

{t('products.details.positivePoints')}

    {review.positivePoints.map((point) => (
  • {point}
  • ))}
)} {review.negativePoints.length > 0 && (

{t('products.details.negativePoints')}

    {review.negativePoints.map((point) => (
  • {point}
  • ))}
)}
)}

{review.text}

{!review.approved && ( {t('products.details.pending')} )}
{review.approved ? ( ) : ( )}
))}
)} )}
) }