← back to Norma

app/api/price-tracker/stats/route.ts

96 lines

import { NextRequest, NextResponse } from 'next/server';
import { query } from '@/lib/db';
import { requireRole } from '@/lib/require-role';

/**
 * GET /api/price-tracker/stats
 * Dashboard widget stats for Price Tracker overview cards.
 */
export async function GET(request: NextRequest) {
  const auth = requireRole(request, 'admin', 'staff');
  if (auth instanceof NextResponse) return auth;

  try {
    // Total CA schools tracked
    const schoolsResult = await query(
      `SELECT COUNT(DISTINCT unit_id) AS count FROM tuition_history WHERE state = 'CA'`,
    );

    // Active unread alerts
    const alertsResult = await query(
      `SELECT COUNT(*) AS count FROM cost_alerts WHERE NOT is_read`,
    );

    // Latest crawl timestamp
    const crawlResult = await query(
      `SELECT MAX(completed_at) AS latest FROM price_crawl_log WHERE status = 'success'`,
    );

    // Average tuition by type (latest year)
    const avgResult = await query(`
      SELECT institution_type,
             ROUND(AVG(tuition_in_state)) AS avg_tuition
      FROM tuition_history
      WHERE state = 'CA'
        AND academic_year = (SELECT MAX(academic_year) FROM tuition_history WHERE state = 'CA')
      GROUP BY institution_type
    `);

    // YoY average tuition change
    const yoyResult = await query(`
      WITH years AS (
        SELECT DISTINCT academic_year FROM tuition_history WHERE state = 'CA'
        ORDER BY academic_year DESC LIMIT 2
      ),
      curr AS (
        SELECT AVG(tuition_in_state) AS avg_t
        FROM tuition_history
        WHERE state = 'CA' AND academic_year = (SELECT MAX(academic_year) FROM years)
      ),
      prev AS (
        SELECT AVG(tuition_in_state) AS avg_t
        FROM tuition_history
        WHERE state = 'CA' AND academic_year = (SELECT MIN(academic_year) FROM years)
      )
      SELECT
        CASE WHEN prev.avg_t > 0
          THEN ROUND(((curr.avg_t - prev.avg_t) / prev.avg_t * 100)::numeric, 1)
          ELSE NULL
        END AS yoy_change_pct
      FROM curr, prev
    `);

    // Current federal rate (Direct Subsidized undergrad)
    const fedResult = await query(`
      SELECT interest_rate FROM federal_rates
      WHERE academic_year = (SELECT MAX(academic_year) FROM federal_rates)
        AND rate_type LIKE '%sub%'
      ORDER BY rate_type LIMIT 1
    `);

    // Data source count
    const sourceResult = await query(
      `SELECT COUNT(DISTINCT data_source) AS count FROM tuition_history`,
    );

    const avgByType: Record<string, number> = {};
    for (const row of avgResult.rows) {
      avgByType[row.institution_type] = parseInt(row.avg_tuition || '0');
    }

    return NextResponse.json({
      schools_tracked: parseInt(schoolsResult.rows[0]?.count || '0'),
      data_sources: parseInt(sourceResult.rows[0]?.count || '0'),
      active_alerts: parseInt(alertsResult.rows[0]?.count || '0'),
      latest_crawl: crawlResult.rows[0]?.latest || null,
      avg_tuition_by_type: avgByType,
      yoy_change_pct: parseFloat(yoyResult.rows[0]?.yoy_change_pct || '0'),
      current_fed_rate: parseFloat(fedResult.rows[0]?.interest_rate || '0'),
    });
  } catch (err: unknown) {
    const message = err instanceof Error ? err.message : 'Unknown error';
    console.error('[api/price-tracker/stats] error:', message);
    return NextResponse.json({ error: 'Failed to fetch stats' }, { status: 500 });
  }
}