# Settings 3

> Agent settings with a status preview card, availability, notification toggles, and a danger zone.

A shadcn/ui block from the [@efferd](https://efferd.com/?utm_source=shadcn.exchange) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/efferd/settings-3).

- Page, with a live preview: https://shadcn.exchange/r/efferd/settings-3
- Screenshot: https://shadcn.exchange/shot/efferd/settings-3/light.png
- Registry item: https://efferd.com/r/settings-3.json
- Categories: settings
- Listed: 2026-10-29, source last changed 2026-10-29

## Install

```bash
npx shadcn@latest add https://shadcn.exchange/r/efferd/settings-3.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- Registry items: `alert-dialog`, `avatar`, `badge`, `button`, `card`, `checkbox`, `field`, `input`, `switch`, `toggle-group`, `@efferd/indicator`, `@efferd/app-shell-3`

## Files

### `registry/blocks/settings/3/agent-preview.tsx`

```tsx
import {
	Avatar,
	AvatarFallback,
	AvatarImage,
} from "@/components/ui/avatar";
import { Badge } from "@/components/ui/badge";
import {
	Card,
	CardContent,
	CardFooter,
	CardHeader,
} from "@/components/ui/card";
import { StatusIndicator } from "@/components/indicator";

const stats = [
	{ label: "CSAT", value: "94%" },
	{ label: "Assigned", value: "6" },
	{ label: "Reply", value: "4.1m" },
] as const;

export function AgentPreview() {
	return (
		<Card className="h-full gap-0 shadow-none dark:ring-0">
			<CardHeader>
				<Badge variant="secondary">Public Preview</Badge>
			</CardHeader>
			<CardContent className="flex flex-1 flex-col items-start justify-center px-(--card-spacing) py-8">
				<Avatar className="size-20">
					<AvatarImage alt="" src="https://github.com/shabanhr.png" />
					<AvatarFallback>SH</AvatarFallback>
				</Avatar>
				<p className="mt-4 text-balance font-heading font-medium text-xl">
					Shaban Haider
				</p>
				<p className="mt-1 text-pretty text-muted-foreground text-sm">
					Support lead · Karachi
				</p>
				<div className="mt-3 flex items-center gap-1.5 text-xs">
					<StatusIndicator color="emerald" />
					<span>Online</span>
					<span className="truncate text-muted-foreground">
						· typically replies in 4.1m
					</span>
				</div>
				{/* <blockquote className="mt-6 w-full text-pretty rounded-lg border bg-muted/40 p-3 text-left text-sm">
					Hi, I&apos;m Shaban. I&apos;ll take it from here.
				</blockquote> */}
			</CardContent>
			<CardFooter className="grid w-full grid-cols-3 divide-x border-t">
				{stats.map((stat) => (
					<div className="flex flex-col gap-1 ps-4" key={stat.label}>
						<p className="text-[10px] text-mono text-muted-foreground">
							{stat.label}
						</p>
						<p className="font-medium text-sm tabular-nums">{stat.value}</p>
					</div>
				))}
			</CardFooter>
		</Card>
	);
}
```

### `registry/blocks/settings/3/availability-settings.tsx`

```tsx
"use client";
import { CardContent } from "@/components/ui/card";
import { FieldGroup, FieldSeparator } from "@/components/ui/field";
import { Input } from "@/components/ui/input";
import { Switch } from "@/components/ui/switch";
import {
	ToggleGroup,
	ToggleGroupItem,
} from "@/components/ui/toggle-group";
import { StatusIndicator } from "@/components/indicator";
import { SettingField } from "@/components/settings-field";
import { SettingsSection } from "@/components/settings-section";

const statuses = [
	{ value: "online", label: "Online", color: "emerald" },
	{ value: "away", label: "Away", color: "amber" },
	{ value: "offline", label: "Offline", color: "rose" },
] as const;

export function AvailabilitySettings() {
	return (
		<SettingsSection
			description="When and how much work gets routed to you."
			title="Availability"
		>
			<CardContent className="flex-1 px-0 py-(--card-spacing)">
				<FieldGroup className="gap-5">
					<SettingField
						description="Shown to teammates in the queue."
						htmlFor="status"
						label="Status"
					>
						<ToggleGroup
							aria-label="Status"
							defaultValue="online"
							id="status"
							type="single"
							variant="outline"
						>
							{statuses.map((status) => (
								<ToggleGroupItem key={status.value} value={status.value}>
									<StatusIndicator color={status.color} pulse={false} />
									{status.label}
								</ToggleGroupItem>
							))}
						</ToggleGroup>
					</SettingField>
					<FieldSeparator />
					<SettingField
						description="Outside these hours you're set to away."
						htmlFor="hours-start"
						label="Working hours"
					>
						<div className="flex items-center gap-2">
							<Input
								aria-label="Start time"
								className="w-32"
								defaultValue="09:00"
								id="hours-start"
								type="time"
							/>
							<span className="text-muted-foreground text-xs">to</span>
							<Input
								aria-label="End time"
								className="w-32"
								defaultValue="17:00"
								type="time"
							/>
						</div>
					</SettingField>

					<FieldSeparator />
					<SettingField
						description="Assign new threads to you automatically."
						htmlFor="auto-assign"
						label="Auto-assign"
						orientation="horizontal"
					>
						<Switch defaultChecked id="auto-assign" />
					</SettingField>
				</FieldGroup>
			</CardContent>
		</SettingsSection>
	);
}
```

### `registry/blocks/settings/3/danger-settings.tsx`

```tsx
import {
	AlertDialog,
	AlertDialogAction,
	AlertDialogCancel,
	AlertDialogContent,
	AlertDialogDescription,
	AlertDialogFooter,
	AlertDialogHeader,
	AlertDialogTitle,
	AlertDialogTrigger,
} from "@/components/ui/alert-dialog";
import { Button } from "@/components/ui/button";
import { CardContent } from "@/components/ui/card";
import { FieldGroup, FieldSeparator } from "@/components/ui/field";
import { SettingField } from "@/components/settings-field";
import { SettingsSection } from "@/components/settings-section";

export function DangerSettings() {
	return (
		<SettingsSection
			className="lg:col-span-4"
			description="Irreversible and destructive actions."
			title="Danger zone"
		>
			<CardContent className="flex-1 px-0 py-(--card-spacing)">
				<FieldGroup className="gap-5">
					<SettingField
						description="End every signed-in session except this one."
						htmlFor="sign-out-sessions"
						label="Sessions"
						orientation="horizontal"
					>
						<Button
							id="sign-out-sessions"
							size="sm"
							type="button"
							variant="outline"
						>
							Sign out all
						</Button>
					</SettingField>
					<FieldSeparator />
					<SettingField
						description="Remove your account and assigned threads."
						htmlFor="delete-account"
						label="Delete account"
						orientation="horizontal"
					>
						<AlertDialog>
							<AlertDialogTrigger asChild>
								<Button
									id="delete-account"
									size="sm"
									type="button"
									variant="destructive"
								>
									Delete
								</Button>
							</AlertDialogTrigger>
							<AlertDialogContent size="sm">
								<AlertDialogHeader>
									<AlertDialogTitle>Delete your account?</AlertDialogTitle>
									<AlertDialogDescription>
										This permanently removes your profile, open conversations
										assigned to you, and notification preferences. This cannot
										be undone.
									</AlertDialogDescription>
								</AlertDialogHeader>
								<AlertDialogFooter>
									<AlertDialogCancel>Cancel</AlertDialogCancel>
									<AlertDialogAction variant="destructive">
										Delete account
									</AlertDialogAction>
								</AlertDialogFooter>
							</AlertDialogContent>
						</AlertDialog>
					</SettingField>
				</FieldGroup>
			</CardContent>
		</SettingsSection>
	);
}
```

### `registry/blocks/settings/3/notification-settings.tsx`

```tsx
import { Fragment } from "react";
import { CardContent } from "@/components/ui/card";
import { Checkbox } from "@/components/ui/checkbox";
import { FieldGroup, FieldSeparator } from "@/components/ui/field";
import { SettingField } from "@/components/settings-field";
import { SettingsSection } from "@/components/settings-section";

const methods = ["Email", "Push", "Desktop"] as const;

type Method = (typeof methods)[number];

const events: {
	id: string;
	label: string;
	description: string;
	enabled: Method[];
}[] = [
	{
		id: "assigned",
		label: "Conversation assigned",
		description: "A thread is routed to you.",
		enabled: ["Email", "Push", "Desktop"],
	},
	{
		id: "reply",
		label: "Customer replied",
		description: "New message on your open threads.",
		enabled: ["Push", "Desktop"],
	},
	{
		id: "mention",
		label: "Mentioned",
		description: "A teammate @mentions you in a note.",
		enabled: ["Email", "Push"],
	},
	{
		id: "sla",
		label: "SLA at risk",
		description: "A reply is due in under 15 minutes.",
		enabled: ["Email", "Push", "Desktop"],
	},
];

export function NotificationSettings() {
	return (
		<SettingsSection
			description="Pick how you hear about each event."
			title="Notifications"
		>
			<CardContent className="flex-1 px-0 py-(--card-spacing)">
				<FieldGroup className="gap-5">
					<div className="flex justify-end px-(--card-spacing)">
						<div className="grid w-56 grid-cols-3 justify-items-center gap-2">
							{methods.map((method) => (
								<span className="text-muted-foreground text-xs" key={method}>
									{method}
								</span>
							))}
						</div>
					</div>
					{events.map((event, index) => (
						<Fragment key={event.id}>
							{index > 0 && <FieldSeparator />}
							<SettingField
								description={event.description}
								htmlFor={`${event.id}-email`}
								label={event.label}
							>
								<div className="grid w-56 grid-cols-3 justify-items-center gap-2">
									{methods.map((method) => (
										<Checkbox
											aria-label={`${event.label} via ${method}`}
											defaultChecked={event.enabled.includes(method)}
											id={`${event.id}-${method.toLowerCase()}`}
											key={method}
										/>
									))}
								</div>
							</SettingField>
						</Fragment>
					))}
				</FieldGroup>
			</CardContent>
		</SettingsSection>
	);
}
```

### `registry/blocks/settings/3/profile-settings.tsx`

```tsx
"use client";
import { useEffect, useState } from "react";
import {
	Avatar,
	AvatarFallback,
	AvatarImage,
} from "@/components/ui/avatar";
import { Button } from "@/components/ui/button";
import { CardContent, CardFooter } from "@/components/ui/card";
import { FieldGroup, FieldSeparator } from "@/components/ui/field";
import { Input } from "@/components/ui/input";

import { SettingField } from "@/components/settings-field";
import { SettingsSection } from "@/components/settings-section";

const defaultSrc = "https://github.com/shabanhr.png";

export function ProfileSettings() {
	return (
		<SettingsSection
			description="How you appear to teammates and customers."
			title="Profile"
		>
			<CardContent className="flex-1 px-0 py-(--card-spacing)">
				<FieldGroup className="gap-5">
					<SettingField
						className="[&>[data-slot=field-content]:last-child]:w-full"
						description="Click to upload a square image, at least 256px."
						htmlFor="avatar"
						label="Your Avatar"
						orientation="horizontal"
					>
						<SettingsAvatar />
					</SettingField>
					<FieldSeparator />
					<SettingField
						description="Used in comments, invoices, and the sidebar."
						htmlFor="display-name"
						label="Display name"
					>
						<Input
							autoComplete="name"
							defaultValue="Shaban Haider"
							id="display-name"
						/>
					</SettingField>
					<FieldSeparator />
					<SettingField
						description="Sign-in and billing receipts go here."
						htmlFor="email"
						label="Primary Email"
					>
						<Input
							autoComplete="email"
							defaultValue="shaban@efferd.com"
							id="email"
							type="email"
						/>
					</SettingField>
				</FieldGroup>
			</CardContent>
			<CardFooter className="justify-end gap-2 border-t">
				<Button type="button" variant="ghost">
					Cancel
				</Button>
				<Button type="button">Save changes</Button>
			</CardFooter>
		</SettingsSection>
	);
}

function SettingsAvatar() {
	const [src, setSrc] = useState(defaultSrc);

	useEffect(() => {
		if (!src.startsWith("blob:")) {
			return;
		}
		return () => URL.revokeObjectURL(src);
	}, [src]);

	return (
		<div className="flex w-full items-center justify-between gap-4">
			<Avatar className="size-16">
				<AvatarImage alt="" src={src} />
				<AvatarFallback>SH</AvatarFallback>
			</Avatar>
			<div className="flex items-center">
				<Button
					className="text-destructive"
					disabled={!src}
					onClick={() => setSrc("")}
					type="button"
					variant="link"
				>
					Delete
				</Button>
				<Button asChild variant="link">
					<label className="cursor-pointer" htmlFor="avatar">
						Update
						<input
							accept="image/*"
							className="sr-only"
							id="avatar"
							onChange={(event) => {
								const file = event.target.files?.[0];
								if (!file) {
									return;
								}
								setSrc(URL.createObjectURL(file));
							}}
							type="file"
						/>
					</label>
				</Button>
			</div>
		</div>
	);
}
```

### `registry/blocks/settings/3/settings-field.tsx`

```tsx
import { cn } from "@/lib/utils";
import {
	Field,
	FieldContent,
	FieldDescription,
	FieldLabel,
} from "@/components/ui/field";

export function SettingField({
	label,
	description,
	htmlFor,
	children,
	className,
	...props
}: React.ComponentProps<typeof Field> & {
	label: string;
	description: string;
	htmlFor: string;
	children: React.ReactNode;
	className?: string;
}) {
	return (
		<Field
			className={cn(
				"px-(--card-spacing) @md/field-group:has-[>[data-slot=field-content]]:items-center",
				className
			)}
			orientation="responsive"
			{...props}
		>
			<FieldContent>
				<FieldLabel className="font-normal" htmlFor={htmlFor}>
					{label}
				</FieldLabel>
				<FieldDescription className="text-xs">{description}</FieldDescription>
			</FieldContent>
			<FieldContent className="items-end">{children}</FieldContent>
		</Field>
	);
}
```

### `registry/blocks/settings/3/settings-section.tsx`

```tsx
import { cn } from "@/lib/utils";
import {
	Card,
	CardDescription,
	CardHeader,
	CardTitle,
} from "@/components/ui/card";

export function SettingsSection({
	title,
	description,
	children,
	className,
	...props
}: Omit<React.ComponentProps<typeof Card>, "title"> & {
	title: string;
	description: string;
}) {
	return (
		<Card
			className={cn("gap-0 pb-0 shadow-none dark:ring-0", className)}
			{...props}
		>
			<CardHeader className="border-b">
				<CardTitle>{title}</CardTitle>
				<CardDescription>{description}</CardDescription>
			</CardHeader>
			{children}
		</Card>
	);
}
```

### `registry/blocks/settings/3/settings.tsx`

```tsx
import { AgentPreview } from "@/components/agent-preview";
import { AvailabilitySettings } from "@/components/availability-settings";
import { DangerSettings } from "@/components/danger-settings";
import { NotificationSettings } from "@/components/notification-settings";
import { ProfileSettings } from "@/components/profile-settings";

export function Settings() {
	return (
		<div className="flex w-full flex-col gap-4">
			<div className="grid w-full gap-4 lg:grid-cols-2 xl:grid-cols-[.3fr_1fr]">
				<AgentPreview />
				<ProfileSettings />
			</div>
			<div className="grid w-full gap-4 xl:grid-cols-2">
				<AvailabilitySettings />
				<NotificationSettings />
			</div>
			<DangerSettings />
		</div>
	);
}
```
