Settings
Profile, notifications, billing, team.
- 6 blocks
- 2 free
- 4 Pro
- Light and dark
- Responsive
Unlock 55 Pro blocks and every template
Every preview is open. With Pro you get the code, copy-paste ready, with every update for good.
Security
FreePassword, two-step sign-in and signed-in devices.
settings-security.tsx
"use client";
import { Laptop, ShieldCheck, Smartphone } from "lucide-react";
import * as React from "react";
import { Badge, Button, FormField, PasswordInput, Switch, toast } from "@waveinno/ui";
const DEVICES = [
{ icon: <Laptop />, name: "Chrome on Windows", where: "Dhaka · this device", current: true },
{ icon: <Smartphone />, name: "ParkingOS app on Android", where: "Dhaka · 2 hours ago" },
{ icon: <Laptop />, name: "Safari on macOS", where: "Chattogram · 3 days ago" },
];
export default function SettingsSecurity() {
const [twoStep, setTwoStep] = React.useState(true);
const [devices, setDevices] = React.useState(DEVICES);
return (
<div className="bg-surface px-4 py-10 sm:px-6">
<div className="mx-auto max-w-3xl space-y-6">
<div>
<h1 className="disp text-[24px] font-bold leading-8 text-text">Security</h1>
<p className="text-[13.5px] text-muted">Your password, sign-in and devices.</p>
</div>
<section className="rounded-[18px] border border-border bg-raised p-6 shadow-[var(--lit)]">
<h2 className="text-[15px] font-semibold text-text">Password</h2>
<p className="text-[13px] text-muted">Last changed 3 months ago.</p>
<form
className="mt-5 grid gap-4 sm:grid-cols-2"
onSubmit={(e) => {
e.preventDefault();
(e.target as HTMLFormElement).reset();
toast.success("Password changed");
}}
>
<FormField label="Current password" htmlFor="ss-cur" className="sm:col-span-2">
<PasswordInput id="ss-cur" required autoComplete="current-password" />
</FormField>
<FormField label="New password" htmlFor="ss-new">
<PasswordInput id="ss-new" required strength autoComplete="new-password" />
</FormField>
<FormField label="Repeat it" htmlFor="ss-rep">
<PasswordInput id="ss-rep" required autoComplete="new-password" />
</FormField>
<div className="sm:col-span-2">
<Button type="submit" variant="primary">
Change password
</Button>
</div>
</form>
</section>
<section className="flex items-start justify-between gap-4 rounded-[18px] border border-border bg-raised p-6 shadow-[var(--lit)]">
<div className="flex gap-3">
<span className="grid size-10 shrink-0 place-items-center rounded-[12px] bg-success-subtle text-success">
<ShieldCheck className="size-5" />
</span>
<div>
<h2 className="flex items-center gap-2 text-[15px] font-semibold text-text">
Two-step sign-in {twoStep ? <Badge tone="success">On</Badge> : null}
</h2>
<p className="mt-0.5 text-[13px] text-muted">A code by SMS to +880 1700 ••• 000 whenever you sign in on a new device.</p>
</div>
</div>
<Switch checked={twoStep} onCheckedChange={setTwoStep} aria-label="Two-step sign-in" />
</section>
<section className="rounded-[18px] border border-border bg-raised shadow-[var(--lit)]">
<div className="flex items-center justify-between px-6 pt-5">
<h2 className="text-[15px] font-semibold text-text">Signed-in devices</h2>
{devices.length > 1 ? (
<Button variant="ghost" size="sm" onClick={() => setDevices((d) => d.filter((x) => x.current))}>
Sign out of all others
</Button>
) : null}
</div>
<ul className="mt-3 divide-y divide-border">
{devices.map((d) => (
<li key={d.name} className="flex items-center gap-3 px-6 py-3.5">
<span className="grid size-9 place-items-center rounded-[10px] bg-hover text-muted [&_svg]:size-[18px]">{d.icon}</span>
<span className="min-w-0 flex-1">
<span className="block text-[13.5px] font-medium text-text">{d.name}</span>
<span className="block text-[12px] text-muted">{d.where}</span>
</span>
{d.current ? (
<Badge tone="info" dot={false}>
This device
</Badge>
) : (
<Button variant="secondary" size="sm" onClick={() => setDevices((all) => all.filter((x) => x !== d))}>
Sign out
</Button>
)}
</li>
))}
</ul>
</section>
</div>
</div>
);
}Appearance
FreeTheme, density and language.
settings-appearance.tsx
"use client";
import { Check, Monitor, Moon, Sun } from "lucide-react";
import * as React from "react";
import { Segmented, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, cn } from "@waveinno/ui";
const THEMES = [
{ id: "light", label: "Light", icon: <Sun /> },
{ id: "dark", label: "Dark", icon: <Moon /> },
{ id: "system", label: "System", icon: <Monitor /> },
] as const;
function Preview({ dark }: { dark: boolean }) {
return (
<span className={cn("block rounded-[10px] p-2", dark ? "bg-[#0b1020]" : "bg-[#f4f6fb]")}>
<span className="flex gap-1.5">
<span className={cn("w-1/4 space-y-1 rounded-[6px] p-1.5", dark ? "bg-white/[.06]" : "bg-white")}>
<span className="block h-1.5 rounded-full bg-[linear-gradient(90deg,var(--brand-1),var(--brand-2))]" />
<span className={cn("block h-1.5 rounded-full", dark ? "bg-white/15" : "bg-[#dfe3ee]")} />
<span className={cn("block h-1.5 rounded-full", dark ? "bg-white/15" : "bg-[#dfe3ee]")} />
</span>
<span className={cn("flex-1 space-y-1.5 rounded-[6px] p-1.5", dark ? "bg-white/[.06]" : "bg-white")}>
<span className={cn("block h-2 w-1/2 rounded-full", dark ? "bg-white/30" : "bg-[#c9cfdd]")} />
<span className={cn("block h-6 rounded-[4px]", dark ? "bg-white/10" : "bg-[#eef0f6]")} />
</span>
</span>
</span>
);
}
export default function SettingsAppearance() {
const [theme, setTheme] = React.useState<(typeof THEMES)[number]["id"]>("system");
const [density, setDensity] = React.useState<"comfortable" | "compact">("comfortable");
const [lang, setLang] = React.useState("en");
return (
<div className="bg-surface px-4 py-10 sm:px-6">
<div className="mx-auto max-w-3xl">
<h1 className="disp text-[24px] font-bold leading-8 text-text">Appearance</h1>
<p className="text-[13.5px] text-muted">How the dashboard looks on this device.</p>
<section className="mt-6 divide-y divide-border rounded-[18px] border border-border bg-raised shadow-[var(--lit)]">
<div className="p-6">
<h2 className="text-[15px] font-semibold text-text">Theme</h2>
<div role="radiogroup" aria-label="Theme" className="mt-4 grid grid-cols-3 gap-3">
{THEMES.map((t) => (
<button
key={t.id}
type="button"
role="radio"
aria-checked={theme === t.id}
onClick={() => setTheme(t.id)}
className={cn(
"relative cursor-pointer rounded-[14px] border-2 p-2 text-left transition-[border-color,box-shadow]",
theme === t.id ? "border-primary shadow-[0_0_0_4px_var(--primary-subtle)]" : "border-border hover:border-border2",
)}
>
{t.id === "system" ? (
<span className="grid grid-cols-2 overflow-hidden rounded-[10px]">
<Preview dark={false} />
<Preview dark />
</span>
) : (
<Preview dark={t.id === "dark"} />
)}
<span className="mt-2 flex items-center gap-1.5 px-1 text-[13px] font-medium text-text [&_svg]:size-3.5">
{t.icon} {t.label}
</span>
{theme === t.id ? (
<span className="absolute right-3 top-3 grid size-5 place-items-center rounded-full bg-primary text-white">
<Check className="size-3" strokeWidth={3} />
</span>
) : null}
</button>
))}
</div>
</div>
<div className="flex flex-wrap items-center justify-between gap-4 p-6">
<div>
<h2 className="text-[15px] font-semibold text-text">Density</h2>
<p className="text-[13px] text-muted">Compact fits more rows in tables.</p>
</div>
<Segmented
ariaLabel="Density"
value={density}
onChange={setDensity}
options={[
{ value: "comfortable", label: "Comfortable" },
{ value: "compact", label: "Compact" },
]}
/>
</div>
<div className="flex flex-wrap items-center justify-between gap-4 p-6">
<div>
<h2 className="text-[15px] font-semibold text-text">Language</h2>
<p className="text-[13px] text-muted">Menus, emails and receipts.</p>
</div>
<div className="w-48">
<Select value={lang} onValueChange={setLang}>
<SelectTrigger aria-label="Language">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="en">English</SelectItem>
<SelectItem value="bn">বাংলা</SelectItem>
</SelectContent>
</Select>
</div>
</div>
</section>
</div>
</div>
);
}Profile settings
ProPhoto, details and a save bar that appears on change.
Email, SMS and push for each event.
Billing
ProPlan, usage, payment methods and invoices.
Team members
ProInvite by email, roles, pending invites.