/opt/canhelp/apps/api/src/routes
Edit: /opt/canhelp/apps/api/src/routes/fomo.ts (6844B)
import { Hono } from 'hono'
import { requireAuth, type AuthVariables } from '../middleware/auth.js'
import { withPlan } from '../middleware/plan.js'
import { getSpecialistFomoDashboard, getCustomerDashboard } from '../lib/fomo.js'
import { db } from '../db.js'
import { autoResponseSettings } from '@canhelp/db'
import { eq } from 'drizzle-orm'
import { getHelperCapability } from '../lib/helper-capability.js'
const app = new Hono<{ Variables: AuthVariables }>()
// GET /fomo/specialist — FOMO dashboard for the current specialist
app.get('/specialist', requireAuth, withPlan, async (c) => {
const user = c.get('user')
const isHelperReady = await getHelperCapability(user.id)
if (!isHelperReady) {
return c.json({ error: 'Specialists only' }, 403)
}
const plan = c.get('plan' as never) as { tier: string } | null
const dashboard = await getSpecialistFomoDashboard(user.id)
// Tier-specific messaging
const tier = plan?.tier ?? 'free'
let fomoMessage: string | null = null
if (tier === 'free') {
if (dashboard.today.missedTotal > 0) {
fomoMessage = `Σήμερα χάσατε ${dashboard.today.missedTotal} παραγγελίες (~€${dashboard.today.missedRevenue}). ${dashboard.today.missedByPro > 0 ? `${dashboard.today.missedByPro} πήγαν σε PRO ειδικούς.` : ''} Αναβαθμίστε για να μην χάνετε!`
}
} else if (tier === 'pro') {
if (dashboard.today.receivedOrders > 0) {
fomoMessage = `Καλά πάτε! ${dashboard.today.receivedOrders} παραγγελίες σήμερα. Κάλυψη: ${dashboard.today.coveragePercent}%.`
}
} else if (tier === 'ultimate') {
if (dashboard.today.receivedOrders > 0 || dashboard.today.missedTotal > 0) {
fomoMessage = `Στατιστικά: ${dashboard.today.receivedOrders} ληφθείσες, ${dashboard.today.missedTotal} χαμένες. Κάλυψη: ${dashboard.today.coveragePercent}%.`
}
}
return c.json({ ...dashboard, fomoMessage, tier })
})
// GET /fomo/customer — customer conversion dashboard (task stats overview)
app.get('/customer', requireAuth, withPlan, async (c) => {
const user = c.get('user')
const isHelperReady = await getHelperCapability(user.id)
if (isHelperReady) {
return c.json({ error: 'Customers only' }, 403)
}
const plan = c.get('plan' as never) as { tier: string } | null
const tier = plan?.tier ?? 'free'
const dashboard = await getCustomerDashboard(user.id)
// Tier-specific messaging
let conversionMessage: string | null = null
if (tier === 'free') {
const totalOffers = dashboard.activeTasks.reduce((sum, t) => sum + t.totalOffers, 0)
const totalViews = dashboard.activeTasks.reduce((sum, t) => sum + t.totalViews, 0)
if (totalViews > 0 || totalOffers > 0) {
conversionMessage = `Τα tasks σας είδαν ${totalViews} ειδικοί και λάβατε ${totalOffers} προσφορές. Με PRO θα βλέπετε ποιοι είδαν τα tasks σας!`
}
} else if (tier === 'pro' || tier === 'ultimate') {
const proViews = dashboard.activeTasks.reduce((sum, t) => sum + t.proViews, 0)
if (proViews > 0) {
conversionMessage = `${proViews} PRO ειδικοί είδαν τα tasks σας.`
}
}
return c.json({ ...dashboard, conversionMessage, tier })
})
// GET /fomo/recommendations — tier-specific feature recommendations
app.get('/recommendations', requireAuth, withPlan, async (c) => {
const user = c.get('user')
const plan = c.get('plan' as never) as {
tier: string
hasAutoResponse: boolean
hasAutoMatch: boolean
hasFavorites: boolean
hasGoogleCalendar: boolean
canShowContactInfo: boolean
hasPersonalSite: boolean
} | null
const tier = plan?.tier ?? 'free'
const recommendations: Array<{ key: string; title: string; description: string; action: string }> = []
const isHelperReady = await getHelperCapability(user.id)
if (isHelperReady) {
if (tier === 'free') {
recommendations.push(
{ key: 'upgrade_pro', title: 'Αναβαθμίστε σε Pro', description: 'Λάβετε περισσότερους πελάτες, δείξτε τα στοιχεία επικοινωνίας και προβληθείτε ψηλότερα στην αναζήτηση.', action: '/plans' },
{ key: 'add_portfolio', title: 'Προσθέστε portfolio', description: 'Οι ειδικοί με portfolio λαμβάνουν 3x περισσότερα αιτήματα.', action: '/portfolio' },
)
} else if (tier === 'pro') {
recommendations.push(
{ key: 'upgrade_ultimate', title: 'Αναβαθμίστε σε Ultimate', description: 'Ενεργοποιήστε αυτόματη απόκριση, badge επαλήθευσης και απεριόριστες δεξιότητες.', action: '/plans' },
)
if (!plan?.hasGoogleCalendar) {
recommendations.push(
{ key: 'enable_calendar', title: 'Συνδέστε Google Calendar', description: 'Συγχρονίστε το πρόγραμμά σας αυτόματα.', action: '/schedule' },
)
}
} else if (tier === 'ultimate') {
// Check if auto-response is configured
const [autoResp] = await db
.select({ isEnabled: autoResponseSettings.isEnabled })
.from(autoResponseSettings)
.where(eq(autoResponseSettings.specialistId, user.id))
if (!autoResp?.isEnabled) {
recommendations.push(
{ key: 'enable_auto_response', title: 'Ενεργοποιήστε αυτόματη απόκριση', description: 'Μην χάνετε παραγγελίες — απαντήστε αυτόματα σε κατάλληλα αιτήματα.', action: '/auto-response' },
)
}
}
} else {
if (tier === 'free') {
recommendations.push(
{ key: 'upgrade_pro', title: 'Αναβαθμίστε σε Pro', description: 'Επικοινωνήστε με όλους τους ειδικούς και δείτε στατιστικά αναζήτησης.', action: '/plans' },
)
} else if (tier === 'pro') {
recommendations.push(
{ key: 'upgrade_ultimate', title: 'Αναβαθμίστε σε Ultimate', description: 'Αυτόματος αντιστοίχιση με τον καλύτερο ειδικό για κάθε εργασία.', action: '/plans' },
)
} else if (tier === 'ultimate') {
recommendations.push(
{ key: 'try_auto_match', title: 'Δοκιμάστε αυτόματη αντιστοίχιση', description: 'Βρείτε τον τέλειο ειδικό αυτόματα βάσει βαθμολογίας και εμπειρίας.', action: '/auto-match' },
)
}
}
return c.json({ tier, recommendations })
})
export default app