Notification preferences
Choose which alerts arrive by email, push or not at all.
Built with
Notifications
Changes save as you make them.
Channels
Turn a channel off to pause every alert it sends.
What to tell me about
Leave both switches off to stop an alert completely.
- Leave requestsWhen someone on your team asks for time off.
- Approvals on my requestsWhen your leave or expenses are approved or declined.
- PayslipsWhen a new payslip is ready, on the 28th of each month.
- Birthdays and work anniversariesA heads-up the day before.
- Policy updatesWhen People Ops changes the handbook.
notification-preferences.tsx
"use client";
import { useState } from "react";
import { LuBell } from "react-icons/lu";
import { Card, Switch, SwitchField } from "@nexera-ui/react";
type Channel = "email" | "push";
const ALERTS = [
{
id: "leave",
label: "Leave requests",
description: "When someone on your team asks for time off.",
},
{
id: "approvals",
label: "Approvals on my requests",
description: "When your leave or expenses are approved or declined.",
},
{
id: "payslips",
label: "Payslips",
description: "When a new payslip is ready, on the 28th of each month.",
},
{
id: "birthdays",
label: "Birthdays and work anniversaries",
description: "A heads-up the day before.",
},
{ id: "policies", label: "Policy updates", description: "When People Ops changes the handbook." },
];
const INITIAL: Record<string, Record<Channel, boolean>> = {
leave: { email: true, push: true },
approvals: { email: true, push: true },
payslips: { email: true, push: false },
birthdays: { email: false, push: true },
policies: { email: true, push: false },
};
/** Notification preferences: turn channels on or off, then choose per alert whether it arrives by email, push or not at all. */
export default function NotificationPreferences() {
const [channels, setChannels] = useState<Record<Channel, boolean>>({ email: true, push: true });
const [alerts, setAlerts] = useState(INITIAL);
const [digest, setDigest] = useState(false);
const setAlert = (id: string, channel: Channel, value: boolean) => {
setAlerts((previous) => ({
...previous,
[id]: { email: false, push: false, ...previous[id], [channel]: value },
}));
};
return (
<div className="@container min-h-[560px] bg-page px-4 py-8 @3xl:px-8">
<div className="mx-auto grid max-w-3xl gap-6">
<header className="grid gap-1">
<h2 className="text-heading-h2 text-primary">Notifications</h2>
<p className="text-body-default text-secondary">Changes save as you make them.</p>
</header>
<Card
title="Channels"
titleLevel={3}
icon={<LuBell />}
body="Turn a channel off to pause every alert it sends."
>
<div className="grid gap-4 pt-2">
<SwitchField
label="Email"
description="To ayesha.khan@northwind.io"
isSelected={channels.email}
onChange={(value) => {
setChannels((previous) => ({ ...previous, email: value }));
}}
/>
<SwitchField
label="Push notifications"
description="On the Northwind app for iPhone"
isSelected={channels.push}
onChange={(value) => {
setChannels((previous) => ({ ...previous, push: value }));
}}
/>
<SwitchField
label="Send one daily digest instead of separate emails"
description="Arrives at 8:00 London time"
isSelected={digest}
isDisabled={!channels.email}
onChange={setDigest}
/>
</div>
</Card>
<Card
title="What to tell me about"
titleLevel={3}
body="Leave both switches off to stop an alert completely."
>
<ul className="m-0 grid list-none divide-y divide-subtle p-0">
{ALERTS.map((alert) => (
<li
key={alert.id}
className="flex flex-wrap items-center justify-between gap-x-6 gap-y-3 py-4"
>
<div className="grid min-w-0 flex-1 basis-56 gap-0.5">
<span className="text-body-strong text-primary">{alert.label}</span>
<span className="text-body-small text-secondary">{alert.description}</span>
</div>
<div className="flex gap-6">
{(["email", "push"] as const).map((channel) => (
<Switch
key={channel}
size="sm"
aria-label={`${alert.label} by ${channel === "email" ? "email" : "push"}`}
isSelected={channels[channel] && (alerts[alert.id]?.[channel] ?? false)}
isDisabled={!channels[channel]}
onChange={(value) => {
setAlert(alert.id, channel, value);
}}
>
{channel === "email" ? "Email" : "Push"}
</Switch>
))}
</div>
</li>
))}
</ul>
</Card>
</div>
</div>
);
}
More settings and preferences blocks
- Profile photoUpload, crop to a circle and save a profile photo in three steps.
- AppearanceTheme, density and accent colour.
- SecurityPassword, two-step verification and active sessions.
- Team members and rolesInvite, change roles and remove members.
- Billing and planCurrent plan, payment method and invoices.
- IntegrationsConnect and configure other apps.
- Delete accountA clear warning and a confirmation step.