Files
famapp/src/modules/bangs/manifest.tsx
T
ginnoir b5a966e3a9 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
2026-06-02 04:26:22 -05:00

47 lines
1.4 KiB
TypeScript

import { z } from "zod";
import type { ModuleManifest, WidgetContext } from "../_core/module";
import { getBangStats } from "./server/queries";
import { BangWidget } from "./components/bang-widget";
const bangWidgetConfigSchema = z.object({
maxRecentBangs: z.number().int().min(1).max(20).default(5),
});
async function BangWidgetServer({ config, ctx }: { config: unknown; ctx: WidgetContext }) {
const parsed = bangWidgetConfigSchema.parse(config);
const stats = await getBangStats(ctx.householdId, parsed.maxRecentBangs);
return <BangWidget stats={stats} maxRecentBangs={parsed.maxRecentBangs} />;
}
const bangsManifest: ModuleManifest = {
id: "bangs",
name: "Bangs",
entities: [
{
type: "bangs.event",
label: { singular: "Bang", plural: "Bangs" },
resolveUrl: () => "/",
renderActivity: (entry) => {
const date = entry.payload?.occurredOn as string | undefined;
return date ? `Recorded a bang on ${date}` : "Recorded a bang";
},
},
],
dashboardWidgets: [
{
id: "bangs.counter",
title: "Bang Counter",
description: "Track and celebrate bangs with your household.",
category: "Fun",
defaultSize: { w: 3, h: 3 },
minSize: { w: 2, h: 2 },
defaultPriority: 60,
configSchema: bangWidgetConfigSchema,
defaultConfig: { maxRecentBangs: 5 },
render: (props) => <BangWidgetServer {...props} />,
},
],
};
export default bangsManifest;