/opt/canhelp/apps/web/src/app/tasks
Edit: /opt/canhelp/apps/web/src/app/tasks/page.tsx (10124B)
import Link from 'next/link'
import { cookies } from 'next/headers'
import { getCategories, getLocations, getTaskBudgetRange } from '@/lib/api'
import { getTranslations } from '@/lib/translations'
import type { Locale } from '@/lib/translations'
import { TaskListClient } from './TaskListClient'
import { TaskFiltersClient } from './TaskFiltersClient'
import { TasksAuthGuard } from './TasksAuthGuard'
import { TaskMapDynamic } from './TaskMapDynamic'
import { TaskViewToggle } from './TaskViewToggle'
// SSR uses INTERNAL_API_URL (or localhost:4000 directly) to avoid external DNS/CORS round-trip
const API_BASE = typeof window === 'undefined'
? (process.env.INTERNAL_API_URL || 'http://localhost:4000')
: (process.env.NEXT_PUBLIC_API_URL || 'http://localhost:4000')
interface Props {
searchParams: Promise
>
}
export default async function TasksPage({ 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 cookieLocation = decodeURIComponent(cookieStore.get('canhelp_location')?.value ?? '')
const t = getTranslations(locale)
const catLocale = locale === 'en' ? 'en' : locale === 'ru' ? 'ru' : locale === 'uk' ? 'uk' : 'el'
const cookieHeader = cookieStore.getAll().map(c => `${c.name}=${c.value}`).join('; ')
// Detect if user is a logged-in specialist (has session cookie)
let isSpecialistSession = false
if (cookieHeader) {
try {
const INTERNAL = process.env.INTERNAL_API_URL || 'http://localhost:4000'
const sessRes = await fetch(`${INTERNAL}/api/auth/get-session`, { headers: { Cookie: cookieHeader } })
if (sessRes.ok) {
const sess = await sessRes.json()
const role = sess?.user?.role
isSpecialistSession = role === 'specialist' || role === 'admin'
}
} catch {}
}
// Guests and customers default to 'all'; specialists default to 'for_me'
const mode: 'for_me' | 'all' = params.mode === 'all' ? 'all'
: params.mode === 'for_me' ? 'for_me'
: isSpecialistSession ? 'for_me' : 'all'
let tasks: any[] = []
let total = 0
let noCards = false
let categories: any[] = []
let locationTree: any[] = []
let budgetRange = { min: 0, max: 0 }
// In 'all' mode only respect explicit URL param; cookie location only applies to 'for_me'
// (for_me filtering is handled server-side by specialist cards, so cookie is unused there too)
const effectiveLocation = params.location ?? (mode === 'for_me' ? cookieLocation : '')
const { q, category } = params
const rangeParams: Record = {}
if (q) rangeParams.q = q
if (category) rangeParams.category = category
if (effectiveLocation) rangeParams.location = effectiveLocation
const effectiveParams: Record = {
page: String(page),
limit: '10',
...params,
...(effectiveLocation ? { location: effectiveLocation } : {}),
}
delete effectiveParams.mode
try {
;[categories, locationTree, budgetRange] = await Promise.all([
getCategories(),
getLocations(),
getTaskBudgetRange(Object.keys(rangeParams).length ? rangeParams : undefined),
])
} catch {}
try {
if (mode === 'for_me') {
const forMeParams = new URLSearchParams(effectiveParams)
const res = await fetch(`${API_BASE}/api/tasks/for-me?${forMeParams}`, {
headers: { Cookie: cookieHeader },
cache: 'no-store',
})
if (res.ok) {
const json = await res.json()
if (json.noCards) {
noCards = true
} else {
tasks = json.data ?? []
total = json.total ?? 0
}
}
} else {
const allParams = new URLSearchParams(effectiveParams)
const res = await fetch(`${API_BASE}/api/tasks?${allParams}`, {
headers: { Cookie: cookieHeader },
cache: 'no-store',
})
if (res.ok) {
const json = await res.json()
tasks = json.data ?? []
total = json.total ?? 0
}
}
} catch {}
const catMap: Record = {}
for (const cat of categories) {
catMap[cat.slug] = (cat.names as any)[catLocale] ?? cat.names.el
for (const sub of cat.children ?? []) {
catMap[sub.slug] = (sub.names as any)[catLocale] ?? sub.names.el
}
}
const locMap: Record = {}
for (const city of locationTree) {
locMap[city.slug] = (city.names as any)[catLocale] ?? city.names.el
for (const d of city.children ?? []) {
locMap[d.slug] = (d.names as any)[catLocale] ?? d.names.el
}
}
const view = params.view === 'map' ? 'map' : 'list'
const hasFilter = !!(params.q || params.category || params.location || params.budgetMin || params.budgetMax)
const filterParams: Record = {}
if (params.q) filterParams.q = params.q
if (params.category) filterParams.category = params.category
if (params.location) filterParams.location = params.location
if (params.budgetMin) filterParams.budgetMin = params.budgetMin
if (params.budgetMax) filterParams.budgetMax = params.budgetMax
return (
{view === 'map' ? (
/* ── Full-screen map layout ────────────────────────────── */
{/* Floating filters panel — left side */}
{/* View toggle floats top-right over the map */}
) : (
/* ── List layout ───────────────────────────────────────── */
{/* Sidebar filters */}
{/* Task list */}
{hasFilter ? <>{total} {t('tasks.title')}> : t('nav.tasks')}
{noCards ? (
🗂️
{t('find_tasks.no_cards')}
{t('find_tasks.add_card')}
) : (
)}
)}
)
}