feat(bangs): add bang counter dashboard widget

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
This commit is contained in:
ginnoir
2026-06-02 04:26:22 -05:00
parent 48063e4b7f
commit b5a966e3a9
13 changed files with 2002 additions and 2 deletions
+18
View File
@@ -0,0 +1,18 @@
import { index, pgTable, text, timestamp, uuid } from "drizzle-orm/pg-core";
import { households, users } from "../_core/schema";
export const bangEvents = pgTable(
"bang_events",
{
id: uuid("id").primaryKey().defaultRandom(),
householdId: uuid("household_id")
.notNull()
.references(() => households.id, { onDelete: "cascade" }),
recordedBy: uuid("recorded_by").references(() => users.id, { onDelete: "set null" }),
occurredOn: text("occurred_on").notNull(), // YYYY-MM-DD, text to avoid timezone confusion
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
},
(t) => [index("bang_events_household_occurred_idx").on(t.householdId, t.occurredOn)],
);
export type BangEvent = typeof bangEvents.$inferSelect;