Skip to content

Commit 471c1b1

Browse files
committed
feat: enhance GalleryShowcase with photo count and tags
- Added photo count and tags display to the GalleryShowcase component, improving the information presented to users. - Updated the FeaturedGalleriesService to fetch photo counts and popular tags for each tenant, ensuring accurate data representation. Signed-off-by: Innei <[email protected]>
1 parent 5a98b43 commit 471c1b1

File tree

2 files changed

+86
-1
lines changed

2 files changed

+86
-1
lines changed

apps/landing/src/components/landing/GalleryShowcase.tsx

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ interface FeaturedGallery {
1818
domain: string | null
1919
description: string | null
2020
author: FeaturedGalleryAuthor | null
21+
photoCount: number
22+
tags: string[]
2123
createdAt: string
2224
}
2325

@@ -206,6 +208,38 @@ export const GalleryShowcase = () => {
206208
</p>
207209
)}
208210

211+
{/* Photo Count & Tags */}
212+
<div className="mb-4 space-y-2">
213+
{gallery.photoCount > 0 && (
214+
<div className="flex items-center gap-2 text-xs text-white/60">
215+
<i className="i-lucide-image size-3.5" />
216+
<span>
217+
{gallery.photoCount}{' '}
218+
<span>
219+
{gallery.photoCount === 1 ? 'photo' : 'photos'}
220+
</span>
221+
</span>
222+
</div>
223+
)}
224+
{gallery.tags.length > 0 && (
225+
<div className="flex flex-wrap gap-1.5">
226+
{gallery.tags.slice(0, 4).map((tag) => (
227+
<span
228+
key={tag}
229+
className="rounded-full border border-white/10 bg-white/5 px-2 py-0.5 text-xs text-white/70"
230+
>
231+
{tag}
232+
</span>
233+
))}
234+
{gallery.tags.length > 4 && (
235+
<span className="rounded-full border border-white/10 bg-white/5 px-2 py-0.5 text-xs text-white/50">
236+
+{gallery.tags.length - 4}
237+
</span>
238+
)}
239+
</div>
240+
)}
241+
</div>
242+
209243
{/* Divider */}
210244
<div className="mb-4 h-px w-full bg-linear-to-r from-transparent via-white/30 to-transparent opacity-50" />
211245

be/apps/core/src/modules/platform/featured-galleries/featured-galleries.service.ts

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { authUsers, settings, tenantDomains } from '@afilmory/db'
1+
import { authUsers, photoAssets, settings, tenantDomains } from '@afilmory/db'
22
import { DbAccessor } from 'core/database/database.provider'
33
import { normalizeDate } from 'core/helpers/normalize.helper'
44
import { and, asc, eq, inArray, sql } from 'drizzle-orm'
@@ -60,6 +60,48 @@ export class FeaturedGalleriesService {
6060
.from(tenantDomains)
6161
.where(and(inArray(tenantDomains.tenantId, tenantIds), eq(tenantDomains.status, 'verified')))
6262

63+
// Fetch photo counts for all tenants (only synced/conflict photos)
64+
const photoCounts = await db
65+
.select({
66+
tenantId: photoAssets.tenantId,
67+
count: sql<number>`count(*)::int`,
68+
})
69+
.from(photoAssets)
70+
.where(and(inArray(photoAssets.tenantId, tenantIds), inArray(photoAssets.syncStatus, ['synced', 'conflict'])))
71+
.groupBy(photoAssets.tenantId)
72+
73+
// Fetch popular tags for all tenants
74+
// This query extracts tags from manifest JSONB and counts them per tenant
75+
// Process tags per tenant to ensure proper SQL parameterization
76+
const tagMap = new Map<string, string[]>()
77+
78+
for (const tenantId of tenantIds) {
79+
const tagsResult = await db.execute<{ tag: string | null; count: number | null }>(sql`
80+
select tag, count(*)::int as count
81+
from (
82+
select nullif(trim(jsonb_array_elements_text(${photoAssets.manifest}->'data'->'tags')), '') as tag
83+
from ${photoAssets}
84+
where ${photoAssets.tenantId} = ${tenantId}
85+
and ${photoAssets.syncStatus} in ('synced', 'conflict')
86+
) as tag_items
87+
where tag is not null and tag != ''
88+
group by tag
89+
order by count desc
90+
limit 5
91+
`)
92+
93+
const tags = tagsResult.rows
94+
.map((row) => {
95+
const tag = row.tag?.trim()
96+
return tag && tag.length > 0 ? tag : null
97+
})
98+
.filter((tag): tag is string => tag !== null)
99+
100+
if (tags.length > 0) {
101+
tagMap.set(tenantId, tags)
102+
}
103+
}
104+
63105
// Build maps for quick lookup
64106
const settingsMap = new Map<string, Map<string, string | null>>()
65107
for (const setting of siteSettings) {
@@ -87,12 +129,19 @@ export class FeaturedGalleriesService {
87129
}
88130
}
89131

132+
const photoCountMap = new Map<string, number>()
133+
for (const count of photoCounts) {
134+
photoCountMap.set(count.tenantId, Number(count.count ?? 0))
135+
}
136+
90137
// Build response
91138
const featuredGalleries = validTenants.map((aggregate) => {
92139
const { tenant } = aggregate
93140
const tenantSettings = settingsMap.get(tenant.id) ?? new Map()
94141
const author = authorMap.get(tenant.id)
95142
const domain = domainMap.get(tenant.id)
143+
const photoCount = photoCountMap.get(tenant.id) ?? 0
144+
const tags = tagMap.get(tenant.id) ?? []
96145

97146
return {
98147
id: tenant.id,
@@ -106,6 +155,8 @@ export class FeaturedGalleriesService {
106155
avatar: author.avatar,
107156
}
108157
: null,
158+
photoCount,
159+
tags,
109160
createdAt: normalizeDate(tenant.createdAt) ?? tenant.createdAt,
110161
}
111162
})

0 commit comments

Comments
 (0)