105 lines
3.5 KiB
TypeScript
105 lines
3.5 KiB
TypeScript
import { redirect } from "next/navigation"
|
|||
|
|
import { and, desc, eq, gte, inArray } from "drizzle-orm"
|
||
|
|
import { db } from "@/lib/db"
|
||
|
|
import {
|
||
|
|
ai_recommendations,
|
||
|
|
ai_predictions,
|
||
|
|
activity_log,
|
||
|
|
rent_payments,
|
||
|
|
units as unitsTable,
|
||
|
|
maintenance_requests,
|
||
|
|
} from "@/lib/db/schema"
|
||
|
|
import { getSessionUser } from "@/lib/session"
|
||
|
|
import { AiDashboardClient } from "./ai-dashboard-client"
|
||
|
|
|
||
|
|
export const metadata = { title: "AI Dashboard" }
|
||
|
|
|
||
|
|
export default async function AiDashboardPage() {
|
||
|
|
const user = await getSessionUser()
|
||
|
|
if (!user) redirect("/login")
|
||
|
|
|
||
|
|
const now = new Date()
|
||
|
|
const threeMonthsAgo = new Date(now)
|
||
|
|
threeMonthsAgo.setMonth(threeMonthsAgo.getMonth() - 3)
|
||
|
|
|
||
|
|
const [recs, predictions, activityLog, payments, units, maintenance] = await Promise.all([
|
||
|
|
db
|
||
|
|
.select()
|
||
|
|
.from(ai_recommendations)
|
||
|
|
.where(eq(ai_recommendations.user_id, user.id))
|
||
|
|
.orderBy(desc(ai_recommendations.created_at))
|
||
|
|
.limit(3),
|
||
|
|
db
|
||
|
|
.select()
|
||
|
|
.from(ai_predictions)
|
||
|
|
.where(eq(ai_predictions.user_id, user.id))
|
||
|
|
.orderBy(desc(ai_predictions.created_at))
|
||
|
|
.limit(3),
|
||
|
|
db
|
||
|
|
.select()
|
||
|
|
.from(activity_log)
|
||
|
|
.where(and(eq(activity_log.user_id, user.id), eq(activity_log.type, "ai_action")))
|
||
|
|
.orderBy(desc(activity_log.created_at))
|
||
|
|
.limit(5),
|
||
|
|
db
|
||
|
|
.select({ amount: rent_payments.amount, status: rent_payments.status })
|
||
|
|
.from(rent_payments)
|
||
|
|
.where(
|
||
|
|
and(
|
||
|
|
eq(rent_payments.user_id, user.id),
|
||
|
|
gte(rent_payments.due_date, threeMonthsAgo.toISOString().slice(0, 10))
|
||
|
|
)
|
||
|
|
),
|
||
|
|
db
|
||
|
|
.select({ status: unitsTable.status })
|
||
|
|
.from(unitsTable)
|
||
|
|
.where(eq(unitsTable.user_id, user.id)),
|
||
|
|
db
|
||
|
|
.select({ status: maintenance_requests.status, priority: maintenance_requests.priority })
|
||
|
|
.from(maintenance_requests)
|
||
|
|
.where(
|
||
|
|
and(
|
||
|
|
eq(maintenance_requests.user_id, user.id),
|
||
|
|
inArray(maintenance_requests.status, ["open", "in_progress"])
|
||
|
|
)
|
||
|
|
),
|
||
|
|
])
|
||
|
|
|
||
|
|
const allRecsData = await db
|
||
|
|
.select({ status: ai_recommendations.status, action_data: ai_recommendations.action_data })
|
||
|
|
.from(ai_recommendations)
|
||
|
|
.where(eq(ai_recommendations.user_id, user.id))
|
||
|
|
const approvedRecs = allRecsData.filter((r) => r.status === "approved")
|
||
|
|
|
||
|
|
let totalImpact = 0
|
||
|
|
for (const r of approvedRecs) {
|
||
|
|
totalImpact += Number(r.action_data?.estimated_value ?? 0)
|
||
|
|
}
|
||
|
|
|
||
|
|
const occupiedUnits = units?.filter((u: any) => u.status === "occupied").length ?? 0
|
||
|
|
const totalUnits = units?.length ?? 0
|
||
|
|
const occupancyRate = totalUnits > 0 ? Math.round((occupiedUnits / totalUnits) * 100) : 0
|
||
|
|
const totalRevenue = payments?.filter((p: any) => p.status === "paid").reduce((s: number, p: any) => s + Number(p.amount), 0) ?? 0
|
||
|
|
const overdueAmount = payments?.filter((p: any) => p.status === "overdue").reduce((s: number, p: any) => s + Number(p.amount), 0) ?? 0
|
||
|
|
const criticalMaintenance = maintenance?.filter((m: any) => m.priority === "emergency" || m.priority === "high").length ?? 0
|
||
|
|
const riskAlerts = predictions?.filter((p: any) => ["critical", "high"].includes(p.risk_level)).length ?? 0
|
||
|
|
|
||
|
|
return (
|
||
|
|
<AiDashboardClient
|
||
|
|
recentRecs={recs ?? []}
|
||
|
|
recentPredictions={predictions ?? []}
|
||
|
|
activityLog={activityLog ?? []}
|
||
|
|
stats={{
|
||
|
|
totalImpact,
|
||
|
|
approvedRecs: approvedRecs.length,
|
||
|
|
pendingRecs: allRecsData.filter((r) => r.status === "pending").length,
|
||
|
|
occupancyRate,
|
||
|
|
totalRevenue,
|
||
|
|
overdueAmount,
|
||
|
|
criticalMaintenance,
|
||
|
|
riskAlerts,
|
||
|
|
}}
|
||
|
|
/>
|
||
|
|
)
|
||
|
|
}
|