Adds a new "bangs" module with a dashboard widget that tracks household bang events. The widget shows the total count, a configurable recent-bang list, and an "Add Bang" button that accepts a backdatable date. On submission, a full celebration sequence fires: screen flash, widget shake, count pop animation, synthesized firework sound (Web Audio API), 50 full-screen emoji particles via portal, canvas-confetti star bursts, side cannons, and a timed sequence of 7 firework bursts. - New bang_events table (migration 0017) - canvas-confetti dependency for fireworks/confetti effects - CSS keyframe animations: shake, countPop, emojiFloat - Fixes pre-existing Drizzle snapshot chain collision (0007/0008) - Adds OpenPlantBook env vars to compose.yaml
40 lines
1013 B
TypeScript
40 lines
1013 B
TypeScript
import { count, desc, eq } from "drizzle-orm";
|
|
import { db } from "@/lib/db";
|
|
import { users } from "@/modules/_core/schema";
|
|
import { bangEvents } from "../schema";
|
|
|
|
export type BangStatsDto = {
|
|
total: number;
|
|
recent: RecentBangDto[];
|
|
};
|
|
|
|
export type RecentBangDto = {
|
|
id: string;
|
|
occurredOn: string;
|
|
recordedByName: string | null;
|
|
};
|
|
|
|
export async function getBangStats(householdId: string, limit: number): Promise<BangStatsDto> {
|
|
const [totalRow] = await db
|
|
.select({ total: count() })
|
|
.from(bangEvents)
|
|
.where(eq(bangEvents.householdId, householdId));
|
|
|
|
const recent = await db
|
|
.select({
|
|
id: bangEvents.id,
|
|
occurredOn: bangEvents.occurredOn,
|
|
recordedByName: users.name,
|
|
})
|
|
.from(bangEvents)
|
|
.leftJoin(users, eq(bangEvents.recordedBy, users.id))
|
|
.where(eq(bangEvents.householdId, householdId))
|
|
.orderBy(desc(bangEvents.occurredOn), desc(bangEvents.createdAt))
|
|
.limit(limit);
|
|
|
|
return {
|
|
total: totalRow?.total ?? 0,
|
|
recent,
|
|
};
|
|
}
|