Access Requests Queue
Review pending access requests with role assignment and one-click approve/deny actions.
Preview
Features
- Inline role assignment for each request
- Approve and deny actions with loading states
- Avatar fallback with initials
- Relative timestamp for request time
- Empty and loading states included
Install Summary
This kit will add the following files and dependencies to your project. Download the bundle and extract it into your project root.
Component Files
| File | Path |
|---|---|
| access-requests.tsx | components/user-management/access-requests/access-requests.tsx |
| access-request-row.tsx | components/user-management/access-requests/access-request-row.tsx |
| types.ts | components/user-management/access-requests/types.ts |
| index.ts | components/user-management/access-requests/index.ts |
Utility Files
| File | Path |
|---|---|
| avatar.ts | lib/avatar.ts |
| format.ts | lib/format.ts |
| design-tokens.ts | lib/design-tokens.ts |
Dependencies
Install these dependencies before using the component:
Terminal
bash
npx shadcn@latest add button select avatar skeletonTerminal
bash
npm install @phosphor-icons/reactInstallation
Download the complete bundle as a ZIP file, or copy the text bundle to your clipboard:
Component Files
The component consists of the following files:
1. access-requests.tsx
access-requests.tsx
tsx
"use client";
import { ShieldCheck } from "@phosphor-icons/react";
import { cn } from "@/lib/utils";
import { Skeleton } from "@/components/ui/skeleton";2. access-request-row.tsx
access-request-row.tsx
tsx
"use client";
import { useEffect, useMemo, useState } from "react";
import { CheckCircle, XCircle, CircleNotch } from "@phosphor-icons/react";
import { Button } from "@/components/ui/button";3. types.ts
types.ts
tsx
export interface AccessRequestRole {
value: string;
label: string;
description?: string;
}4. index.ts
index.ts
tsx
export { AccessRequestsQueue } from "./access-requests";
export type * from "./types";
Shared Utilities
This component uses shared utility functions. These are included in the bundle above:
avatar.ts
avatar.ts
tsx
export function getInitials(name: string): string {
return name
.split(" ")
.map((n) => n[0])
.join("")
.toUpperCase()
.slice(0, 2);
}
const avatarColors = [
"bg-red-500",
"bg-orange-500",
"bg-amber-500",
"bg-yellow-500",
"bg-lime-500",
"bg-green-500",
"bg-emerald-500",
"bg-teal-500",
"bg-cyan-500",
"bg-sky-500",
"bg-blue-500",
"bg-indigo-500",
"bg-violet-500",
"bg-purple-500",
"bg-fuchsia-500",
"bg-pink-500",
"bg-rose-500",
];
export function getAvatarColor(name: string): string {
let hash = 0;
for (let i = 0; i < name.length; i++) {
hash = name.charCodeAt(i) + ((hash << 5) - hash);
}
return avatarColors[Math.abs(hash) % avatarColors.length];
}
format.ts
format.ts
tsx
export function formatDate(date: Date): string {
return date.toLocaleDateString("en-US", {
month: "short",
day: "numeric",
year: "numeric",
});
}
export function formatRelativeTime(date: Date, now: Date = new Date()): string {
const diff = now.getTime() - date.getTime();
const minutes = Math.floor(diff / 60000);
if (minutes < 1) return "Just now";
if (minutes < 60) return `${minutes}m ago`;
const hours = Math.floor(minutes / 60);
if (hours < 24) return `${hours}h ago`;
const days = Math.floor(hours / 24);
if (days < 7) return `${days}d ago`;
return formatDate(date);
}
export function formatPrice(amount: number, currency = "USD"): string {
return new Intl.NumberFormat("en-US", {
style: "currency",
currency,
minimumFractionDigits: 0,
maximumFractionDigits: 2,
}).format(amount);
}
export function formatNumber(num: number): string {
if (num >= 1_000_000) return `${(num / 1_000_000).toFixed(1)}M`;
if (num >= 1_000) return `${(num / 1_000).toFixed(1)}K`;
return num.toString();
}
design-tokens.ts
design-tokens.ts
tsx
// Border radius
export const radius = {
card: "rounded-xl",
badge: "rounded-full",
button: "rounded-lg",Usage
app/settings/members/page.tsx
tsx
"use client";
import { useState } from "react";
import { AccessRequestsQueue, type AccessRequest } from "@/components/user-management/access-requests";
Props
| Prop | Type | Default |
|---|---|---|
| requests | AccessRequest[] | Required |
| roles | AccessRequestRole[] | Defaults provided |
| defaultRole | string | - |
| onApprove | (id, role) => void | - |
| onDeny | (id) => void | - |
| isLoading | boolean | false |
| className | string | - |