Release 2025-05-22-GmO6
This commit is contained in:
@@ -1,7 +1,12 @@
|
||||
---
|
||||
import { z } from 'astro/zod'
|
||||
import { Icon } from 'astro-icon/components'
|
||||
|
||||
import BadgeSmall from '../../components/BadgeSmall.astro'
|
||||
import CommentModeration from '../../components/CommentModeration.astro'
|
||||
import MyPicture from '../../components/MyPicture.astro'
|
||||
import TimeFormatted from '../../components/TimeFormatted.astro'
|
||||
import UserBadge from '../../components/UserBadge.astro'
|
||||
import {
|
||||
commentStatusFilters,
|
||||
commentStatusFiltersZodEnum,
|
||||
@@ -36,11 +41,18 @@ const [comments = [], totalComments = 0] = await Astro.locals.banners.try(
|
||||
prisma.comment.findManyAndCount({
|
||||
where: statusFilter.whereClause,
|
||||
include: {
|
||||
author: true,
|
||||
author: {
|
||||
select: {
|
||||
name: true,
|
||||
displayName: true,
|
||||
picture: true,
|
||||
},
|
||||
},
|
||||
service: {
|
||||
select: {
|
||||
name: true,
|
||||
slug: true,
|
||||
imageUrl: true,
|
||||
},
|
||||
},
|
||||
parent: {
|
||||
@@ -72,7 +84,7 @@ const totalPages = Math.ceil(totalComments / PAGE_SIZE)
|
||||
class={cn([
|
||||
'font-title rounded-md border px-3 py-1 text-sm',
|
||||
params.status === filter.value
|
||||
? filter.styles.filter
|
||||
? filter.classNames.filter
|
||||
: 'border-zinc-700 transition-colors hover:border-green-500/50',
|
||||
])}
|
||||
>
|
||||
@@ -98,22 +110,7 @@ const totalPages = Math.ceil(totalComments / PAGE_SIZE)
|
||||
>
|
||||
<div class="mb-4 flex flex-wrap items-center gap-2">
|
||||
{/* Author Info */}
|
||||
<span class="font-title text-sm">{comment.author.name}</span>
|
||||
{comment.author.admin && (
|
||||
<span class="rounded-sm bg-yellow-500/20 px-2 py-0.5 text-[12px] font-medium text-yellow-500">
|
||||
admin
|
||||
</span>
|
||||
)}
|
||||
{comment.author.verified && !comment.author.admin && (
|
||||
<span class="rounded-sm bg-blue-500/20 px-2 py-0.5 text-[12px] font-medium text-blue-500">
|
||||
verified
|
||||
</span>
|
||||
)}
|
||||
{comment.author.verifier && !comment.author.admin && (
|
||||
<span class="rounded-sm bg-green-500/20 px-2 py-0.5 text-[12px] font-medium text-green-500">
|
||||
verifier
|
||||
</span>
|
||||
)}
|
||||
<UserBadge user={comment.author} size="md" />
|
||||
|
||||
{/* Service Link */}
|
||||
<span class="text-xs text-zinc-500">•</span>
|
||||
@@ -121,21 +118,55 @@ const totalPages = Math.ceil(totalComments / PAGE_SIZE)
|
||||
href={`/service/${comment.service.slug}#comment-${comment.id.toString()}`}
|
||||
class="text-sm text-blue-400 transition-colors hover:text-blue-300"
|
||||
>
|
||||
{!!comment.service.imageUrl && (
|
||||
<MyPicture
|
||||
src={comment.service.imageUrl}
|
||||
height={16}
|
||||
width={16}
|
||||
class="inline-block size-4 rounded-full align-[-0.2em]"
|
||||
alt=""
|
||||
/>
|
||||
)}
|
||||
{comment.service.name}
|
||||
</a>
|
||||
|
||||
{/* Date */}
|
||||
<span class="text-xs text-zinc-500">•</span>
|
||||
<span class="text-sm text-zinc-400">{new Date(comment.createdAt).toLocaleString()}</span>
|
||||
<TimeFormatted
|
||||
date={comment.createdAt}
|
||||
hourPrecision
|
||||
caseType="sentence"
|
||||
class="text-sm text-zinc-400"
|
||||
/>
|
||||
|
||||
<span class="text-xs text-zinc-500">•</span>
|
||||
|
||||
{/* Status Badges */}
|
||||
<span class={comment.statusFilterInfo.styles.badge}>{comment.statusFilterInfo.label}</span>
|
||||
<BadgeSmall
|
||||
color={comment.statusFilterInfo.color}
|
||||
text={comment.statusFilterInfo.label}
|
||||
icon={comment.statusFilterInfo.icon}
|
||||
inlineIcon
|
||||
/>
|
||||
|
||||
<span class="text-xs text-zinc-500">•</span>
|
||||
|
||||
{/* Link to Comment */}
|
||||
<a
|
||||
href={`/service/${comment.service.slug}?showPending=true#comment-${comment.id.toString()}`}
|
||||
class="text-whit/50 flex items-center gap-1 text-sm transition-colors hover:underline"
|
||||
>
|
||||
<Icon name="ri:link" class="size-4" />
|
||||
Open
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{/* Parent Comment Context */}
|
||||
{comment.parent && (
|
||||
<div class="mb-4 border-l-2 border-zinc-700 pl-4">
|
||||
<div class="mb-1 text-sm text-zinc-500">Replying to {comment.parent.author.name}:</div>
|
||||
<div class="mb-1 text-sm opacity-50">
|
||||
Replying to <UserBadge user={comment.parent.author} size="md" />
|
||||
</div>
|
||||
<div class="text-sm text-zinc-400">{comment.parent.content}</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -66,6 +66,7 @@ const serviceSuggestion = await Astro.locals.banners.try('Error fetching service
|
||||
user: {
|
||||
select: {
|
||||
id: true,
|
||||
displayName: true,
|
||||
name: true,
|
||||
picture: true,
|
||||
},
|
||||
|
||||
@@ -6,6 +6,7 @@ import { orderBy as lodashOrderBy } from 'lodash-es'
|
||||
|
||||
import SortArrowIcon from '../../../components/SortArrowIcon.astro'
|
||||
import TimeFormatted from '../../../components/TimeFormatted.astro'
|
||||
import UserBadge from '../../../components/UserBadge.astro'
|
||||
import {
|
||||
getServiceSuggestionStatusInfo,
|
||||
serviceSuggestionStatuses,
|
||||
@@ -67,8 +68,9 @@ let suggestions = await prisma.serviceSuggestion.findMany({
|
||||
createdAt: true,
|
||||
user: {
|
||||
select: {
|
||||
id: true,
|
||||
displayName: true,
|
||||
name: true,
|
||||
picture: true,
|
||||
},
|
||||
},
|
||||
service: {
|
||||
@@ -293,9 +295,7 @@ const makeSortUrl = (slug: string) => {
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-4 py-3">
|
||||
<a href={`/admin/users/${suggestion.user.name}`} class="hover:text-green-500">
|
||||
{suggestion.user.name}
|
||||
</a>
|
||||
<UserBadge user={suggestion.user} size="md" />
|
||||
</td>
|
||||
<td class="px-4 py-3">
|
||||
<form method="POST" action={actions.admin.serviceSuggestions.update}>
|
||||
|
||||
@@ -10,6 +10,7 @@ import { Icon } from 'astro-icon/components'
|
||||
import { actions, isInputError } from 'astro:actions'
|
||||
|
||||
import MyPicture from '../../../../components/MyPicture.astro'
|
||||
import UserBadge from '../../../../components/UserBadge.astro'
|
||||
import { serviceVisibilities } from '../../../../constants/serviceVisibility'
|
||||
import BaseLayout from '../../../../layouts/BaseLayout.astro'
|
||||
import { cn } from '../../../../lib/cn'
|
||||
@@ -80,9 +81,9 @@ const service = await Astro.locals.banners.try('Error fetching service', () =>
|
||||
id: true,
|
||||
user: {
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
displayName: true,
|
||||
picture: true,
|
||||
},
|
||||
},
|
||||
createdAt: true,
|
||||
@@ -1183,7 +1184,9 @@ const buttonSmallWarningClasses = cn(
|
||||
<tbody class="divide-y divide-zinc-700/80">
|
||||
{service.verificationRequests.map((request) => (
|
||||
<tr class="transition-colors hover:bg-zinc-700/40">
|
||||
<td class="p-3 text-zinc-300">{request.user.displayName ?? request.user.name}</td>
|
||||
<td class="p-3 text-zinc-300">
|
||||
<UserBadge user={request.user} size="md" />
|
||||
</td>
|
||||
<td class="p-3 text-zinc-400">{new Date(request.createdAt).toLocaleString()}</td>
|
||||
</tr>
|
||||
))}
|
||||
|
||||
@@ -116,7 +116,7 @@ if (!user) return Astro.rewrite('/404')
|
||||
---
|
||||
|
||||
<BaseLayout
|
||||
pageTitle={`User: ${user.name}`}
|
||||
pageTitle={`${user.displayName ?? user.name} - User`}
|
||||
widthClassName="max-w-screen-lg"
|
||||
className={{ main: 'space-y-24' }}
|
||||
>
|
||||
|
||||
@@ -6,6 +6,7 @@ import { orderBy as lodashOrderBy } from 'lodash-es'
|
||||
import SortArrowIcon from '../../../components/SortArrowIcon.astro'
|
||||
import TimeFormatted from '../../../components/TimeFormatted.astro'
|
||||
import Tooltip from '../../../components/Tooltip.astro'
|
||||
import UserBadge from '../../../components/UserBadge.astro'
|
||||
import BaseLayout from '../../../layouts/BaseLayout.astro'
|
||||
import { zodParseQueryParamsStoringErrors } from '../../../lib/parseUrlFilters'
|
||||
import { pluralize } from '../../../lib/pluralize'
|
||||
@@ -74,6 +75,8 @@ const dbUsers = await prisma.user.findMany({
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
displayName: true,
|
||||
picture: true,
|
||||
verified: true,
|
||||
admin: true,
|
||||
verifier: true,
|
||||
@@ -241,10 +244,10 @@ const makeSortUrl = (slug: NonNullable<(typeof filters)['sort-by']>) => {
|
||||
class={`group hover:bg-zinc-700/30 ${user.spammer ? 'bg-red-900/10' : ''}`}
|
||||
>
|
||||
<td class="px-4 py-3 text-sm font-medium text-zinc-200">
|
||||
<div>{user.name}</div>
|
||||
<UserBadge user={user} size="md" class="flex text-white" />
|
||||
{user.internalNotes.length > 0 && (
|
||||
<Tooltip
|
||||
class="text-2xs mt-1 text-yellow-400"
|
||||
class="text-2xs font-light text-yellow-200/40"
|
||||
position="right"
|
||||
text={user.internalNotes
|
||||
.map(
|
||||
@@ -257,7 +260,7 @@ const makeSortUrl = (slug: NonNullable<(typeof filters)['sort-by']>) => {
|
||||
)
|
||||
.join('\n\n')}
|
||||
>
|
||||
<Icon name="ri:sticky-note-line" class="mr-1 inline-block size-3" />
|
||||
<Icon name="ri:sticky-note-line" class="mr-0.5 inline-block size-3" />
|
||||
{user.internalNotes.length} internal {pluralize('note', user.internalNotes.length)}
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user