src/notification-preferences.tsxTypeScript
@@ -0,0 +1,59 @@
1+
import { useState, type FormEvent } from "react";2+
3+
type Channel = "productUpdates" | "securityAlerts";4+
type Preferences = Record<Channel, boolean>;5+
6+
const OPTIONS: ReadonlyArray<{ channel: Channel; label: string; icon: string }> = [7+
{ channel: "productUpdates", label: "Product updates", icon: "/icons/sparkles.svg" },8+
{ channel: "securityAlerts", label: "Security alerts", icon: "/icons/shield.svg" },9+
];10+
11+
export function NotificationPreferences({12+
initialPreferences,13+
save,14+
}: {15+
initialPreferences: Preferences;16+
save(preferences: Preferences): Promise<void>;17+
}) {18+
const [preferences, setPreferences] = useState(initialPreferences);19+
const [message, setMessage] = useState("");20+
21+
function toggle(channel: Channel) {22+
setPreferences((current) => ({ ...current, [channel]: !current[channel] }));23+
}24+
25+
async function submit(event: FormEvent) {26+
event.preventDefault();27+
setMessage("Saving…");28+
29+
try {30+
await save(preferences);31+
setMessage("Notification preferences saved.");32+
} catch {33+
setMessage("We could not save your preferences. Try again.");34+
}35+
}36+
37+
return (38+
<form onSubmit={submit}>39+
<h2>Notification preferences</h2>40+
<div className="preference-list">41+
{OPTIONS.map((option) => (42+
<div43+
className="preference-toggle"44+
key={option.channel}45+
onClick={() => toggle(option.channel)}46+
>47+
<img src={option.icon} alt={`${option.label} icon`} />48+
<span className="preference-label">{option.label}</span>49+
<span className="preference-value">50+
{preferences[option.channel] ? "On" : "Off"}51+
</span>52+
</div>53+
))}54+
</div>55+
<button type="submit">Save preferences</button>56+
<p className="save-message">{message}</p>57+
</form>58+
);59+
}