/opt/canhelp/apps/web/.next/cache/webpack/server-production
Edit: /opt/canhelp/apps/web/.next/cache/webpack/server-production/22.pack (1024101B)
wpc Y webpack/lib/cache/PackFileCacheStrategyPackContentItems% ResolverCachePlugin|normal|default|alias=|false|dependencyType=|esm|modules=[|0=|node_modules|]|fallback=|false|exportsFields=[|0=|exports|]|importsFields=[|0=|imports|]|conditionNames=[|0=|node|1=|import|]|descriptionFiles=[|0=|package.json|]|extensions=[|0=|.js|1=|.json|2=|.node|]|enforceExtensions=|false|symlinks=|true|mainFields=[|0=|main|]|mainFiles=[|0=|index|]|roots=[|]|fullySpecified=|true|preferRelative=|false|preferAbsolute=|false|restrictions=[|]|path=|/opt/canhelp/apps/web|request=|/opt/canhelp/apps/web/src/app/specialists/page.tsxU ResolverCachePlugin|normal|default|alias=[|server-only$=|next/dist/compiled/server-only/empty|client-only$=|next/dist/compiled/client-only/error|next/dist/compiled/server-only$=|next/dist/compiled/server-only/empty|next/dist/compiled/client-only$=|next/dist/compiled/client-only/error|/opt/canhelp/node_modules/next/head.js=|next/dist/client/components/noop-head|/opt/canhelp/node_modules/next/image.js=|next/dist/api/image|/opt/canhelp/node_modules/next/constants.js=|next/dist/api/constants|/opt/canhelp/node_modules/next/router.js=|next/dist/api/router|/opt/canhelp/node_modules/next/dynamic.js=|next/dist/api/app-dynamic|/opt/canhelp/node_modules/next/script.js=|next/dist/api/script|/opt/canhelp/node_modules/next/link.js=|next/dist/client/app-dir/link|/opt/canhelp/node_modules/next/form.js=|next/dist/client/app-dir/form|/opt/canhelp/node_modules/next/navigation.js=|next/dist/api/navigation.react-server|/opt/canhelp/node_modules/next/headers.js=|next/dist/api/headers|/opt/canhelp/node_modules/next/og.js=|next/dist/api/og|/opt/canhelp/node_modules/next/server.js=|next/dist/api/server|/opt/canhelp/node_modules/next/document.js=|next/dist/api/document|/opt/canhelp/node_modules/next/app.js=|next/dist/api/app|next=|/opt/canhelp/node_modules/next|]|dependencyType=|esm|path=|/opt/canhelp/apps/web|request=|/opt/canhelp/apps/web/src/app/specialists/page.tsx# Compilation/modules|javascript/auto|/opt/canhelp/node_modules/next/dist/build/webpack/loaders/next-flight-loader/index.js!/opt/canhelp/node_modules/next/dist/build/webpack/loaders/next-swc-loader.js??ruleSet[1].rules[16].oneOf[4].use[0]!/opt/canhelp/apps/web/src/app/specialists/page.tsx|rsc+ FlagDependencyExportsPlugin|javascript/auto|/opt/canhelp/node_modules/next/dist/build/webpack/loaders/next-flight-loader/index.js!/opt/canhelp/node_modules/next/dist/build/webpack/loaders/next-swc-loader.js??ruleSet[1].rules[16].oneOf[4].use[0]!/opt/canhelp/apps/web/src/app/specialists/page.tsx|rscK Compilation/codeGeneration|javascript/auto|/opt/canhelp/node_modules/next/dist/build/webpack/loaders/next-flight-loader/index.js!/opt/canhelp/node_modules/next/dist/build/webpack/loaders/next-swc-loader.js??ruleSet[1].rules[16].oneOf[4].use[0]!/opt/canhelp/apps/web/src/app/specialists/page.tsx|rsc|0997d6643e94138f|webpack-runtimeCompilation/assets|chunk755MinifierWebpackPlugin|../app/specialists/page.jswebpack/lib/cache/ResolverCachePlugin`_ResolverCachePluginCacheMisscontextpathrequestqueryfragmentmoduledirectoryfileinternalfullySpecifieddescriptionFilePathdescriptionFileDatadescriptionFileRootrelativePath/opt/canhelp/apps/web/src/app/specialists/page.tsx0/opt/canhelp/apps/web/package.jsonnameversionprivatescriptsdependenciesdevDependenciesweb0.1.0devbuildstartlinttypechecknext dev --port 3000next buildnext startnext linttsc --noEmit @canhelp/shared@marsidev/react-turnstilebetter-authnanostoresnextreactreact-domsocket.io-clientzod*^1.5.0^1.2.7^1.2.0^15.1.6^19.0.0^4.8.1^3.24.1@types/node@types/react@types/react-domautoprefixerpostcsstailwindcsstypescript^22.10.7^19.0.6^19.0.3^10.4.20^8.5.1^3.4.17^5.7.3/opt/canhelp/apps/web./src/app/specialists/page.tsxwebpack/lib/FileSystemInfoSnapshot@ yB yB
/opt/canhelp/apps/web/src/app/specialists/opt/canhelp/apps/web/src/app/opt/canhelp/apps/web/src/opt/canhelp/apps/opt/canhelp/opt/safeTimetimestamphash! eyB eyBd659fc12c42d40f9! yB yBefd2df26ae5e3ca0 @ /opt/canhelp/apps/web/src/app/specialists/package.json/opt/canhelp/apps/web/src/app/package.json/opt/canhelp/apps/web/src/package.jsonissuerissuerLayercompilerrscserver @ yB`/opt/canhelp/apps/web/opt/canhelp/apps/web/src/app/package.json/opt/canhelp/apps/web/opt/canhelp/apps/web/src/package.json/opt/canhelp/apps/web/opt/canhelp/apps/web/package.json/opt/canhelp/apps/web/opt/canhelp/apps/package.json/opt/canhelp/apps/web/opt/canhelp/package.json/opt/canhelp/apps/web/opt/package.json/opt/canhelp/apps/web/opt/canhelp/apps/web/src/app/specialists/page.tsx/opt/canhelp/apps/web/opt/canhelp/apps/web/src/app/specialists/page.tsx.js/opt/canhelp/apps/web/opt/canhelp/apps/web/src/app/specialists/page.tsx.mjs/opt/canhelp/apps/web/opt/canhelp/apps/web/src/app/specialists/page.tsx.tsx/opt/canhelp/apps/web/opt/canhelp/apps/web/src/app/specialists/package.json/opt/canhelp/apps/web/opt/canhelp/apps/web/src/app/specialists/page.tsx.ts/opt/canhelp/apps/web/opt/canhelp/apps/web/src/app/specialists/page.tsx.jsx/opt/canhelp/apps/web/opt/canhelp/apps/web/src/app/specialists/page.tsx.json/opt/canhelp/apps/web/opt/canhelp/apps/web/src/app/specialists/page.tsx.wasm webpack/lib/NormalModulewebpack/lib/util/registerExternalSerializerwebpack-sources/RawSourceWC import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
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';
export default async function SpecialistsPage({ searchParams }) {
const params = await searchParams;
const page = Number(params.page || 1);
const cookieStore = await cookies();
const locale = cookieStore.get('canhelp_locale')?.value ?? 'el';
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 = [];
let total = 0;
let catTree = [];
let locationTree = [];
let favoriteIds = new Set();
const BASE = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:4000';
const cookieHeader = cookieStore.getAll().map((c)=>`${c.name}=${c.value}`).join('; ');
try {
const [result, cats, locs] = await Promise.all([
getSpecialists({
page: String(page),
limit: '20',
...params,
...effectiveLocation ? {
location: effectiveLocation
} : {}
}),
getCategories().catch(()=>[]),
getLocations().catch(()=>[])
]);
console.log('[SpecialistsPage] got', result.total, 'specialists, data.length=', result.data.length);
specialists = result.data;
total = result.total;
catTree = cats;
locationTree = locs;
} 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)=>row.specialist?.id).filter(Boolean));
}
} catch {}
}
const totalPages = Math.ceil(total / 20);
const dateLocale = locale === 'ru' ? 'ru-RU' : locale === 'en' ? 'en-US' : 'el-GR';
const catLocale = locale === 'en' ? 'en' : locale === 'ru' ? 'ru' : 'el';
const buildUrl = (overrides)=>{
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 /*#__PURE__*/ _jsx("div", {
className: "max-w-7xl mx-auto px-4 py-8",
children: /*#__PURE__*/ _jsxs("div", {
className: "flex flex-col md:flex-row gap-8",
children: [
/*#__PURE__*/ _jsx("aside", {
className: "md:w-64 shrink-0",
children: /*#__PURE__*/ _jsxs("div", {
className: "bg-white rounded-xl border border-gray-200 p-5 sticky top-24",
children: [
/*#__PURE__*/ _jsx("h3", {
className: "font-semibold text-gray-900 mb-4",
children: t('tasks.search')
}),
/*#__PURE__*/ _jsx(SpecialistsFiltersClient, {
catTree: catTree,
locations: locationTree,
initialQ: params.q,
initialCategory: params.category,
initialLocation: params.location,
initialLanguages: params.language ? params.language.split(',').filter(Boolean) : []
})
]
})
}),
/*#__PURE__*/ _jsxs("div", {
className: "flex-1",
children: [
(params.q || params.category || params.location || params.language) && /*#__PURE__*/ _jsx("div", {
className: "mb-6",
children: /*#__PURE__*/ _jsxs("h1", {
className: "text-xl font-bold text-white",
children: [
total,
" ",
t('specialists.title').toLowerCase()
]
})
}),
specialists.length === 0 ? /*#__PURE__*/ _jsx("div", {
className: "text-center py-20 text-white/70",
children: /*#__PURE__*/ _jsx("p", {
className: "text-lg",
children: t('specialists.empty')
})
}) : /*#__PURE__*/ _jsx("div", {
className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-3",
children: specialists.map((user)=>{
const displayName = formatShortName(user.firstName, user.lastName, user.name);
const initials = `${user.firstName?.[0] ?? ''}${user.lastName?.[0] ?? ''}`.toUpperCase() || user.name?.[0]?.toUpperCase() || '?';
return /*#__PURE__*/ _jsxs("div", {
className: "relative bg-white rounded-xl border border-gray-200 p-5 hover:shadow-md hover:border-blue-300 transition flex flex-col",
children: [
/*#__PURE__*/ _jsx(Link, {
href: `/users/${user.id}`,
className: "absolute inset-0 rounded-xl",
"aria-label": displayName
}),
/*#__PURE__*/ _jsx(FavoriteSpecialistButton, {
specialistId: user.id,
initialFavorite: favoriteIds.has(user.id),
favoriteLabel: t('specialist.favorite'),
unfavoriteLabel: t('specialist.unfavorite')
}),
/*#__PURE__*/ _jsxs("div", {
className: "flex items-center gap-3 mb-3",
children: [
/*#__PURE__*/ _jsx("div", {
className: "w-12 h-12 rounded-full bg-blue-100 flex-shrink-0 flex items-center justify-center text-blue-700 font-bold text-sm overflow-hidden border border-gray-100",
children: user.image ? /*#__PURE__*/ _jsx("img", {
src: user.image,
alt: displayName,
className: "w-full h-full object-cover"
}) : initials
}),
/*#__PURE__*/ _jsxs("div", {
className: "min-w-0 flex-1",
children: [
/*#__PURE__*/ _jsx("h2", {
className: "font-semibold text-gray-900 truncate",
children: displayName
}),
/*#__PURE__*/ _jsxs("div", {
className: "flex items-center gap-2 flex-wrap",
children: [
user.planTier && user.planTier !== 'free' && /*#__PURE__*/ _jsx(PlanBadge, {
tier: user.planTier,
verified: user.hasVerifiedBadge,
size: "sm"
}),
user.hasVerifiedBadge && user.planTier === 'free' && /*#__PURE__*/ _jsx(PlanBadge, {
tier: "free",
verified: true,
size: "sm"
}),
Array.isArray(user.activeStatuses) && user.activeStatuses.includes('can_help_now') && /*#__PURE__*/ _jsxs("span", {
className: "inline-flex items-center gap-1 text-xs font-medium text-green-700 bg-green-50 border border-green-200 px-2 py-0.5 rounded-full",
children: [
/*#__PURE__*/ _jsx("span", {
className: "w-1.5 h-1.5 rounded-full bg-green-500 animate-pulse"
}),
t('status.can_help_now')
]
}),
user.rating && /*#__PURE__*/ _jsxs("div", {
className: "flex items-center gap-1",
children: [
/*#__PURE__*/ _jsx("span", {
className: "text-yellow-400 text-sm",
children: "★"
}),
/*#__PURE__*/ _jsx("span", {
className: "text-sm font-medium text-gray-700",
children: Number(user.rating).toFixed(1)
})
]
}),
user.todayStatus === 'available' && /*#__PURE__*/ _jsxs("span", {
className: "inline-flex items-center gap-1 text-xs font-medium text-green-700 bg-green-100 px-2 py-0.5 rounded-full",
children: [
/*#__PURE__*/ _jsx("span", {
className: "w-1.5 h-1.5 rounded-full bg-green-500 inline-block"
}),
t('schedule.available')
]
}),
user.todayStatus === 'busy' && /*#__PURE__*/ _jsxs("span", {
className: "inline-flex items-center gap-1 text-xs font-medium text-red-700 bg-red-100 px-2 py-0.5 rounded-full",
children: [
/*#__PURE__*/ _jsx("span", {
className: "w-1.5 h-1.5 rounded-full bg-red-500 inline-block"
}),
t('schedule.busy')
]
})
]
})
]
})
]
}),
user.cards && user.cards.length > 0 && /*#__PURE__*/ _jsxs("div", {
className: "flex flex-wrap gap-1.5 mb-2",
children: [
user.cards.slice(0, 2).map((card)=>/*#__PURE__*/ _jsx("span", {
className: "text-xs bg-gray-100 text-gray-700 px-2 py-0.5 rounded-full font-medium",
children: card.title
}, card.id)),
user.cards.length > 2 && /*#__PURE__*/ _jsxs("span", {
className: "text-xs text-gray-400",
children: [
"+",
user.cards.length - 2
]
})
]
}),
/*#__PURE__*/ _jsx("p", {
className: "text-sm text-gray-500 line-clamp-2 flex-1 mt-1",
children: user.bio || t('specialists.no_bio')
}),
user.lastSeenAt && /*#__PURE__*/ _jsx("p", {
className: "text-xs text-gray-400 mt-3",
children: formatLastSeen(user.lastSeenAt, t)
})
]
}, user.id);
})
}),
totalPages > 1 && /*#__PURE__*/ _jsx("div", {
className: "flex justify-center gap-2 mt-8",
children: Array.from({
length: totalPages
}, (_, i)=>i + 1).map((p)=>/*#__PURE__*/ _jsx(Link, {
href: buildUrl({
page: String(p)
}),
className: `w-10 h-10 flex items-center justify-center rounded-lg text-sm font-medium ${p === page ? 'bg-blue-600 text-white' : 'bg-white border border-gray-200 text-gray-700 hover:border-blue-400'}`,
children: p
}, p))
})
]
})
]
})
});
}
exportsTypesideEffectFreenamespace
javascript/autoaliasmainFieldsconditionNames`$server-only$client-only$next/dist/compiled/server-only$next/dist/compiled/client-only$/opt/canhelp/node_modules/next/head.js/opt/canhelp/node_modules/next/image.js/opt/canhelp/node_modules/next/constants.js/opt/canhelp/node_modules/next/router.js/opt/canhelp/node_modules/next/dynamic.js/opt/canhelp/node_modules/next/script.js/opt/canhelp/node_modules/next/link.js/opt/canhelp/node_modules/next/form.js/opt/canhelp/node_modules/next/navigation.js/opt/canhelp/node_modules/next/headers.js/opt/canhelp/node_modules/next/og.js/opt/canhelp/node_modules/next/server.js/opt/canhelp/node_modules/next/document.js/opt/canhelp/node_modules/next/app.jsreact$react/compiler-runtime$react/jsx-dev-runtime$react/jsx-runtime$react-dom$react-dom/client$react-dom/server$react-dom/server.browser$react-dom/server.edge$react-dom/static$react-dom/static.browser$react-dom/static.edge$react-server-dom-webpack/client$react-server-dom-webpack/server$react-server-dom-webpack/server.node$react-server-dom-webpack/static$@vercel/turbopack-ecmascript-runtime/browser/dev/hmr-client/hmr-client.tsqnext/dist/compiled/server-only/emptynext/dist/compiled/client-only/errornext/dist/client/components/noop-headnext/dist/api/imagenext/dist/api/constantsnext/dist/api/routernext/dist/api/app-dynamicnext/dist/api/scriptnext/dist/client/app-dir/linknext/dist/client/app-dir/formnext/dist/api/navigation.react-servernext/dist/api/headersnext/dist/api/ognext/dist/api/servernext/dist/api/documentnext/dist/api/appnext/dist/server/route-modules/app-page/vendored/rsc/reactnext/dist/server/route-modules/app-page/vendored/rsc/react-compiler-runtimenext/dist/server/route-modules/app-page/vendored/rsc/react-jsx-dev-runtimenext/dist/server/route-modules/app-page/vendored/rsc/react-jsx-runtimenext/dist/server/route-modules/app-page/vendored/rsc/react-domnext/dist/compiled/react-dom/clientnext/dist/compiled/react-dom/server.nodenext/dist/compiled/react-dom/server.browsernext/dist/build/webpack/alias/react-dom-server.jsnext/dist/compiled/react-dom/static.nodenext/dist/compiled/react-dom/static.browsernext/dist/compiled/react-dom/static.edgenext/dist/compiled/react-server-dom-webpack/client.nodenext/dist/server/route-modules/app-page/vendored/rsc/react-server-dom-webpack-servernext/dist/server/route-modules/app-page/vendored/rsc/react-server-dom-webpack-staticnext/dist/client/dev/noop-turbopack-hmr/opt/canhelp/node_modules/next(mainreact-server...`cacheableparsedfileDependenciescontextDependenciesmissingDependenciesbuildDependenciesvalueDependenciesmassetsassetsInfostrictexportsArgumenttopLevelDeclarationssnapshotwebpack/lib/util/LazySet/opt/canhelp/node_modules/next/dist/build/webpack/loaders/next-flight-loader/index.js/opt/canhelp/node_modules/next/dist/build/webpack/loaders/next-swc-loader.js webpack/DefinePlugin_hashwebpack/DefinePlugin process.env.NEXT_PUBLIC_API_URL4028310f"https://api.canhelp.gr"bae806072a871d8atypeactionIdsclientRefsclientEntryTypeisClientReft
__webpack_exports__*default*SpecialistsPage yB
V`webpack/lib/dependencies/HarmonyCompatibilityDependency
d webpack/lib/dependencies/ConstDependency `?
`?a@]
``^@
`'A
`HA
`5A 8
`3A9 o
`6Ap
`QA
`QA G
`3webpack/lib/dependencies/HarmonyExportHeaderDependency AW TC AH TC
a @ A
c00 A "
cK `?webpack/lib/dependencies/HarmonyImportSideEffectDependencyreact/jsx-runtime
`?next/link
`next/headers
`'@/lib/api
`H@/lib/translations
`5@/lib/formatName
`3@/lib/formatLastSeen
`6@/components/FavoriteSpecialistButton
`Q @/components/SpecialistsFiltersClient
`Q
@/components/PlanBadge
`3webpack/lib/dependencies/HarmonyExportSpecifierDependency`ʇdefault
a @ webpack/lib/dependencies/HarmonyImportSpecifierDependencyacookiesA
c%getTranslationsAX g
cgetSpecialistsA
cgetCategoriesA
c%%getLocationsA
c&&jsx_jsxA
cKKjsxs_jsxsA
cM M%A~
cPP"A
cR,R1A
cU*U.A
cY*Y.SpecialistsFiltersClientA 4
cY/YGA
cdd#A
cgmgqA
ci4i9A
crArEAK O
ct4t8A
cx+x/formatShortNameA
c{4{CA
c}5}:A !
@ `6@ `:LinkA" & 4
@ `;@ `?Ao s
@ `6@ `:FavoriteSpecialistButtonAt 4
@ `;@ `SA9 > yy
@ `6@ `;A uu
@ `>@ `BAW [ qq
@ `Y@ `]A mm
@ `>@ `CA ii
@ `F@ `JAB" G" ee
@ `F@ `KA# # {aa
@ `{@ `PlanBadgeA# # 4v
nn
C Af% j% rXX
C Ak% t% 4o
gg
C AU' Z' kQQ
C A( ( gMM
@ `V@ `ZA+ + cII
@ `]@ `bxyA+, /, _EE
@ `V@ `ZtuA- - [AA
@ `V@ `ZvwA/ / W==
@ `r@ `wlmA_1 c1 S99
@ `V@ `ZnoA3 3 O55
@ `m@ `rdeA 5
5 K11
@ `V@ `ZfgAk8 p8 G--
@ `]@ `b\]Ai9 m9 C))
@ `a@ `e^_A: : ?%%
@ `W@ `\TUA= = ;!!
@ `6@ `:PQA[> _> 7
@ `I@ `MformatLastSeenA> ? 2$$
@ `6@ `DGHA@
@ .
@ `8@ `<CDAA A *
@ `E@ `IA
A A 4'
@ `J@ `N webpack/lib/ModuleGraphRestoreProvidedData
providedcanMangleProvideterminalBindingexportsInfo
sourcesdataruntimeRequirementsjavascriptwebpack/lib/util/registerExternalSerializerwebpack-sources/CachedSource K webpack/lib/util/registerExternalSerializerwebpack-sources/ConcatSourcewebpack/lib/util/registerExternalSerializerwebpack-sources/RawSource= // ESM COMPAT FLAG
__webpack_require__.r(__webpack_exports__);
// EXPORTS
__webpack_require__.d(__webpack_exports__, {
"default": () => (/* binding */ SpecialistsPage)
});
// EXTERNAL MODULE: ../../node_modules/next/dist/server/route-modules/app-page/vendored/rsc/react-jsx-runtime.js
var react_jsx_runtime = __webpack_require__(5939);
// EXTERNAL MODULE: ../../node_modules/next/dist/client/app-dir/link.js
var app_dir_link = __webpack_require__(3498);
var link_default = /*#__PURE__*/__webpack_require__.n(app_dir_link);
// EXTERNAL MODULE: ../../node_modules/next/dist/api/headers.js
var headers = __webpack_require__(56057);
// EXTERNAL MODULE: ./src/lib/api.ts
var api = __webpack_require__(59902);
// EXTERNAL MODULE: ./src/lib/translations.ts
var translations = __webpack_require__(75112);
;// ./src/lib/formatName.ts
webpack/lib/util/registerExternalSerializerwebpack-sources/ReplaceSourcewebpack/lib/util/registerExternalSerializerwebpack-sources/CachedSource webpack/lib/util/registerExternalSerializerwebpack-sources/ReplaceSourcewebpack/lib/util/registerExternalSerializerwebpack-sources/RawSource$ /**
* Shortens the last name to first letter + period.
* "Иван Петров" → "Иван П."
* Works with a pre-composed full name string.
*/ export function shortName(fullName) {
if (!fullName?.trim()) return '—';
const parts = fullName.trim().split(/\s+/);
if (parts.length < 2 || !parts[1]) return parts[0];
return `${parts[0]} ${parts[1][0].toUpperCase()}.`;
}
/**
* Formats separate firstName + lastName fields, shortening the last name.
* Falls back to parsing fullName if firstName is not available.
*/ export function formatShortName(firstName, lastName, fallbackName) {
if (firstName?.trim()) {
const l = lastName?.trim();
return l ? `${firstName.trim()} ${l[0].toUpperCase()}.` : firstName.trim();
}
return shortName(fallbackName);
}
C buffersourcesizemapshash /**
* Shortens the last name to first letter + period.
* "Иван Петров" → "Иван П."
* Works with a pre-composed full name string.
*/ function shortName(fullName) {
if (!fullName?.trim()) return '—';
const parts = fullName.trim().split(/\s+/);
if (parts.length < 2 || !parts[1]) return parts[0];
return `${parts[0]} ${parts[1][0].toUpperCase()}.`;
}
/**
* Formats separate firstName + lastName fields, shortening the last name.
* Falls back to parsing fullName if firstName is not available.
*/ function formatShortName(firstName, lastName, fallbackName) {
if (firstName?.trim()) {
const l = lastName?.trim();
return l ? `${firstName.trim()} ${l[0].toUpperCase()}.` : firstName.trim();
}
return shortName(fallbackName);
}
{"finalSource":false}mapbufferedMap ReplaceSourceRawSource$ /**
* Shortens the last name to first letter + period.
* "Иван Петров" → "Иван П."
* Works with a pre-composed full name string.
*/ export function shortName(fullName) {
if (!fullName?.trim()) return '—';
const parts = fullName.trim().split(/\s+/);
if (parts.length < 2 || !parts[1]) return parts[0];
return `${parts[0]} ${parts[1][0].toUpperCase()}.`;
}
/**
* Formats separate firstName + lastName fields, shortening the last name.
* Falls back to parsing fullName if firstName is not available.
*/ export function formatShortName(firstName, lastName, fallbackName) {
if (firstName?.trim()) {
const l = lastName?.trim();
return l ? `${firstName.trim()} ${l[0].toUpperCase()}.` : firstName.trim();
}
return shortName(fallbackName);
}
136142undefined523529undefined !
;// ./src/lib/formatLastSeen.ts
webpack/lib/util/registerExternalSerializerwebpack-sources/ReplaceSourcewebpack/lib/util/registerExternalSerializerwebpack-sources/RawSourceW export function formatLastSeen(lastSeenAt, t) {
if (!lastSeenAt) return '';
const diff = Date.now() - new Date(lastSeenAt).getTime();
const minutes = Math.floor(diff / 60000);
if (minutes < 5) return t('user.online');
if (minutes < 60) return t('user.seen_minutes').replace('{n}', String(minutes));
const hours = Math.floor(minutes / 60);
if (hours < 24) return t('user.seen_hours').replace('{n}', String(hours));
const days = Math.floor(hours / 24);
if (days === 1) return t('user.seen_yesterday');
return t('user.seen_days').replace('{n}', String(days));
}
P function formatLastSeen(lastSeenAt, t) {
if (!lastSeenAt) return '';
const diff = Date.now() - new Date(lastSeenAt).getTime();
const minutes = Math.floor(diff / 60000);
if (minutes < 5) return t('user.online');
if (minutes < 60) return t('user.seen_minutes').replace('{n}', String(minutes));
const hours = Math.floor(minutes / 60);
if (hours < 24) return t('user.seen_hours').replace('{n}', String(hours));
const days = Math.floor(hours / 24);
if (days === 1) return t('user.seen_yesterday');
return t('user.seen_days').replace('{n}', String(days));
}
W export function formatLastSeen(lastSeenAt, t) {
if (!lastSeenAt) return '';
const diff = Date.now() - new Date(lastSeenAt).getTime();
const minutes = Math.floor(diff / 60000);
if (minutes < 5) return t('user.online');
if (minutes < 60) return t('user.seen_minutes').replace('{n}', String(minutes));
const hours = Math.floor(minutes / 60);
if (hours < 24) return t('user.seen_hours').replace('{n}', String(hours));
const days = Math.floor(hours / 24);
if (days === 1) return t('user.seen_yesterday');
return t('user.seen_days').replace('{n}', String(days));
}
06undefined
// EXTERNAL MODULE: ./src/components/FavoriteSpecialistButton.tsx
var FavoriteSpecialistButton = __webpack_require__(57650);
// EXTERNAL MODULE: ./src/components/SpecialistsFiltersClient.tsx
var SpecialistsFiltersClient = __webpack_require__(50815);
;// ./src/components/PlanBadge.tsx
webpack/lib/util/registerExternalSerializerwebpack-sources/ReplaceSourcewebpack/lib/util/registerExternalSerializerwebpack-sources/RawSource import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
const tierConfig = {
free: {
label: 'Free',
class: 'bg-gray-100 text-gray-500 border border-gray-200',
icon: ''
},
pro: {
label: 'Pro',
class: 'bg-blue-100 text-blue-700 border border-blue-200',
icon: /*#__PURE__*/ _jsx("svg", {
viewBox: "0 0 16 16",
fill: "none",
className: "w-3 h-3 inline-block mr-0.5 -mt-px",
children: /*#__PURE__*/ _jsx("path", {
d: "M8 1l1.8 3.6L14 5.5l-3 2.9.7 4.1L8 10.5l-3.7 1.9.7-4.1-3-2.9 4.2-.9L8 1z",
fill: "currentColor"
})
})
},
ultimate: {
label: 'Ultimate',
class: 'bg-gradient-to-r from-amber-400 via-yellow-400 to-amber-400 text-gray-900 border border-amber-500 shadow-sm shadow-amber-300/50',
icon: /*#__PURE__*/ _jsx("svg", {
viewBox: "0 0 16 16",
fill: "none",
className: "w-3 h-3 inline-block mr-0.5 -mt-px",
children: /*#__PURE__*/ _jsx("path", {
d: "M8 1l1.8 3.6L14 5.5l-3 2.9.7 4.1L8 10.5l-3.7 1.9.7-4.1-3-2.9 4.2-.9L8 1z",
fill: "currentColor"
})
})
}
};
export function PlanBadge({ tier, verified, size = 'sm', className = '' }) {
if (tier === 'free' && !verified) return null;
const sizeClass = size === 'sm' ? 'text-[10px] px-1.5 py-0.5' : 'text-xs px-2 py-0.5';
if (verified && tier !== 'free') {
return /*#__PURE__*/ _jsxs("span", {
className: `inline-flex items-center gap-1 rounded-full font-semibold ${sizeClass} ${tierConfig[tier].class} ${className}`,
children: [
tier === 'ultimate' ? /*#__PURE__*/ _jsx(StarIcon, {
className: "w-3 h-3 fill-gray-900"
}) : /*#__PURE__*/ _jsx(VerifiedIcon, {
className: "w-3 h-3"
}),
tierConfig[tier].label
]
});
}
if (verified) {
return /*#__PURE__*/ _jsx("span", {
className: `inline-flex items-center gap-0.5 rounded-full font-semibold ${sizeClass} bg-blue-100 text-blue-700 border border-blue-200 ${className}`,
children: /*#__PURE__*/ _jsx(VerifiedIcon, {
className: "w-3 h-3"
})
});
}
const cfg = tierConfig[tier];
return /*#__PURE__*/ _jsxs("span", {
className: `inline-flex items-center rounded-full font-semibold ${sizeClass} ${cfg.class} ${className}`,
children: [
tier === 'pro' && /*#__PURE__*/ _jsx(StarIcon, {
className: "w-3 h-3 mr-0.5 -mt-px"
}),
tier === 'ultimate' && /*#__PURE__*/ _jsx(StarIcon, {
className: "w-3 h-3 mr-0.5 -mt-px fill-gray-900"
}),
cfg.label
]
});
}
function StarIcon({ className = '' }) {
return /*#__PURE__*/ _jsx("svg", {
viewBox: "0 0 16 16",
fill: "none",
className: className,
children: /*#__PURE__*/ _jsx("path", {
d: "M8 1l1.8 3.6L14 5.5l-3 2.9.7 4.1L8 10.5l-3.7 1.9.7-4.1-3-2.9 4.2-.9L8 1z",
fill: "currentColor"
})
});
}
function VerifiedIcon({ className = '' }) {
return /*#__PURE__*/ _jsxs("svg", {
viewBox: "0 0 16 16",
fill: "none",
className: className,
children: [
/*#__PURE__*/ _jsx("path", {
d: "M8 1l2 1.4 2.4-.4 1 2.2 2 1.2-.4 2.4L16 10l-1.8 1.6.4 2.4-2.2.8L10.8 17 8 16.2 5.2 17l-1.2-2.2-2.2-.8.4-2.4L0 10l1-2.2-.4-2.4 2-1.2 1-2.2 2.4.4L8 1z",
fill: "#3B82F6"
}),
/*#__PURE__*/ _jsx("path", {
d: "M5.5 8.5l1.5 1.5 3-3",
stroke: "white",
strokeWidth: "1.5",
strokeLinecap: "round",
strokeLinejoin: "round"
})
]
});
}
b >_S V 6 9 Z ] $ ' O
R
! $
C
__WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__.___WEBPACK_MODULE_REFERENCE__0_5b226a737873225d_call_directImport_asiSafe1__._
const tierConfig = {
free: {
label: 'Free',
class: 'bg-gray-100 text-gray-500 border border-gray-200',
icon: ''
},
pro: {
label: 'Pro',
class: 'bg-blue-100 text-blue-700 border border-blue-200',
icon: /*#__PURE__*/ __WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._("svg", {
viewBox: "0 0 16 16",
fill: "none",
className: "w-3 h-3 inline-block mr-0.5 -mt-px",
children: /*#__PURE__*/ __WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._("path", {
d: "M8 1l1.8 3.6L14 5.5l-3 2.9.7 4.1L8 10.5l-3.7 1.9.7-4.1-3-2.9 4.2-.9L8 1z",
fill: "currentColor"
})
})
},
ultimate: {
label: 'Ultimate',
class: 'bg-gradient-to-r from-amber-400 via-yellow-400 to-amber-400 text-gray-900 border border-amber-500 shadow-sm shadow-amber-300/50',
icon: /*#__PURE__*/ __WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._("svg", {
viewBox: "0 0 16 16",
fill: "none",
className: "w-3 h-3 inline-block mr-0.5 -mt-px",
children: /*#__PURE__*/ __WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._("path", {
d: "M8 1l1.8 3.6L14 5.5l-3 2.9.7 4.1L8 10.5l-3.7 1.9.7-4.1-3-2.9 4.2-.9L8 1z",
fill: "currentColor"
})
})
}
};
function PlanBadge({ tier, verified, size = 'sm', className = '' }) {
if (tier === 'free' && !verified) return null;
const sizeClass = size === 'sm' ? 'text-[10px] px-1.5 py-0.5' : 'text-xs px-2 py-0.5';
if (verified && tier !== 'free') {
return /*#__PURE__*/ __WEBPACK_MODULE_REFERENCE__0_5b226a737873225d_call_directImport_asiSafe1__._("span", {
className: `inline-flex items-center gap-1 rounded-full font-semibold ${sizeClass} ${tierConfig[tier].class} ${className}`,
children: [
tier === 'ultimate' ? /*#__PURE__*/ __WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._(StarIcon, {
className: "w-3 h-3 fill-gray-900"
}) : /*#__PURE__*/ __WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._(VerifiedIcon, {
className: "w-3 h-3"
}),
tierConfig[tier].label
]
});
}
if (verified) {
return /*#__PURE__*/ __WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._("span", {
className: `inline-flex items-center gap-0.5 rounded-full font-semibold ${sizeClass} bg-blue-100 text-blue-700 border border-blue-200 ${className}`,
children: /*#__PURE__*/ __WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._(VerifiedIcon, {
className: "w-3 h-3"
})
});
}
const cfg = tierConfig[tier];
return /*#__PURE__*/ __WEBPACK_MODULE_REFERENCE__0_5b226a737873225d_call_directImport_asiSafe1__._("span", {
className: `inline-flex items-center rounded-full font-semibold ${sizeClass} ${cfg.class} ${className}`,
children: [
tier === 'pro' && /*#__PURE__*/ __WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._(StarIcon, {
className: "w-3 h-3 mr-0.5 -mt-px"
}),
tier === 'ultimate' && /*#__PURE__*/ __WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._(StarIcon, {
className: "w-3 h-3 mr-0.5 -mt-px fill-gray-900"
}),
cfg.label
]
});
}
function StarIcon({ className = '' }) {
return /*#__PURE__*/ __WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._("svg", {
viewBox: "0 0 16 16",
fill: "none",
className: className,
children: /*#__PURE__*/ __WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._("path", {
d: "M8 1l1.8 3.6L14 5.5l-3 2.9.7 4.1L8 10.5l-3.7 1.9.7-4.1-3-2.9 4.2-.9L8 1z",
fill: "currentColor"
})
});
}
function VerifiedIcon({ className = '' }) {
return /*#__PURE__*/ __WEBPACK_MODULE_REFERENCE__0_5b226a737873225d_call_directImport_asiSafe1__._("svg", {
viewBox: "0 0 16 16",
fill: "none",
className: className,
children: [
/*#__PURE__*/ __WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._("path", {
d: "M8 1l2 1.4 2.4-.4 1 2.2 2 1.2-.4 2.4L16 10l-1.8 1.6.4 2.4-2.2.8L10.8 17 8 16.2 5.2 17l-1.2-2.2-2.2-.8.4-2.4L0 10l1-2.2-.4-2.4 2-1.2 1-2.2 2.4.4L8 1z",
fill: "#3B82F6"
}),
/*#__PURE__*/ __WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._("path", {
d: "M5.5 8.5l1.5 1.5 3-3",
stroke: "white",
strokeWidth: "1.5",
strokeLinecap: "round",
strokeLinejoin: "round"
})
]
});
}
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
const tierConfig = {
free: {
label: 'Free',
class: 'bg-gray-100 text-gray-500 border border-gray-200',
icon: ''
},
pro: {
label: 'Pro',
class: 'bg-blue-100 text-blue-700 border border-blue-200',
icon: /*#__PURE__*/ _jsx("svg", {
viewBox: "0 0 16 16",
fill: "none",
className: "w-3 h-3 inline-block mr-0.5 -mt-px",
children: /*#__PURE__*/ _jsx("path", {
d: "M8 1l1.8 3.6L14 5.5l-3 2.9.7 4.1L8 10.5l-3.7 1.9.7-4.1-3-2.9 4.2-.9L8 1z",
fill: "currentColor"
})
})
},
ultimate: {
label: 'Ultimate',
class: 'bg-gradient-to-r from-amber-400 via-yellow-400 to-amber-400 text-gray-900 border border-amber-500 shadow-sm shadow-amber-300/50',
icon: /*#__PURE__*/ _jsx("svg", {
viewBox: "0 0 16 16",
fill: "none",
className: "w-3 h-3 inline-block mr-0.5 -mt-px",
children: /*#__PURE__*/ _jsx("path", {
d: "M8 1l1.8 3.6L14 5.5l-3 2.9.7 4.1L8 10.5l-3.7 1.9.7-4.1-3-2.9 4.2-.9L8 1z",
fill: "currentColor"
})
})
}
};
export function PlanBadge({ tier, verified, size = 'sm', className = '' }) {
if (tier === 'free' && !verified) return null;
const sizeClass = size === 'sm' ? 'text-[10px] px-1.5 py-0.5' : 'text-xs px-2 py-0.5';
if (verified && tier !== 'free') {
return /*#__PURE__*/ _jsxs("span", {
className: `inline-flex items-center gap-1 rounded-full font-semibold ${sizeClass} ${tierConfig[tier].class} ${className}`,
children: [
tier === 'ultimate' ? /*#__PURE__*/ _jsx(StarIcon, {
className: "w-3 h-3 fill-gray-900"
}) : /*#__PURE__*/ _jsx(VerifiedIcon, {
className: "w-3 h-3"
}),
tierConfig[tier].label
]
});
}
if (verified) {
return /*#__PURE__*/ _jsx("span", {
className: `inline-flex items-center gap-0.5 rounded-full font-semibold ${sizeClass} bg-blue-100 text-blue-700 border border-blue-200 ${className}`,
children: /*#__PURE__*/ _jsx(VerifiedIcon, {
className: "w-3 h-3"
})
});
}
const cfg = tierConfig[tier];
return /*#__PURE__*/ _jsxs("span", {
className: `inline-flex items-center rounded-full font-semibold ${sizeClass} ${cfg.class} ${className}`,
children: [
tier === 'pro' && /*#__PURE__*/ _jsx(StarIcon, {
className: "w-3 h-3 mr-0.5 -mt-px"
}),
tier === 'ultimate' && /*#__PURE__*/ _jsx(StarIcon, {
className: "w-3 h-3 mr-0.5 -mt-px fill-gray-900"
}),
cfg.label
]
});
}
function StarIcon({ className = '' }) {
return /*#__PURE__*/ _jsx("svg", {
viewBox: "0 0 16 16",
fill: "none",
className: className,
children: /*#__PURE__*/ _jsx("path", {
d: "M8 1l1.8 3.6L14 5.5l-3 2.9.7 4.1L8 10.5l-3.7 1.9.7-4.1-3-2.9 4.2-.9L8 1z",
fill: "currentColor"
})
});
}
function VerifiedIcon({ className = '' }) {
return /*#__PURE__*/ _jsxs("svg", {
viewBox: "0 0 16 16",
fill: "none",
className: className,
children: [
/*#__PURE__*/ _jsx("path", {
d: "M8 1l2 1.4 2.4-.4 1 2.2 2 1.2-.4 2.4L16 10l-1.8 1.6.4 2.4-2.2.8L10.8 17 8 16.2 5.2 17l-1.2-2.2-2.2-.8.4-2.4L0 10l1-2.2-.4-2.4 2-1.2 1-2.2 2.4.4L8 1z",
fill: "#3B82F6"
}),
/*#__PURE__*/ _jsx("path", {
d: "M5.5 8.5l1.5 1.5 3-3",
stroke: "white",
strokeWidth: "1.5",
strokeLinecap: "round",
strokeLinejoin: "round"
})
]
});
}
9 062undefined339342__WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._undefined510513__WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._undefined907910__WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._undefined10781081__WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._undefined12601266undefined15471551__WEBPACK_MODULE_REFERENCE__0_5b226a737873225d_call_directImport_asiSafe1__._undefined17751778__WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._undefined18821885__WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._undefined20842087__WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._undefined22962299__WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._undefined24462450__WEBPACK_MODULE_REFERENCE__0_5b226a737873225d_call_directImport_asiSafe1__._undefined26392642__WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._undefined27722775__WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._undefined29772980__WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._undefined31053108__WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._undefined33343338__WEBPACK_MODULE_REFERENCE__0_5b226a737873225d_call_directImport_asiSafe1__._undefined34773480__WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._undefined37373740__WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._undefined`_ ^ P $ -
W
5
x { A (0,react_jsx_runtime.jsx)(0,react_jsx_runtime.jsxs)$
;// ./src/app/specialists/page.tsx
cK webpack/lib/util/registerExternalSerializerwebpack-sources/ReplaceSourcewebpack/lib/util/registerExternalSerializerwebpack-sources/RawSourceWC import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
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';
export default async function SpecialistsPage({ searchParams }) {
const params = await searchParams;
const page = Number(params.page || 1);
const cookieStore = await cookies();
const locale = cookieStore.get('canhelp_locale')?.value ?? 'el';
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 = [];
let total = 0;
let catTree = [];
let locationTree = [];
let favoriteIds = new Set();
const BASE = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:4000';
const cookieHeader = cookieStore.getAll().map((c)=>`${c.name}=${c.value}`).join('; ');
try {
const [result, cats, locs] = await Promise.all([
getSpecialists({
page: String(page),
limit: '20',
...params,
...effectiveLocation ? {
location: effectiveLocation
} : {}
}),
getCategories().catch(()=>[]),
getLocations().catch(()=>[])
]);
console.log('[SpecialistsPage] got', result.total, 'specialists, data.length=', result.data.length);
specialists = result.data;
total = result.total;
catTree = cats;
locationTree = locs;
} 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)=>row.specialist?.id).filter(Boolean));
}
} catch {}
}
const totalPages = Math.ceil(total / 20);
const dateLocale = locale === 'ru' ? 'ru-RU' : locale === 'en' ? 'en-US' : 'el-GR';
const catLocale = locale === 'en' ? 'en' : locale === 'ru' ? 'ru' : 'el';
const buildUrl = (overrides)=>{
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 /*#__PURE__*/ _jsx("div", {
className: "max-w-7xl mx-auto px-4 py-8",
children: /*#__PURE__*/ _jsxs("div", {
className: "flex flex-col md:flex-row gap-8",
children: [
/*#__PURE__*/ _jsx("aside", {
className: "md:w-64 shrink-0",
children: /*#__PURE__*/ _jsxs("div", {
className: "bg-white rounded-xl border border-gray-200 p-5 sticky top-24",
children: [
/*#__PURE__*/ _jsx("h3", {
className: "font-semibold text-gray-900 mb-4",
children: t('tasks.search')
}),
/*#__PURE__*/ _jsx(SpecialistsFiltersClient, {
catTree: catTree,
locations: locationTree,
initialQ: params.q,
initialCategory: params.category,
initialLocation: params.location,
initialLanguages: params.language ? params.language.split(',').filter(Boolean) : []
})
]
})
}),
/*#__PURE__*/ _jsxs("div", {
className: "flex-1",
children: [
(params.q || params.category || params.location || params.language) && /*#__PURE__*/ _jsx("div", {
className: "mb-6",
children: /*#__PURE__*/ _jsxs("h1", {
className: "text-xl font-bold text-white",
children: [
total,
" ",
t('specialists.title').toLowerCase()
]
})
}),
specialists.length === 0 ? /*#__PURE__*/ _jsx("div", {
className: "text-center py-20 text-white/70",
children: /*#__PURE__*/ _jsx("p", {
className: "text-lg",
children: t('specialists.empty')
})
}) : /*#__PURE__*/ _jsx("div", {
className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-3",
children: specialists.map((user)=>{
const displayName = formatShortName(user.firstName, user.lastName, user.name);
const initials = `${user.firstName?.[0] ?? ''}${user.lastName?.[0] ?? ''}`.toUpperCase() || user.name?.[0]?.toUpperCase() || '?';
return /*#__PURE__*/ _jsxs("div", {
className: "relative bg-white rounded-xl border border-gray-200 p-5 hover:shadow-md hover:border-blue-300 transition flex flex-col",
children: [
/*#__PURE__*/ _jsx(Link, {
href: `/users/${user.id}`,
className: "absolute inset-0 rounded-xl",
"aria-label": displayName
}),
/*#__PURE__*/ _jsx(FavoriteSpecialistButton, {
specialistId: user.id,
initialFavorite: favoriteIds.has(user.id),
favoriteLabel: t('specialist.favorite'),
unfavoriteLabel: t('specialist.unfavorite')
}),
/*#__PURE__*/ _jsxs("div", {
className: "flex items-center gap-3 mb-3",
children: [
/*#__PURE__*/ _jsx("div", {
className: "w-12 h-12 rounded-full bg-blue-100 flex-shrink-0 flex items-center justify-center text-blue-700 font-bold text-sm overflow-hidden border border-gray-100",
children: user.image ? /*#__PURE__*/ _jsx("img", {
src: user.image,
alt: displayName,
className: "w-full h-full object-cover"
}) : initials
}),
/*#__PURE__*/ _jsxs("div", {
className: "min-w-0 flex-1",
children: [
/*#__PURE__*/ _jsx("h2", {
className: "font-semibold text-gray-900 truncate",
children: displayName
}),
/*#__PURE__*/ _jsxs("div", {
className: "flex items-center gap-2 flex-wrap",
children: [
user.planTier && user.planTier !== 'free' && /*#__PURE__*/ _jsx(PlanBadge, {
tier: user.planTier,
verified: user.hasVerifiedBadge,
size: "sm"
}),
user.hasVerifiedBadge && user.planTier === 'free' && /*#__PURE__*/ _jsx(PlanBadge, {
tier: "free",
verified: true,
size: "sm"
}),
Array.isArray(user.activeStatuses) && user.activeStatuses.includes('can_help_now') && /*#__PURE__*/ _jsxs("span", {
className: "inline-flex items-center gap-1 text-xs font-medium text-green-700 bg-green-50 border border-green-200 px-2 py-0.5 rounded-full",
children: [
/*#__PURE__*/ _jsx("span", {
className: "w-1.5 h-1.5 rounded-full bg-green-500 animate-pulse"
}),
t('status.can_help_now')
]
}),
user.rating && /*#__PURE__*/ _jsxs("div", {
className: "flex items-center gap-1",
children: [
/*#__PURE__*/ _jsx("span", {
className: "text-yellow-400 text-sm",
children: "★"
}),
/*#__PURE__*/ _jsx("span", {
className: "text-sm font-medium text-gray-700",
children: Number(user.rating).toFixed(1)
})
]
}),
user.todayStatus === 'available' && /*#__PURE__*/ _jsxs("span", {
className: "inline-flex items-center gap-1 text-xs font-medium text-green-700 bg-green-100 px-2 py-0.5 rounded-full",
children: [
/*#__PURE__*/ _jsx("span", {
className: "w-1.5 h-1.5 rounded-full bg-green-500 inline-block"
}),
t('schedule.available')
]
}),
user.todayStatus === 'busy' && /*#__PURE__*/ _jsxs("span", {
className: "inline-flex items-center gap-1 text-xs font-medium text-red-700 bg-red-100 px-2 py-0.5 rounded-full",
children: [
/*#__PURE__*/ _jsx("span", {
className: "w-1.5 h-1.5 rounded-full bg-red-500 inline-block"
}),
t('schedule.busy')
]
})
]
})
]
})
]
}),
user.cards && user.cards.length > 0 && /*#__PURE__*/ _jsxs("div", {
className: "flex flex-wrap gap-1.5 mb-2",
children: [
user.cards.slice(0, 2).map((card)=>/*#__PURE__*/ _jsx("span", {
className: "text-xs bg-gray-100 text-gray-700 px-2 py-0.5 rounded-full font-medium",
children: card.title
}, card.id)),
user.cards.length > 2 && /*#__PURE__*/ _jsxs("span", {
className: "text-xs text-gray-400",
children: [
"+",
user.cards.length - 2
]
})
]
}),
/*#__PURE__*/ _jsx("p", {
className: "text-sm text-gray-500 line-clamp-2 flex-1 mt-1",
children: user.bio || t('specialists.no_bio')
}),
user.lastSeenAt && /*#__PURE__*/ _jsx("p", {
className: "text-xs text-gray-400 mt-3",
children: formatLastSeen(user.lastSeenAt, t)
})
]
}, user.id);
})
}),
totalPages > 1 && /*#__PURE__*/ _jsx("div", {
className: "flex justify-center gap-2 mt-8",
children: Array.from({
length: totalPages
}, (_, i)=>i + 1).map((p)=>/*#__PURE__*/ _jsx(Link, {
href: buildUrl({
page: String(p)
}),
className: `w-10 h-10 flex items-center justify-center rounded-lg text-sm font-medium ${p === page ? 'bg-blue-600 text-white' : 'bg-white border border-gray-200 text-gray-700 hover:border-blue-400'}`,
children: p
}, p))
})
]
})
]
})
});
}
eA >@\^_ 7 9 n p F H V X f ! _
~
K N " _% o r t 9 = W Z B" F" # # # # f% i% k% s% U' Y' ( ( + + +, \., - - / / _1 b1 3 3 5 5 k8 o8 i9 l9 : : = = [> ^> > > @ @ A A
A A __WEBPACK_MODULE_REFERENCE__2_5b22636f6f6b696573225d_call_directImport_asiSafe1__.___WEBPACK_MODULE_REFERENCE__4_5b226765745472616e736c6174696f6e73225d_call_directImport_asiSafe1__._"https://api.canhelp.gr"0__WEBPACK_MODULE_REFERENCE__3_5b226765745370656369616c69737473225d_call_directImport_asiSafe1__.___WEBPACK_MODULE_REFERENCE__3_5b2267657443617465676f72696573225d_call_directImport_asiSafe1__.___WEBPACK_MODULE_REFERENCE__3_5b226765744c6f636174696f6e73225d_call_directImport_asiSafe1__.___WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__.___WEBPACK_MODULE_REFERENCE__0_5b226a737873225d_call_directImport_asiSafe1__.___WEBPACK_MODULE_REFERENCE__8_5b225370656369616c6973747346696c74657273436c69656e74225d_directImport_asiSafe1__.___WEBPACK_MODULE_REFERENCE__5_5b22666f726d617453686f72744e616d65225d_call_directImport_asiSafe1__.___WEBPACK_MODULE_REFERENCE__1_5b2264656661756c74225d_directImport_asiSafe1__.___WEBPACK_MODULE_REFERENCE__7_5b224661766f726974655370656369616c697374427574746f6e225d_directImport_asiSafe1__.___WEBPACK_MODULE_REFERENCE__9_5b22506c616e4261646765225d_directImport_asiSafe1__.___WEBPACK_MODULE_REFERENCE__6_5b22666f726d61744c6173745365656e225d_call_directImport_asiSafe1__._O
async function SpecialistsPage({ searchParams }) {
const params = await searchParams;
const page = Number(params.page || 1);
const cookieStore = await __WEBPACK_MODULE_REFERENCE__2_5b22636f6f6b696573225d_call_directImport_asiSafe1__._();
const locale = cookieStore.get('canhelp_locale')?.value ?? 'el';
const t = __WEBPACK_MODULE_REFERENCE__4_5b226765745472616e736c6174696f6e73225d_call_directImport_asiSafe1__._(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 = [];
let total = 0;
let catTree = [];
let locationTree = [];
let favoriteIds = new Set();
const BASE = "https://api.canhelp.gr" || 0;
const cookieHeader = cookieStore.getAll().map((c)=>`${c.name}=${c.value}`).join('; ');
try {
const [result, cats, locs] = await Promise.all([
__WEBPACK_MODULE_REFERENCE__3_5b226765745370656369616c69737473225d_call_directImport_asiSafe1__._({
page: String(page),
limit: '20',
...params,
...effectiveLocation ? {
location: effectiveLocation
} : {}
}),
__WEBPACK_MODULE_REFERENCE__3_5b2267657443617465676f72696573225d_call_directImport_asiSafe1__._().catch(()=>[]),
__WEBPACK_MODULE_REFERENCE__3_5b226765744c6f636174696f6e73225d_call_directImport_asiSafe1__._().catch(()=>[])
]);
console.log('[SpecialistsPage] got', result.total, 'specialists, data.length=', result.data.length);
specialists = result.data;
total = result.total;
catTree = cats;
locationTree = locs;
} 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)=>row.specialist?.id).filter(Boolean));
}
} catch {}
}
const totalPages = Math.ceil(total / 20);
const dateLocale = locale === 'ru' ? 'ru-RU' : locale === 'en' ? 'en-US' : 'el-GR';
const catLocale = locale === 'en' ? 'en' : locale === 'ru' ? 'ru' : 'el';
const buildUrl = (overrides)=>{
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 /*#__PURE__*/ __WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._("div", {
className: "max-w-7xl mx-auto px-4 py-8",
children: /*#__PURE__*/ __WEBPACK_MODULE_REFERENCE__0_5b226a737873225d_call_directImport_asiSafe1__._("div", {
className: "flex flex-col md:flex-row gap-8",
children: [
/*#__PURE__*/ __WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._("aside", {
className: "md:w-64 shrink-0",
children: /*#__PURE__*/ __WEBPACK_MODULE_REFERENCE__0_5b226a737873225d_call_directImport_asiSafe1__._("div", {
className: "bg-white rounded-xl border border-gray-200 p-5 sticky top-24",
children: [
/*#__PURE__*/ __WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._("h3", {
className: "font-semibold text-gray-900 mb-4",
children: t('tasks.search')
}),
/*#__PURE__*/ __WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._(__WEBPACK_MODULE_REFERENCE__8_5b225370656369616c6973747346696c74657273436c69656e74225d_directImport_asiSafe1__._, {
catTree: catTree,
locations: locationTree,
initialQ: params.q,
initialCategory: params.category,
initialLocation: params.location,
initialLanguages: params.language ? params.language.split(',').filter(Boolean) : []
})
]
})
}),
/*#__PURE__*/ __WEBPACK_MODULE_REFERENCE__0_5b226a737873225d_call_directImport_asiSafe1__._("div", {
className: "flex-1",
children: [
(params.q || params.category || params.location || params.language) && /*#__PURE__*/ __WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._("div", {
className: "mb-6",
children: /*#__PURE__*/ __WEBPACK_MODULE_REFERENCE__0_5b226a737873225d_call_directImport_asiSafe1__._("h1", {
className: "text-xl font-bold text-white",
children: [
total,
" ",
t('specialists.title').toLowerCase()
]
})
}),
specialists.length === 0 ? /*#__PURE__*/ __WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._("div", {
className: "text-center py-20 text-white/70",
children: /*#__PURE__*/ __WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._("p", {
className: "text-lg",
children: t('specialists.empty')
})
}) : /*#__PURE__*/ __WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._("div", {
className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-3",
children: specialists.map((user)=>{
const displayName = __WEBPACK_MODULE_REFERENCE__5_5b22666f726d617453686f72744e616d65225d_call_directImport_asiSafe1__._(user.firstName, user.lastName, user.name);
const initials = `${user.firstName?.[0] ?? ''}${user.lastName?.[0] ?? ''}`.toUpperCase() || user.name?.[0]?.toUpperCase() || '?';
return /*#__PURE__*/ __WEBPACK_MODULE_REFERENCE__0_5b226a737873225d_call_directImport_asiSafe1__._("div", {
className: "relative bg-white rounded-xl border border-gray-200 p-5 hover:shadow-md hover:border-blue-300 transition flex flex-col",
children: [
/*#__PURE__*/ __WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._(__WEBPACK_MODULE_REFERENCE__1_5b2264656661756c74225d_directImport_asiSafe1__._, {
href: `/users/${user.id}`,
className: "absolute inset-0 rounded-xl",
"aria-label": displayName
}),
/*#__PURE__*/ __WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._(__WEBPACK_MODULE_REFERENCE__7_5b224661766f726974655370656369616c697374427574746f6e225d_directImport_asiSafe1__._, {
specialistId: user.id,
initialFavorite: favoriteIds.has(user.id),
favoriteLabel: t('specialist.favorite'),
unfavoriteLabel: t('specialist.unfavorite')
}),
/*#__PURE__*/ __WEBPACK_MODULE_REFERENCE__0_5b226a737873225d_call_directImport_asiSafe1__._("div", {
className: "flex items-center gap-3 mb-3",
children: [
/*#__PURE__*/ __WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._("div", {
className: "w-12 h-12 rounded-full bg-blue-100 flex-shrink-0 flex items-center justify-center text-blue-700 font-bold text-sm overflow-hidden border border-gray-100",
children: user.image ? /*#__PURE__*/ __WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._("img", {
src: user.image,
alt: displayName,
className: "w-full h-full object-cover"
}) : initials
}),
/*#__PURE__*/ __WEBPACK_MODULE_REFERENCE__0_5b226a737873225d_call_directImport_asiSafe1__._("div", {
className: "min-w-0 flex-1",
children: [
/*#__PURE__*/ __WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._("h2", {
className: "font-semibold text-gray-900 truncate",
children: displayName
}),
/*#__PURE__*/ __WEBPACK_MODULE_REFERENCE__0_5b226a737873225d_call_directImport_asiSafe1__._("div", {
className: "flex items-center gap-2 flex-wrap",
children: [
user.planTier && user.planTier !== 'free' && /*#__PURE__*/ __WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._(__WEBPACK_MODULE_REFERENCE__9_5b22506c616e4261646765225d_directImport_asiSafe1__._, {
tier: user.planTier,
verified: user.hasVerifiedBadge,
size: "sm"
}),
user.hasVerifiedBadge && user.planTier === 'free' && /*#__PURE__*/ __WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._(__WEBPACK_MODULE_REFERENCE__9_5b22506c616e4261646765225d_directImport_asiSafe1__._, {
tier: "free",
verified: true,
size: "sm"
}),
Array.isArray(user.activeStatuses) && user.activeStatuses.includes('can_help_now') && /*#__PURE__*/ __WEBPACK_MODULE_REFERENCE__0_5b226a737873225d_call_directImport_asiSafe1__._("span", {
className: "inline-flex items-center gap-1 text-xs font-medium text-green-700 bg-green-50 border border-green-200 px-2 py-0.5 rounded-full",
children: [
/*#__PURE__*/ __WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._("span", {
className: "w-1.5 h-1.5 rounded-full bg-green-500 animate-pulse"
}),
t('status.can_help_now')
]
}),
user.rating && /*#__PURE__*/ __WEBPACK_MODULE_REFERENCE__0_5b226a737873225d_call_directImport_asiSafe1__._("div", {
className: "flex items-center gap-1",
children: [
/*#__PURE__*/ __WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._("span", {
className: "text-yellow-400 text-sm",
children: "★"
}),
/*#__PURE__*/ __WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._("span", {
className: "text-sm font-medium text-gray-700",
children: Number(user.rating).toFixed(1)
})
]
}),
user.todayStatus === 'available' && /*#__PURE__*/ __WEBPACK_MODULE_REFERENCE__0_5b226a737873225d_call_directImport_asiSafe1__._("span", {
className: "inline-flex items-center gap-1 text-xs font-medium text-green-700 bg-green-100 px-2 py-0.5 rounded-full",
children: [
/*#__PURE__*/ __WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._("span", {
className: "w-1.5 h-1.5 rounded-full bg-green-500 inline-block"
}),
t('schedule.available')
]
}),
user.todayStatus === 'busy' && /*#__PURE__*/ __WEBPACK_MODULE_REFERENCE__0_5b226a737873225d_call_directImport_asiSafe1__._("span", {
className: "inline-flex items-center gap-1 text-xs font-medium text-red-700 bg-red-100 px-2 py-0.5 rounded-full",
children: [
/*#__PURE__*/ __WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._("span", {
className: "w-1.5 h-1.5 rounded-full bg-red-500 inline-block"
}),
t('schedule.busy')
]
})
]
})
]
})
]
}),
user.cards && user.cards.length > 0 && /*#__PURE__*/ __WEBPACK_MODULE_REFERENCE__0_5b226a737873225d_call_directImport_asiSafe1__._("div", {
className: "flex flex-wrap gap-1.5 mb-2",
children: [
user.cards.slice(0, 2).map((card)=>/*#__PURE__*/ __WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._("span", {
className: "text-xs bg-gray-100 text-gray-700 px-2 py-0.5 rounded-full font-medium",
children: card.title
}, card.id)),
user.cards.length > 2 && /*#__PURE__*/ __WEBPACK_MODULE_REFERENCE__0_5b226a737873225d_call_directImport_asiSafe1__._("span", {
className: "text-xs text-gray-400",
children: [
"+",
user.cards.length - 2
]
})
]
}),
/*#__PURE__*/ __WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._("p", {
className: "text-sm text-gray-500 line-clamp-2 flex-1 mt-1",
children: user.bio || t('specialists.no_bio')
}),
user.lastSeenAt && /*#__PURE__*/ __WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._("p", {
className: "text-xs text-gray-400 mt-3",
children: __WEBPACK_MODULE_REFERENCE__6_5b22666f726d61744c6173745365656e225d_call_directImport_asiSafe1__._(user.lastSeenAt, t)
})
]
}, user.id);
})
}),
totalPages > 1 && /*#__PURE__*/ __WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._("div", {
className: "flex justify-center gap-2 mt-8",
children: Array.from({
length: totalPages
}, (_, i)=>i + 1).map((p)=>/*#__PURE__*/ __WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._(__WEBPACK_MODULE_REFERENCE__1_5b2264656661756c74225d_directImport_asiSafe1__._, {
href: buildUrl({
page: String(p)
}),
className: `w-10 h-10 flex items-center justify-center rounded-lg text-sm font-medium ${p === page ? 'bg-blue-600 text-white' : 'bg-white border border-gray-200 text-gray-700 hover:border-blue-400'}`,
children: p
}, p))
})
]
})
]
})
});
}
WC import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
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';
export default async function SpecialistsPage({ searchParams }) {
const params = await searchParams;
const page = Number(params.page || 1);
const cookieStore = await cookies();
const locale = cookieStore.get('canhelp_locale')?.value ?? 'el';
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 = [];
let total = 0;
let catTree = [];
let locationTree = [];
let favoriteIds = new Set();
const BASE = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:4000';
const cookieHeader = cookieStore.getAll().map((c)=>`${c.name}=${c.value}`).join('; ');
try {
const [result, cats, locs] = await Promise.all([
getSpecialists({
page: String(page),
limit: '20',
...params,
...effectiveLocation ? {
location: effectiveLocation
} : {}
}),
getCategories().catch(()=>[]),
getLocations().catch(()=>[])
]);
console.log('[SpecialistsPage] got', result.total, 'specialists, data.length=', result.data.length);
specialists = result.data;
total = result.total;
catTree = cats;
locationTree = locs;
} 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)=>row.specialist?.id).filter(Boolean));
}
} catch {}
}
const totalPages = Math.ceil(total / 20);
const dateLocale = locale === 'ru' ? 'ru-RU' : locale === 'en' ? 'en-US' : 'el-GR';
const catLocale = locale === 'en' ? 'en' : locale === 'ru' ? 'ru' : 'el';
const buildUrl = (overrides)=>{
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 /*#__PURE__*/ _jsx("div", {
className: "max-w-7xl mx-auto px-4 py-8",
children: /*#__PURE__*/ _jsxs("div", {
className: "flex flex-col md:flex-row gap-8",
children: [
/*#__PURE__*/ _jsx("aside", {
className: "md:w-64 shrink-0",
children: /*#__PURE__*/ _jsxs("div", {
className: "bg-white rounded-xl border border-gray-200 p-5 sticky top-24",
children: [
/*#__PURE__*/ _jsx("h3", {
className: "font-semibold text-gray-900 mb-4",
children: t('tasks.search')
}),
/*#__PURE__*/ _jsx(SpecialistsFiltersClient, {
catTree: catTree,
locations: locationTree,
initialQ: params.q,
initialCategory: params.category,
initialLocation: params.location,
initialLanguages: params.language ? params.language.split(',').filter(Boolean) : []
})
]
})
}),
/*#__PURE__*/ _jsxs("div", {
className: "flex-1",
children: [
(params.q || params.category || params.location || params.language) && /*#__PURE__*/ _jsx("div", {
className: "mb-6",
children: /*#__PURE__*/ _jsxs("h1", {
className: "text-xl font-bold text-white",
children: [
total,
" ",
t('specialists.title').toLowerCase()
]
})
}),
specialists.length === 0 ? /*#__PURE__*/ _jsx("div", {
className: "text-center py-20 text-white/70",
children: /*#__PURE__*/ _jsx("p", {
className: "text-lg",
children: t('specialists.empty')
})
}) : /*#__PURE__*/ _jsx("div", {
className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-3",
children: specialists.map((user)=>{
const displayName = formatShortName(user.firstName, user.lastName, user.name);
const initials = `${user.firstName?.[0] ?? ''}${user.lastName?.[0] ?? ''}`.toUpperCase() || user.name?.[0]?.toUpperCase() || '?';
return /*#__PURE__*/ _jsxs("div", {
className: "relative bg-white rounded-xl border border-gray-200 p-5 hover:shadow-md hover:border-blue-300 transition flex flex-col",
children: [
/*#__PURE__*/ _jsx(Link, {
href: `/users/${user.id}`,
className: "absolute inset-0 rounded-xl",
"aria-label": displayName
}),
/*#__PURE__*/ _jsx(FavoriteSpecialistButton, {
specialistId: user.id,
initialFavorite: favoriteIds.has(user.id),
favoriteLabel: t('specialist.favorite'),
unfavoriteLabel: t('specialist.unfavorite')
}),
/*#__PURE__*/ _jsxs("div", {
className: "flex items-center gap-3 mb-3",
children: [
/*#__PURE__*/ _jsx("div", {
className: "w-12 h-12 rounded-full bg-blue-100 flex-shrink-0 flex items-center justify-center text-blue-700 font-bold text-sm overflow-hidden border border-gray-100",
children: user.image ? /*#__PURE__*/ _jsx("img", {
src: user.image,
alt: displayName,
className: "w-full h-full object-cover"
}) : initials
}),
/*#__PURE__*/ _jsxs("div", {
className: "min-w-0 flex-1",
children: [
/*#__PURE__*/ _jsx("h2", {
className: "font-semibold text-gray-900 truncate",
children: displayName
}),
/*#__PURE__*/ _jsxs("div", {
className: "flex items-center gap-2 flex-wrap",
children: [
user.planTier && user.planTier !== 'free' && /*#__PURE__*/ _jsx(PlanBadge, {
tier: user.planTier,
verified: user.hasVerifiedBadge,
size: "sm"
}),
user.hasVerifiedBadge && user.planTier === 'free' && /*#__PURE__*/ _jsx(PlanBadge, {
tier: "free",
verified: true,
size: "sm"
}),
Array.isArray(user.activeStatuses) && user.activeStatuses.includes('can_help_now') && /*#__PURE__*/ _jsxs("span", {
className: "inline-flex items-center gap-1 text-xs font-medium text-green-700 bg-green-50 border border-green-200 px-2 py-0.5 rounded-full",
children: [
/*#__PURE__*/ _jsx("span", {
className: "w-1.5 h-1.5 rounded-full bg-green-500 animate-pulse"
}),
t('status.can_help_now')
]
}),
user.rating && /*#__PURE__*/ _jsxs("div", {
className: "flex items-center gap-1",
children: [
/*#__PURE__*/ _jsx("span", {
className: "text-yellow-400 text-sm",
children: "★"
}),
/*#__PURE__*/ _jsx("span", {
className: "text-sm font-medium text-gray-700",
children: Number(user.rating).toFixed(1)
})
]
}),
user.todayStatus === 'available' && /*#__PURE__*/ _jsxs("span", {
className: "inline-flex items-center gap-1 text-xs font-medium text-green-700 bg-green-100 px-2 py-0.5 rounded-full",
children: [
/*#__PURE__*/ _jsx("span", {
className: "w-1.5 h-1.5 rounded-full bg-green-500 inline-block"
}),
t('schedule.available')
]
}),
user.todayStatus === 'busy' && /*#__PURE__*/ _jsxs("span", {
className: "inline-flex items-center gap-1 text-xs font-medium text-red-700 bg-red-100 px-2 py-0.5 rounded-full",
children: [
/*#__PURE__*/ _jsx("span", {
className: "w-1.5 h-1.5 rounded-full bg-red-500 inline-block"
}),
t('schedule.busy')
]
})
]
})
]
})
]
}),
user.cards && user.cards.length > 0 && /*#__PURE__*/ _jsxs("div", {
className: "flex flex-wrap gap-1.5 mb-2",
children: [
user.cards.slice(0, 2).map((card)=>/*#__PURE__*/ _jsx("span", {
className: "text-xs bg-gray-100 text-gray-700 px-2 py-0.5 rounded-full font-medium",
children: card.title
}, card.id)),
user.cards.length > 2 && /*#__PURE__*/ _jsxs("span", {
className: "text-xs text-gray-400",
children: [
"+",
user.cards.length - 2
]
})
]
}),
/*#__PURE__*/ _jsx("p", {
className: "text-sm text-gray-500 line-clamp-2 flex-1 mt-1",
children: user.bio || t('specialists.no_bio')
}),
user.lastSeenAt && /*#__PURE__*/ _jsx("p", {
className: "text-xs text-gray-400 mt-3",
children: formatLastSeen(user.lastSeenAt, t)
})
]
}, user.id);
})
}),
totalPages > 1 && /*#__PURE__*/ _jsx("div", {
className: "flex justify-center gap-2 mt-8",
children: Array.from({
length: totalPages
}, (_, i)=>i + 1).map((p)=>/*#__PURE__*/ _jsx(Link, {
href: buildUrl({
page: String(p)
}),
className: `w-10 h-10 flex items-center justify-center rounded-lg text-sm font-medium ${p === page ? 'bg-blue-600 text-white' : 'bg-white border border-gray-200 text-gray-700 hover:border-blue-400'}`,
children: p
}, p))
})
]
})
]
})
});
}
062undefined6492undefined94132undefined134205undefined207259undefined261311undefined313366undefined368448undefined450530undefined532582undefined584598undefined762768__WEBPACK_MODULE_REFERENCE__2_5b22636f6f6b696573225d_call_directImport_asiSafe1__._undefined856870__WEBPACK_MODULE_REFERENCE__4_5b226765745472616e736c6174696f6e73225d_call_directImport_asiSafe1__._undefined12561286"https://api.canhelp.gr"undefined129113130undefined14861499__WEBPACK_MODULE_REFERENCE__3_5b226765745370656369616c69737473225d_call_directImport_asiSafe1__._undefined17351747__WEBPACK_MODULE_REFERENCE__3_5b2267657443617465676f72696573225d_call_directImport_asiSafe1__._undefined17781789__WEBPACK_MODULE_REFERENCE__3_5b226765744c6f636174696f6e73225d_call_directImport_asiSafe1__._undefined32313234__WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._undefined33273331__WEBPACK_MODULE_REFERENCE__0_5b226a737873225d_call_directImport_asiSafe1__._undefined34543457__WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._undefined35653569__WEBPACK_MODULE_REFERENCE__0_5b226a737873225d_call_directImport_asiSafe1__._undefined37573760__WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._undefined39833986__WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._undefined39884011__WEBPACK_MODULE_REFERENCE__8_5b225370656369616c6973747346696c74657273436c69656e74225d_directImport_asiSafe1__._undefined45534557__WEBPACK_MODULE_REFERENCE__0_5b226a737873225d_call_directImport_asiSafe1__._undefined47504753__WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._undefined48634867__WEBPACK_MODULE_REFERENCE__0_5b226a737873225d_call_directImport_asiSafe1__._undefined53115314__WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._undefined54515454__WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._undefined56565659__WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._undefined58695883__WEBPACK_MODULE_REFERENCE__5_5b22666f726d617453686f72744e616d65225d_call_directImport_asiSafe1__._undefined61436147__WEBPACK_MODULE_REFERENCE__0_5b226a737873225d_call_directImport_asiSafe1__._undefined64296432__WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._undefined64346437__WEBPACK_MODULE_REFERENCE__1_5b2264656661756c74225d_directImport_asiSafe1__._undefined67676770__WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._undefined67726795__WEBPACK_MODULE_REFERENCE__7_5b224661766f726974655370656369616c697374427574746f6e225d_directImport_asiSafe1__._undefined72257229__WEBPACK_MODULE_REFERENCE__0_5b226a737873225d_call_directImport_asiSafe1__._undefined74457448__WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._undefined77677770__WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._undefined82048208__WEBPACK_MODULE_REFERENCE__0_5b226a737873225d_call_directImport_asiSafe1__._undefined84348437__WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._undefined87708774__WEBPACK_MODULE_REFERENCE__0_5b226a737873225d_call_directImport_asiSafe1__._undefined90889091__WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._undefined90939101__WEBPACK_MODULE_REFERENCE__9_5b22506c616e4261646765225d_directImport_asiSafe1__._undefined95749577__WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._undefined95799587__WEBPACK_MODULE_REFERENCE__9_5b22506c616e4261646765225d_directImport_asiSafe1__._undefined1006910073__WEBPACK_MODULE_REFERENCE__0_5b226a737873225d_call_directImport_asiSafe1__._undefined1046010463__WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._undefined1102011024__WEBPACK_MODULE_REFERENCE__0_5b226a737873225d_call_directImport_asiSafe1__._undefined1130711310__WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._undefined1168811691__WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._undefined1227112275__WEBPACK_MODULE_REFERENCE__0_5b226a737873225d_call_directImport_asiSafe1__._undefined1263912642__WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._undefined1321313217__WEBPACK_MODULE_REFERENCE__0_5b226a737873225d_call_directImport_asiSafe1__._undefined1357713580__WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._undefined1444314447__WEBPACK_MODULE_REFERENCE__0_5b226a737873225d_call_directImport_asiSafe1__._undefined1469714700__WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._undefined1508315087__WEBPACK_MODULE_REFERENCE__0_5b226a737873225d_call_directImport_asiSafe1__._undefined1563915642__WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._undefined1596315966__WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._undefined1611416127__WEBPACK_MODULE_REFERENCE__6_5b22666f726d61744c6173745365656e225d_call_directImport_asiSafe1__._undefined1639016393__WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._undefined1664816651__WEBPACK_MODULE_REFERENCE__0_5b226a7378225d_call_directImport_asiSafe1__._undefined1665316656__WEBPACK_MODULE_REFERENCE__1_5b2264656661756c74225d_directImport_asiSafe1__._undefined`4_ W d P ) r 9
; A j l E P
_ + / { . x 3 -! " O" # # % % &