feat: real user avatars in reviews, conditional product link
This commit is contained in:
@@ -26,8 +26,15 @@ export type PublicReviewFeedItem = {
|
|||||||
imageUrl: string | null
|
imageUrl: string | null
|
||||||
createdAt: string
|
createdAt: string
|
||||||
authorDisplay: string
|
authorDisplay: string
|
||||||
productId: string
|
authorAvatar?: string | null
|
||||||
productTitle: string
|
authorAvatarType?: string | null
|
||||||
|
authorAvatarStyle?: string | null
|
||||||
|
product: {
|
||||||
|
id: string
|
||||||
|
title: string
|
||||||
|
published: boolean
|
||||||
|
slug: string
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PublicReviewsLatestResponse = {
|
export type PublicReviewsLatestResponse = {
|
||||||
@@ -48,6 +55,9 @@ export type PublicProductReviewItem = {
|
|||||||
imageUrl: string | null
|
imageUrl: string | null
|
||||||
createdAt: string
|
createdAt: string
|
||||||
authorDisplay: string
|
authorDisplay: string
|
||||||
|
authorAvatar?: string | null
|
||||||
|
authorAvatarType?: string | null
|
||||||
|
authorAvatarStyle?: string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PublicProductReviewsResponse = {
|
export type PublicProductReviewsResponse = {
|
||||||
|
|||||||
@@ -19,7 +19,13 @@ function ReviewItem({ rv }: { rv: PublicProductReviewItem }) {
|
|||||||
<Paper variant="outlined" sx={{ p: 1.5, borderRadius: 2 }}>
|
<Paper variant="outlined" sx={{ p: 1.5, borderRadius: 2 }}>
|
||||||
<Stack spacing={0.75}>
|
<Stack spacing={0.75}>
|
||||||
<Stack direction="row" spacing={1.5} sx={{ alignItems: 'center' }}>
|
<Stack direction="row" spacing={1.5} sx={{ alignItems: 'center' }}>
|
||||||
<UserAvatar userId={rv.authorDisplay} avatarUrl={null} avatarType={null} avatarStyle={null} size={32} />
|
<UserAvatar
|
||||||
|
userId={rv.authorDisplay}
|
||||||
|
avatarUrl={rv.authorAvatar}
|
||||||
|
avatarType={rv.authorAvatarType}
|
||||||
|
avatarStyle={rv.authorAvatarStyle}
|
||||||
|
size={32}
|
||||||
|
/>
|
||||||
<Box sx={{ flexGrow: 1 }}>
|
<Box sx={{ flexGrow: 1 }}>
|
||||||
<Typography sx={{ fontWeight: 700 }}>{rv.authorDisplay}</Typography>
|
<Typography sx={{ fontWeight: 700 }}>{rv.authorDisplay}</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -103,9 +103,9 @@ export function ReviewsBlock() {
|
|||||||
<Stack direction="row" spacing={1.5} sx={{ minWidth: { sm: 200 }, alignItems: 'center' }}>
|
<Stack direction="row" spacing={1.5} sx={{ minWidth: { sm: 200 }, alignItems: 'center' }}>
|
||||||
<UserAvatar
|
<UserAvatar
|
||||||
userId={r.authorDisplay}
|
userId={r.authorDisplay}
|
||||||
avatarUrl={null}
|
avatarUrl={r.authorAvatar}
|
||||||
avatarType={null}
|
avatarType={r.authorAvatarType}
|
||||||
avatarStyle={null}
|
avatarStyle={r.authorAvatarStyle}
|
||||||
size={40}
|
size={40}
|
||||||
/>
|
/>
|
||||||
<Box>
|
<Box>
|
||||||
@@ -122,20 +122,26 @@ export function ReviewsBlock() {
|
|||||||
{formatReviewDate(r.createdAt)}
|
{formatReviewDate(r.createdAt)}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Stack>
|
</Stack>
|
||||||
<Typography
|
{r.product.published ? (
|
||||||
variant="caption"
|
<Typography
|
||||||
component={RouterLink}
|
variant="caption"
|
||||||
to={`/products/${r.productId}`}
|
component={RouterLink}
|
||||||
sx={{
|
to={`/products/${r.product.slug || r.product.id}`}
|
||||||
display: 'block',
|
sx={{
|
||||||
mt: 0.25,
|
display: 'block',
|
||||||
color: 'primary.main',
|
mt: 0.25,
|
||||||
textDecoration: 'none',
|
color: 'primary.main',
|
||||||
'&:hover': { textDecoration: 'underline' },
|
textDecoration: 'none',
|
||||||
}}
|
'&:hover': { textDecoration: 'underline' },
|
||||||
>
|
}}
|
||||||
{r.productTitle}
|
>
|
||||||
</Typography>
|
{r.product.title}
|
||||||
|
</Typography>
|
||||||
|
) : (
|
||||||
|
<Typography variant="caption" color="text.secondary" sx={{ display: 'block', mt: 0.25 }}>
|
||||||
|
{r.product.title}
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
|
|||||||
@@ -40,8 +40,8 @@ export async function registerPublicReviewRoutes(fastify) {
|
|||||||
const rows = await prisma.review.findMany({
|
const rows = await prisma.review.findMany({
|
||||||
where: { status: 'approved', product: { published: true } },
|
where: { status: 'approved', product: { published: true } },
|
||||||
include: {
|
include: {
|
||||||
user: { select: { email: true, displayName: true } },
|
user: { select: { email: true, displayName: true, avatar: true, avatarType: true, avatarStyle: true } },
|
||||||
product: { select: { id: true, title: true } },
|
product: { select: { id: true, title: true, published: true, slug: true } },
|
||||||
},
|
},
|
||||||
orderBy: { createdAt: 'desc' },
|
orderBy: { createdAt: 'desc' },
|
||||||
take,
|
take,
|
||||||
@@ -54,8 +54,15 @@ export async function registerPublicReviewRoutes(fastify) {
|
|||||||
imageUrl: r.imageUrl,
|
imageUrl: r.imageUrl,
|
||||||
createdAt: r.createdAt,
|
createdAt: r.createdAt,
|
||||||
authorDisplay: publicReviewAuthorDisplay(r.user),
|
authorDisplay: publicReviewAuthorDisplay(r.user),
|
||||||
productId: r.productId,
|
authorAvatar: r.user?.avatar ?? null,
|
||||||
productTitle: r.product?.title ?? '',
|
authorAvatarType: r.user?.avatarType ?? null,
|
||||||
|
authorAvatarStyle: r.user?.avatarStyle ?? null,
|
||||||
|
product: {
|
||||||
|
id: r.product?.id ?? r.productId,
|
||||||
|
title: r.product?.title ?? '',
|
||||||
|
published: r.product?.published ?? false,
|
||||||
|
slug: r.product?.slug ?? '',
|
||||||
|
},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
return { items }
|
return { items }
|
||||||
@@ -80,7 +87,7 @@ export async function registerPublicReviewRoutes(fastify) {
|
|||||||
const total = await prisma.review.count({ where })
|
const total = await prisma.review.count({ where })
|
||||||
const rawItems = await prisma.review.findMany({
|
const rawItems = await prisma.review.findMany({
|
||||||
where,
|
where,
|
||||||
include: { user: { select: { email: true, displayName: true } } },
|
include: { user: { select: { email: true, displayName: true, avatar: true, avatarType: true, avatarStyle: true } } },
|
||||||
orderBy: { createdAt: 'desc' },
|
orderBy: { createdAt: 'desc' },
|
||||||
skip: (page - 1) * pageSize,
|
skip: (page - 1) * pageSize,
|
||||||
take: pageSize,
|
take: pageSize,
|
||||||
@@ -93,6 +100,9 @@ export async function registerPublicReviewRoutes(fastify) {
|
|||||||
imageUrl: r.imageUrl,
|
imageUrl: r.imageUrl,
|
||||||
createdAt: r.createdAt,
|
createdAt: r.createdAt,
|
||||||
authorDisplay: publicReviewAuthorDisplay(r.user),
|
authorDisplay: publicReviewAuthorDisplay(r.user),
|
||||||
|
authorAvatar: r.user?.avatar ?? null,
|
||||||
|
authorAvatarType: r.user?.avatarType ?? null,
|
||||||
|
authorAvatarStyle: r.user?.avatarStyle ?? null,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
return { items, total, page, pageSize }
|
return { items, total, page, pageSize }
|
||||||
|
|||||||
Reference in New Issue
Block a user