brackt/app/components/NotificationSettings.tsx
Claude 2f5d36ae48
Address code review feedback on email notification feature
- sendOnTheClockEmail now fetches season (and currentPickNumber) internally,
  removing the blocking postChainSeason pre-fetch from both make-pick.ts and
  executeAutoPick; callers are now true fire-and-forget with no IIFE needed
- Parallel Promise.all for team, autodraftSettings, and league queries reduces
  sequential DB round-trips from 5 to 3
- Remove team.ownerId type cast; assign to local after the null guard
- Combine the two calculatePickInfo calls for the same pick into one
- Fix popoverOpen/enabled divergence: remove the {enabled && ...} guard on
  PopoverContent so popoverOpen is the sole visibility control
- Unify NotificationsSection feedback pattern: both Discord and email sections
  now read success/error directly from their respective fetcher data via a
  shared feedbackFromFetcher helper; removes the success/error props and the
  verbose cast that was used for email
- Add 13 unit tests for sendOnTheClockEmail covering: early-exit paths
  (no season, past totalPicks, no owner, notifications disabled, autodraft
  enabled, no league), single vs back-to-back subject selection, draft room
  link presence, error logging without throwing, and parallel query execution

https://claude.ai/code/session_01LMGxgYvtE3CF8Jf3u2pXgA
2026-05-20 22:23:30 +00:00

114 lines
3.6 KiB
TypeScript

import { useState } from "react";
import { Switch } from "~/components/ui/switch";
import { Label } from "~/components/ui/label";
import { RadioGroup, RadioGroupItem } from "~/components/ui/radio-group";
import { Popover, PopoverAnchor, PopoverContent } from "~/components/ui/popover";
import type { NotificationMode } from "~/hooks/useDraftNotifications";
interface NotificationSettingsProps {
enabled: boolean;
onEnabledChange: (enabled: boolean) => void;
mode: NotificationMode;
onModeChange: (mode: NotificationMode) => void;
permissionState: NotificationPermission | "unsupported";
switchOnly?: boolean;
}
export function NotificationSettings({
enabled,
onEnabledChange,
mode,
onModeChange,
permissionState,
switchOnly = false,
}: NotificationSettingsProps) {
const [popoverOpen, setPopoverOpen] = useState(false);
if (permissionState === "unsupported") {
return null;
}
if (switchOnly) {
const handleEnabledChange = (val: boolean) => {
onEnabledChange(val);
setPopoverOpen(val);
};
return (
<Popover open={popoverOpen} onOpenChange={setPopoverOpen}>
<PopoverAnchor asChild>
<Switch
aria-label="Enable push notifications"
checked={enabled}
onCheckedChange={handleEnabledChange}
disabled={permissionState === "denied"}
/>
</PopoverAnchor>
<PopoverContent align="end" className="w-48 p-3">
<RadioGroup
value={mode}
onValueChange={(value) => onModeChange(value as NotificationMode)}
className="space-y-2"
>
<div className="flex items-center space-x-2">
<RadioGroupItem value="my_turn" id="notif_header_my_turn" />
<Label htmlFor="notif_header_my_turn" className="text-sm cursor-pointer">
My Turn Only
</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="all_picks" id="notif_header_all_picks" />
<Label htmlFor="notif_header_all_picks" className="text-sm cursor-pointer">
All Picks
</Label>
</div>
</RadioGroup>
</PopoverContent>
</Popover>
);
}
return (
<div>
<div className="flex items-center justify-between">
<Label htmlFor="notifications-switch" className="text-sm font-semibold">
Notifications
</Label>
<Switch
id="notifications-switch"
checked={enabled}
onCheckedChange={onEnabledChange}
disabled={permissionState === "denied"}
/>
</div>
{permissionState === "denied" && (
<p className="text-xs text-muted-foreground mt-2">
Notifications blocked by browser. Update in browser settings to
enable.
</p>
)}
{enabled && (
<RadioGroup
value={mode}
onValueChange={(value) => onModeChange(value as NotificationMode)}
className="space-y-2 mt-3"
>
<div className="flex items-center space-x-2">
<RadioGroupItem value="my_turn" id="notif_my_turn" />
<Label htmlFor="notif_my_turn" className="text-sm cursor-pointer">
My Turn Only
</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="all_picks" id="notif_all_picks" />
<Label htmlFor="notif_all_picks" className="text-sm cursor-pointer">
All Picks
</Label>
</div>
</RadioGroup>
)}
</div>
);
}