/opt/canhelp/apps/api/src/routes
NameSizeModeActions
admin.ts950570644editdlrm
auto-match.ts74060644editdlrm
auto-response.ts81110644editdlrm
availability.ts72940644editdlrm
categories.ts18670644editdlrm
chat.ts93770644editdlrm
dashboard.ts51420644editdlrm
favorites.ts44410644editdlrm
fomo.ts68440644editdlrm
google.ts48720644editdlrm
health.ts12140644editdlrm
i18n.ts1670860644editdlrm
locations.ts17810644editdlrm
notifications.ts28620644editdlrm
offers.ts100470644editdlrm
payments.ts211670644editdlrm
plans.ts13520644editdlrm
portfolio.ts66580644editdlrm
price-list.ts144170644editdlrm
reports.ts20070644editdlrm
reviews.ts27830644editdlrm
skill-suggestions.ts10350644editdlrm
specialist-cards.ts186410644editdlrm
stats.ts10930644editdlrm
statuses.ts37310644editdlrm
support.ts84520644editdlrm
tasks.ts300180644editdlrm
uploads.ts15260644editdlrm
users.ts371570644editdlrm
Edit: /opt/canhelp/apps/api/src/routes/dashboard.ts (5142B)
import { Hono } from 'hono' import { eq, count, and, gte } from 'drizzle-orm' import { db } from '../db.js' import { users, plans, tasks, offers, reviews, specialistCards, portfolioItems, notifications, settings as settingsTable } from '@canhelp/db' import { requireAuth, type AuthVariables } from '../middleware/auth.js' import { getDailyUsageSummary } from '../lib/daily-limits.js' import { getCalendarStatus } from '../lib/google-oauth.js' import { getHelperCapability } from '../lib/helper-capability.js' const app = new Hono<{ Variables: AuthVariables }>() // GET /dashboard/me — authenticated user's plan dashboard app.get('/me', requireAuth, async (c) => { const authUser = c.get('user') // Fetch user + plan const [user] = await db.select().from(users).where(eq(users.id, authUser.id)) if (!user) return c.json({ error: 'User not found' }, 404) let plan = null if (user.planId) { const [p] = await db.select().from(plans).where(eq(plans.id, user.planId)) plan = p ?? null } // Daily usage const usage = await getDailyUsageSummary(authUser.id, plan) // Stats const isHelperReady = await getHelperCapability(user.id) let stats: Record = {} const [unreadNotifications] = await db .select({ total: count() }) .from(notifications) .where(and(eq(notifications.recipientId, user.id), eq(notifications.isRead, false))) if (isHelperReady) { // Specialist stats: cards, portfolio, active offers, reviews const [cardCount] = await db .select({ total: count() }) .from(specialistCards) .where(and(eq(specialistCards.specialistId, user.id), eq(specialistCards.isActive, true))) const [totalCardCount] = await db .select({ total: count() }) .from(specialistCards) .where(eq(specialistCards.specialistId, user.id)) const [portfolioCount] = await db .select({ total: count() }) .from(portfolioItems) .where(eq(portfolioItems.specialistId, user.id)) const [offerCount] = await db .select({ total: count() }) .from(offers) .where(and(eq(offers.specialistId, user.id), eq(offers.status, 'pending'))) const [reviewCount] = await db .select({ total: count() }) .from(reviews) .where(eq(reviews.targetId, user.id)) stats = { cards: cardCount?.total ?? 0, totalCards: totalCardCount?.total ?? 0, maxCards: plan?.maxCards ?? 1, portfolioItems: portfolioCount?.total ?? 0, maxPortfolioItems: plan?.maxPortfolioItems ?? 5, activeOffers: offerCount?.total ?? 0, reviewsReceived: reviewCount?.total ?? 0, unreadNotifications: unreadNotifications?.total ?? 0, } } else { // Customer stats: active tasks, completed tasks const [activeTaskCount] = await db .select({ total: count() }) .from(tasks) .where(and(eq(tasks.customerId, user.id), eq(tasks.status, 'open'))) const [completedTaskCount] = await db .select({ total: count() }) .from(tasks) .where(and(eq(tasks.customerId, user.id), eq(tasks.status, 'completed'))) stats = { activeTasks: activeTaskCount?.total ?? 0, completedTasks: completedTaskCount?.total ?? 0, unreadNotifications: unreadNotifications?.total ?? 0, } } // Google Calendar status (for specialists) let googleCalendar = null if (isHelperReady && plan?.hasGoogleCalendar) { try { googleCalendar = await getCalendarStatus(user.id) } catch { googleCalendar = { connected: false } } } // Plan expiration const planExpiration = user.planExpiresAt ? { expiresAt: user.planExpiresAt, daysLeft: Math.max(0, Math.ceil((new Date(user.planExpiresAt).getTime() - Date.now()) / (1000 * 60 * 60 * 24))), } : null // Feature summary const features = plan ? { canContactFreePlan: plan.canContactFreePlan, canContactProPlan: plan.canContactProPlan, canContactAll: plan.canContactAll, canShowContactInfo: plan.canShowContactInfo, canViewPhone: plan.canViewPhone, canUploadVideo: plan.canUploadVideo, hasFavorites: plan.hasFavorites, hasGoogleCalendar: plan.hasGoogleCalendar, hasVerifiedBadge: plan.hasVerifiedBadge, hasPersonalSite: plan.hasPersonalSite, hasCanHelpNowStatus: plan.hasCanHelpNowStatus, hasNeedHelpStatus: plan.hasNeedHelpStatus, hasAutoResponse: plan.hasAutoResponse, hasAutoMatch: plan.hasAutoMatch, hasPriceList: plan.hasPriceList, highlightedReviews: plan.highlightedReviews, searchBoost: plan.searchBoost, notifyNewTasks: plan.notifyNewTasks, } : null const [showPricingSetting] = await db .select({ value: settingsTable.value }) .from(settingsTable) .where(eq(settingsTable.key, 'general.showPricing')) return c.json({ plan: plan ? { id: plan.id, name: plan.name, role: plan.role, tier: plan.tier, price: plan.price, } : null, planExpiration, usage, stats, features, showPricing: showPricingSetting?.value !== 'false', googleCalendar, personalSiteSlug: user.personalSiteSlug, }) }) export default app