perf: use cached database data for dashboard stats
- Created /api/dashboard/stats endpoint that queries local PostgreSQL only - Updated companies API to use cached database instead of Autotask API - Dashboard now loads instantly from cached data instead of waiting for external APIs - Scheduled syncs keep data fresh throughout the day
This commit is contained in:
parent
6df319166d
commit
db4d431d26
3 changed files with 92 additions and 49 deletions
|
|
@ -1,18 +1,15 @@
|
|||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { getAutotaskClient } from '@/lib/services/autotask-factory';
|
||||
import { postgresClient } from '@/lib/services/postgres-client';
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const client = getAutotaskClient();
|
||||
const allCompanies = await client.getAllCompanies();
|
||||
const result = await postgresClient.query(
|
||||
'SELECT * FROM companies WHERE is_active = true AND company_type = 1 ORDER BY company_name'
|
||||
);
|
||||
|
||||
// Filter to only show customers (companyType = 1)
|
||||
// companyType 1 = Customer, 2 = Lead, 3 = Prospect, 4 = Dead, 5 = Cancelation, 6 = Vendor, 7 = Partner
|
||||
const companies = allCompanies.filter(company => company.companyType === 1);
|
||||
|
||||
return NextResponse.json({ companies });
|
||||
return NextResponse.json({ companies: result.rows });
|
||||
} catch (error) {
|
||||
console.error('Error fetching companies:', error);
|
||||
console.error('Error fetching companies from database:', error);
|
||||
return NextResponse.json(
|
||||
{ error: error instanceof Error ? error.message : 'Failed to fetch companies' },
|
||||
{ status: 500 }
|
||||
|
|
|
|||
77
app/api/dashboard/stats/route.ts
Normal file
77
app/api/dashboard/stats/route.ts
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { postgresClient } from '@/lib/services/postgres-client';
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
// Get companies count from database
|
||||
const companiesResult = await postgresClient.query(
|
||||
'SELECT COUNT(*) as total, COUNT(CASE WHEN is_active = true THEN 1 END) as active FROM companies WHERE company_type = 1'
|
||||
);
|
||||
|
||||
// Get Auvik mapping stats from database
|
||||
const auvikResult = await postgresClient.query(
|
||||
'SELECT COUNT(*) as mapped FROM auvik_tenant_mappings WHERE autotask_company_id IS NOT NULL'
|
||||
);
|
||||
|
||||
// Get total Auvik tenants count
|
||||
const auvikTotalResult = await postgresClient.query(
|
||||
'SELECT COUNT(*) as total FROM auvik_tenants'
|
||||
);
|
||||
|
||||
// Get RMM mapping stats from database
|
||||
const rmmResult = await postgresClient.query(
|
||||
'SELECT COUNT(*) as mapped FROM rmm_site_mappings WHERE autotask_company_id IS NOT NULL'
|
||||
);
|
||||
|
||||
// Get total RMM sites count
|
||||
const rmmTotalResult = await postgresClient.query(
|
||||
'SELECT COUNT(*) as total FROM rmm_sites'
|
||||
);
|
||||
|
||||
// Get quotes count (if quotes table exists)
|
||||
let quotesOpen = 0;
|
||||
let quotesTotal = 0;
|
||||
try {
|
||||
const quotesResult = await postgresClient.query(
|
||||
"SELECT COUNT(*) as open FROM quotes WHERE status = 'open'"
|
||||
);
|
||||
quotesOpen = parseInt(quotesResult.rows[0]?.open || '0');
|
||||
|
||||
const quotesTotalResult = await postgresClient.query(
|
||||
'SELECT COUNT(*) as total FROM quotes'
|
||||
);
|
||||
quotesTotal = parseInt(quotesTotalResult.rows[0]?.total || '0');
|
||||
} catch {
|
||||
// Quotes table may not exist yet
|
||||
}
|
||||
|
||||
const stats = {
|
||||
companies: {
|
||||
total: parseInt(companiesResult.rows[0]?.total || '0'),
|
||||
active: parseInt(companiesResult.rows[0]?.active || '0'),
|
||||
},
|
||||
mappings: {
|
||||
auvik: {
|
||||
mapped: parseInt(auvikResult.rows[0]?.mapped || '0'),
|
||||
unmapped: parseInt(auvikTotalResult.rows[0]?.total || '0') - parseInt(auvikResult.rows[0]?.mapped || '0'),
|
||||
},
|
||||
rmm: {
|
||||
mapped: parseInt(rmmResult.rows[0]?.mapped || '0'),
|
||||
unmapped: parseInt(rmmTotalResult.rows[0]?.total || '0') - parseInt(rmmResult.rows[0]?.mapped || '0'),
|
||||
},
|
||||
},
|
||||
quotes: {
|
||||
open: quotesOpen,
|
||||
total: quotesTotal,
|
||||
},
|
||||
};
|
||||
|
||||
return NextResponse.json(stats);
|
||||
} catch (error) {
|
||||
console.error('Error fetching dashboard stats:', error);
|
||||
return NextResponse.json(
|
||||
{ error: 'Failed to fetch dashboard stats' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -69,52 +69,21 @@ export default function DashboardPage() {
|
|||
|
||||
const fetchStats = async () => {
|
||||
try {
|
||||
// Fetch companies
|
||||
const companiesRes = await fetch('/api/companies');
|
||||
const companiesData = await companiesRes.json();
|
||||
|
||||
// Fetch Auvik mappings
|
||||
const auvikRes = await fetch('/api/auvik/tenant-mappings?includeUnmapped=true');
|
||||
const auvikData = await auvikRes.json();
|
||||
|
||||
// Fetch RMM mappings
|
||||
const rmmRes = await fetch('/api/rmm/site-mappings?includeUnmapped=true');
|
||||
const rmmData = await rmmRes.json();
|
||||
|
||||
// Fetch SalesBldr quotes
|
||||
let quotesData = { results: [], total: 0 };
|
||||
try {
|
||||
const quotesRes = await fetch('/api/salesbldr/quotes?status=open&size=100');
|
||||
if (quotesRes.ok) {
|
||||
quotesData = await quotesRes.json();
|
||||
}
|
||||
} catch (quotesError) {
|
||||
console.error('Error fetching quotes:', quotesError);
|
||||
}
|
||||
// Fetch cached stats from database (fast, no external API calls)
|
||||
const statsRes = await fetch('/api/dashboard/stats');
|
||||
const statsData = await statsRes.json();
|
||||
|
||||
setStats({
|
||||
companies: {
|
||||
total: companiesData.companies?.length || 0,
|
||||
active: companiesData.companies?.filter((c: any) => c.isActive).length || 0
|
||||
},
|
||||
companies: statsData.companies || { total: 0, active: 0 },
|
||||
configurationItems: {
|
||||
total: 0, // Would need to fetch this
|
||||
total: 0,
|
||||
active: 0
|
||||
},
|
||||
mappings: {
|
||||
auvik: {
|
||||
mapped: auvikData.stats?.mapped || 0,
|
||||
unmapped: auvikData.stats?.unmapped || 0
|
||||
},
|
||||
rmm: {
|
||||
mapped: rmmData.stats?.mapped || 0,
|
||||
unmapped: rmmData.stats?.unmapped || 0
|
||||
}
|
||||
mappings: statsData.mappings || {
|
||||
auvik: { mapped: 0, unmapped: 0 },
|
||||
rmm: { mapped: 0, unmapped: 0 }
|
||||
},
|
||||
quotes: {
|
||||
open: quotesData.results?.length || 0,
|
||||
total: quotesData.total || 0
|
||||
}
|
||||
quotes: statsData.quotes || { open: 0, total: 0 }
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error fetching dashboard stats:', error);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue