/opt/canhelp/apps/web/src/app/specialists
NameSizeModeActions
page.tsx108390644editdlrm
Edit: /opt/canhelp/apps/web/src/app/specialists/page.tsx (10839B)
import Link from 'next/link' import { cookies } from 'next/headers' import { getSpecialists, getCategories, getLocations } from '@/lib/api' import { getTranslations } from '@/lib/translations' import { formatShortName } from '@/lib/formatName' import { formatLastSeen } from '@/lib/formatLastSeen' import { FavoriteSpecialistButton } from '@/components/FavoriteSpecialistButton' import { SpecialistsFiltersClient } from '@/components/SpecialistsFiltersClient' import { PlanBadge } from '@/components/PlanBadge' import type { Locale } from '@/lib/translations' interface Props { searchParams: Promise> } export default async function SpecialistsPage({ searchParams }: Props) { const params = await searchParams const page = Number(params.page || 1) const cookieStore = await cookies() const locale = (cookieStore.get('canhelp_locale')?.value ?? 'el') as Locale const t = getTranslations(locale) const cookieLocation = decodeURIComponent(cookieStore.get('canhelp_location')?.value ?? '') // Location filter only applies when explicitly set in URL params, not from cookie const effectiveLocation = params.location let specialists: any[] = [] let total = 0 let catTree: any[] = [] let locationTree: any[] = [] let favoriteIds = new Set() const BASE = process.env.INTERNAL_API_URL || 'http://localhost:4000' const cookieHeader = cookieStore.getAll().map((c: any) => `${c.name}=${c.value}`).join('; ') try { const [result, cats, locs] = await Promise.all([ getSpecialists({ page: String(page), limit: '20', locale, ...params, ...(effectiveLocation ? { location: effectiveLocation } : {}) }), getCategories().catch(() => [] as any[]), getLocations().catch(() => [] as any[]), ]) console.log('[SpecialistsPage] got', result.total, 'specialists, data.length=', result.data.length) specialists = result.data total = result.total catTree = cats as any[] locationTree = locs as any[] } catch (err) { console.error('[SpecialistsPage] fetch error:', err) } if (cookieHeader) { try { const favRes = await fetch(`${BASE}/api/favorites/specialists`, { headers: { Cookie: cookieHeader }, }) if (favRes.ok) { const favData = await favRes.json() favoriteIds = new Set(favData.map((row: any) => row.specialist?.id).filter(Boolean)) } } catch {} } const totalPages = Math.ceil(total / 20) const dateLocale = locale === 'ru' ? 'ru-RU' : locale === 'uk' ? 'uk-UA' : locale === 'en' ? 'en-US' : 'el-GR' const catLocale = locale === 'en' ? 'en' : locale === 'ru' ? 'ru' : locale === 'uk' ? 'uk' : 'el' const buildUrl = (overrides: Record) => { const merged = { ...params, ...(effectiveLocation ? { location: effectiveLocation } : {}), ...overrides } const qs = Object.entries(merged) .filter(([, v]) => v !== undefined && v !== '') .map(([k, v]) => `${k}=${encodeURIComponent(v!)}`) .join('&') return `/specialists${qs ? '?' + qs : ''}` } return (
{/* Sidebar search */} {/* Specialists list */}
{(params.q || params.category || params.location || params.language) && (

{total} {t('specialists.title').toLowerCase()}

)} {specialists.length === 0 ? (

{t('specialists.empty')}

) : (
{specialists.map((user: any) => { const displayName = formatShortName(user.firstName, user.lastName, user.name) const initials = `${user.firstName?.[0] ?? ''}${user.lastName?.[0] ?? ''}`.toUpperCase() || user.name?.[0]?.toUpperCase() || '?' return (
{/* Stretched link — covers the whole card for navigation */}
{/* Avatar */}
{user.image ? ( {displayName} ) : ( initials )}

{displayName}

{user.planTier && user.planTier !== 'free' && ( )} {user.hasVerifiedBadge && user.planTier === 'free' && ( )} {Array.isArray(user.activeStatuses) && user.activeStatuses.includes('can_help_now') && ( {t('status.can_help_now')} )} {user.rating && (
{Number(user.rating).toFixed(1)}
)} {user.todayStatus === 'available' && ( {t('schedule.available')} )} {user.todayStatus === 'busy' && ( {t('schedule.busy')} )}
{/* Card titles */} {user.cards && user.cards.length > 0 && (
{user.cards.slice(0, 2).map((card: any) => ( {card.title} ))} {user.cards.length > 2 && ( +{user.cards.length - 2} )}
)} {/* Bio */}

{user.bio || t('specialists.no_bio')}

{/* Last seen */} {user.lastSeenAt && (

{formatLastSeen(user.lastSeenAt, t)}

)}
) })}
)} {/* Pagination */} {totalPages > 1 && (
{Array.from({ length: totalPages }, (_, i) => i + 1).map((p) => ( {p} ))}
)}
) }