- Replaced `LIST_PUBLIC_CATEGORIES` with `LIST_ADMIN_CATEGORIES_FLAT` in `ArticleFormModal.tsx`. - Removed the "Add Event" button from the dashboard page. - Simplified the `EventsPage` by removing the button and adjusting the layout. - Cleaned up the `AdminEventEditPage` by removing unnecessary props. - Deleted unused layout and page files related to event creation. - Updated `AdminAuthContent` to use `useAdminCitiesQuery` instead of `useDiscoveryCitiesQuery`. - Refactored `EventGuestListAccessPanel` to remove the `accessMode` prop and adjust API calls accordingly. - Removed several unused components and tests related to event creation, enhancing project maintainability.
137 lines
3.8 KiB
TypeScript
137 lines
3.8 KiB
TypeScript
'use client'
|
|
|
|
import { useCallback, useEffect, useState } from 'react'
|
|
|
|
import Button from '@/components/formElements/Button'
|
|
import AdminState from '@/components/feedback/AdminState'
|
|
import ReviewCard from '@/components/reviews/ReviewCard'
|
|
import StatusChip from '@/components/ui/StatusChip'
|
|
import { getReviewStatus } from '@/constants/status'
|
|
import {
|
|
EVENT_REVIEWS_PAGE_SIZE,
|
|
LIST_ADMIN_EVENT_REVIEWS_PAGE,
|
|
type AdminEventReview,
|
|
} from '@/services/reviews'
|
|
import { texts } from '@/texts'
|
|
|
|
interface EventReviewsPanelProps {
|
|
eventId: string
|
|
}
|
|
|
|
const authorDisplayName = (review: AdminEventReview) => {
|
|
const name = [review.user?.firstName, review.user?.lastName].filter(Boolean).join(' ').trim()
|
|
|
|
return name || texts.events.guest
|
|
}
|
|
|
|
/** Read-only admin reviews for one event (includes hidden/deleted). */
|
|
const EventReviewsPanel = ({ eventId }: EventReviewsPanelProps) => {
|
|
const [reviews, setReviews] = useState<AdminEventReview[]>([])
|
|
const [isLoading, setIsLoading] = useState(true)
|
|
const [isLoadingMore, setIsLoadingMore] = useState(false)
|
|
const [page, setPage] = useState(1)
|
|
const [totalItems, setTotalItems] = useState(0)
|
|
|
|
const fetchPage = useCallback(
|
|
async (nextPage: number, options?: { errorMode: 'silent' }) =>
|
|
LIST_ADMIN_EVENT_REVIEWS_PAGE(eventId, nextPage, EVENT_REVIEWS_PAGE_SIZE, options),
|
|
[eventId]
|
|
)
|
|
|
|
const load = useCallback(async () => {
|
|
setIsLoading(true)
|
|
const result = await fetchPage(1)
|
|
|
|
if (result.ok) {
|
|
setReviews(result.data.items)
|
|
setTotalItems(result.data.totalItemsCount)
|
|
setPage(1)
|
|
}
|
|
setIsLoading(false)
|
|
}, [fetchPage])
|
|
|
|
const loadMore = async () => {
|
|
if (isLoadingMore || reviews.length >= totalItems) return
|
|
setIsLoadingMore(true)
|
|
const nextPage = page + 1
|
|
const result = await fetchPage(nextPage, { errorMode: 'silent' })
|
|
|
|
if (result.ok) {
|
|
setReviews((current) => {
|
|
const seen = new Set(current.map((review) => review.id))
|
|
|
|
return [...current, ...result.data.items.filter((review) => !seen.has(review.id))]
|
|
})
|
|
}
|
|
setPage(nextPage)
|
|
setIsLoadingMore(false)
|
|
}
|
|
|
|
useEffect(() => {
|
|
void load()
|
|
}, [load])
|
|
|
|
if (isLoading) {
|
|
return <p className="mt-4 text-sm text-text-muted">{texts.reviews.loading}</p>
|
|
}
|
|
|
|
if (reviews.length === 0) {
|
|
return (
|
|
<div className="mt-4">
|
|
<AdminState
|
|
description={texts.reviews.hostEmptyDescription}
|
|
title={texts.reviews.hostEmptyTitle}
|
|
variant="empty"
|
|
/>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<div className="mt-4 space-y-3">
|
|
<ul className="space-y-3">
|
|
{reviews.map((review) => {
|
|
const statusPresentation = getReviewStatus(review.status)
|
|
|
|
return (
|
|
<ReviewCard
|
|
key={review.id}
|
|
authorAvatarUrl={review.user?.avatarUrl}
|
|
authorName={authorDisplayName(review)}
|
|
authorUserId={review.user?.id}
|
|
body={review.body ?? texts.reviews.ratingOnly}
|
|
className="admin-surface space-y-2 p-4"
|
|
createdAt={review.createdAt}
|
|
footer={
|
|
<div className="mt-3">
|
|
<StatusChip
|
|
chipColor={statusPresentation.chipColor}
|
|
label={statusPresentation.label}
|
|
/>
|
|
</div>
|
|
}
|
|
hostReplyBody={review.hostReplyBody}
|
|
rating={review.rating}
|
|
tone="organizer"
|
|
/>
|
|
)
|
|
})}
|
|
</ul>
|
|
|
|
{reviews.length < totalItems ? (
|
|
<div className="flex justify-center">
|
|
<Button
|
|
isLoading={isLoadingMore}
|
|
variant="flat"
|
|
onClick={() => void loadMore()}
|
|
>
|
|
{texts.reviews.loadMore}
|
|
</Button>
|
|
</div>
|
|
) : null}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default EventReviewsPanel
|