├── .eslintrc.json ├── app ├── favicon.ico ├── page.tsx ├── users │ ├── definitions.ts │ ├── userSchema.ts │ ├── data-table-view-options.tsx │ ├── data-table-toolbar.tsx │ ├── data-table-row-actions.tsx │ ├── data-table-column-header.tsx │ ├── columns.tsx │ ├── data-table.tsx │ ├── data-table-pagination.tsx │ ├── data-table-faceted-filter.tsx │ └── users.ts ├── layout.tsx └── globals.css ├── next.config.mjs ├── postcss.config.js ├── lib └── utils.ts ├── types.d.ts ├── components.json ├── .gitignore ├── public ├── vercel.svg └── next.svg ├── prettier.config.js ├── tsconfig.json ├── notes.md ├── components └── ui │ ├── separator.tsx │ ├── input.tsx │ ├── badge.tsx │ ├── popover.tsx │ ├── button.tsx │ ├── table.tsx │ ├── dialog.tsx │ ├── command.tsx │ ├── select.tsx │ └── dropdown-menu.tsx ├── package.json ├── README.md └── tailwind.config.ts /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EliteArmy/shadcn-tables-example/HEAD/app/favicon.ico -------------------------------------------------------------------------------- /next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {}; 3 | 4 | export default nextConfig; 5 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { type ClassValue, clsx } from "clsx" 2 | import { twMerge } from "tailwind-merge" 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)) 6 | } 7 | -------------------------------------------------------------------------------- /types.d.ts: -------------------------------------------------------------------------------- 1 | type User = { 2 | id: string; 3 | userName: string; 4 | phone: string; 5 | email: string; 6 | location: string; 7 | role: "client" | "provider"; 8 | status: "active" | "inactive"; 9 | image: string; 10 | rtn?: string; 11 | otherInformation?: string; 12 | 13 | createdAt?: date; 14 | updatedAt?: date; 15 | }; 16 | -------------------------------------------------------------------------------- /components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "default", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.ts", 8 | "css": "app/globals.css", 9 | "baseColor": "slate", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils" 16 | } 17 | } -------------------------------------------------------------------------------- /app/page.tsx: -------------------------------------------------------------------------------- 1 | import { columns } from "@/app/users/columns"; 2 | import DataTable from "@/app/users/data-table"; 3 | import { users } from "@/app/users/users"; 4 | 5 | export default async function Home() { 6 | // This is where you would fetch external data: 7 | // const exampleExternalData = await fetchExternalDataFunction(); 8 | 9 | // In Our example we use local data 10 | return ( 11 |
12 | 13 |
14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /app/users/definitions.ts: -------------------------------------------------------------------------------- 1 | import { CheckCircledIcon, CrossCircledIcon } from "@radix-ui/react-icons"; 2 | 3 | export const usersStatus = [ 4 | { 5 | value: "active", 6 | label: "Active", 7 | icon: CheckCircledIcon, 8 | }, 9 | { 10 | value: "inactive", 11 | label: "Inactive", 12 | icon: CrossCircledIcon, 13 | }, 14 | ]; 15 | 16 | export const usersRole = [ 17 | { 18 | value: "client", 19 | label: "Client", 20 | }, 21 | { 22 | value: "provider", 23 | label: "Provider", 24 | }, 25 | ]; 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next"; 2 | import { Inter } from "next/font/google"; 3 | 4 | import "./globals.css"; 5 | 6 | const inter = Inter({ subsets: ["latin"] }); 7 | 8 | export const metadata: Metadata = { 9 | title: "Create Next App", 10 | description: "Generated by create next app", 11 | }; 12 | 13 | export default function RootLayout({ 14 | children, 15 | }: Readonly<{ 16 | children: React.ReactNode; 17 | }>) { 18 | return ( 19 | 20 | {children} 21 | 22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: ["@ianvs/prettier-plugin-sort-imports"], 3 | singleQuote: false, 4 | semi: true, 5 | bracketSameLine: true, 6 | printWidth: 100, 7 | trailingComma: "es5", 8 | importOrder: [ 9 | "^(react/(.*)$)|^(react$)", 10 | "^(next/(.*)$)|^(next$)", 11 | "", 12 | "", 13 | "^types$", 14 | "^@/env(.*)$", 15 | "^@/types/(.*)$", 16 | "^@/config/(.*)$", 17 | "^@/lib/(.*)$", 18 | "^@/hooks/(.*)$", 19 | "^@/components/ui/(.*)$", 20 | "^@/components/(.*)$", 21 | "^@/styles/(.*)$", 22 | "^@/app/(.*)$", 23 | "", 24 | "^[./]", 25 | ], 26 | }; 27 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["dom", "dom.iterable", "esnext"], 4 | "allowJs": true, 5 | "skipLibCheck": true, 6 | "strict": true, 7 | "noEmit": true, 8 | "esModuleInterop": true, 9 | "module": "esnext", 10 | "moduleResolution": "bundler", 11 | "resolveJsonModule": true, 12 | "isolatedModules": true, 13 | "jsx": "preserve", 14 | "incremental": true, 15 | "plugins": [ 16 | { 17 | "name": "next" 18 | } 19 | ], 20 | "paths": { 21 | "@/*": ["./*"] 22 | } 23 | }, 24 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 25 | "exclude": ["node_modules"] 26 | } 27 | -------------------------------------------------------------------------------- /notes.md: -------------------------------------------------------------------------------- 1 | documentation: https://nextjs.org/docs 2 | npx create-next-app@latest 3 | 4 | documentation: https://ui.shadcn.com/docs/components/data-table 5 | npx shadcn-ui@latest init 6 | 7 | npx shadcn-ui@latest add table 8 | npx shadcn-ui@latest add badge 9 | npx shadcn-ui@latest add button 10 | npx shadcn-ui@latest add command 11 | npx shadcn-ui@latest add popover 12 | npx shadcn-ui@latest add separator 13 | npx shadcn-ui@latest add dropdown-menu 14 | npx shadcn-ui@latest add input 15 | npx shadcn-ui@latest add select 16 | 17 | npm install @radix-ui/react-icons 18 | 19 | documentation: https://tanstack.com/table/latest/docs/introduction 20 | npm install @tanstack/react-table 21 | 22 | documentatino: https://zod.dev 23 | npm install zod 24 | -------------------------------------------------------------------------------- /components/ui/separator.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as SeparatorPrimitive from "@radix-ui/react-separator" 5 | 6 | import { cn } from "@/lib/utils" 7 | 8 | const Separator = React.forwardRef< 9 | React.ElementRef, 10 | React.ComponentPropsWithoutRef 11 | >( 12 | ( 13 | { className, orientation = "horizontal", decorative = true, ...props }, 14 | ref 15 | ) => ( 16 | 27 | ) 28 | ) 29 | Separator.displayName = SeparatorPrimitive.Root.displayName 30 | 31 | export { Separator } 32 | -------------------------------------------------------------------------------- /app/users/userSchema.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | 3 | export enum Role { 4 | "provider" = "provider", 5 | "client" = "client", 6 | } 7 | export enum UserStatus { 8 | "active" = "active", 9 | "inactive" = "inactive", 10 | } 11 | 12 | export const UserSchema = z.object({ 13 | id: z.string(), 14 | 15 | userName: z.string({}).trim().min(5), 16 | 17 | phone: z.string({}).trim().min(8), 18 | 19 | email: z.string({}).email().trim().or(z.literal("")).optional(), 20 | 21 | location: z.string({}).trim().or(z.literal("")).optional(), 22 | 23 | role: z.nativeEnum(Role, {}), 24 | 25 | status: z.nativeEnum(UserStatus, {}), 26 | 27 | otherInformation: z.string({}).trim().or(z.literal("")).optional(), 28 | 29 | rtn: z.string({}).trim().or(z.literal("")).optional(), 30 | 31 | image: z.string().or(z.literal("")).optional(), 32 | 33 | createdAt: z.date().optional(), 34 | updatedAt: z.date().optional(), 35 | }); 36 | -------------------------------------------------------------------------------- /components/ui/input.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | import { cn } from "@/lib/utils" 4 | 5 | export interface InputProps 6 | extends React.InputHTMLAttributes {} 7 | 8 | const Input = React.forwardRef( 9 | ({ className, type, ...props }, ref) => { 10 | return ( 11 | 20 | ) 21 | } 22 | ) 23 | Input.displayName = "Input" 24 | 25 | export { Input } 26 | -------------------------------------------------------------------------------- /components/ui/badge.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import { cva, type VariantProps } from "class-variance-authority" 3 | 4 | import { cn } from "@/lib/utils" 5 | 6 | const badgeVariants = cva( 7 | "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", 8 | { 9 | variants: { 10 | variant: { 11 | default: 12 | "border-transparent bg-primary text-primary-foreground hover:bg-primary/80", 13 | secondary: 14 | "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80", 15 | destructive: 16 | "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80", 17 | outline: "text-foreground", 18 | }, 19 | }, 20 | defaultVariants: { 21 | variant: "default", 22 | }, 23 | } 24 | ) 25 | 26 | export interface BadgeProps 27 | extends React.HTMLAttributes, 28 | VariantProps {} 29 | 30 | function Badge({ className, variant, ...props }: BadgeProps) { 31 | return ( 32 |
33 | ) 34 | } 35 | 36 | export { Badge, badgeVariants } 37 | -------------------------------------------------------------------------------- /public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "shadcn-tables-example", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@radix-ui/react-dialog": "^1.0.5", 13 | "@radix-ui/react-dropdown-menu": "^2.0.6", 14 | "@radix-ui/react-icons": "^1.3.0", 15 | "@radix-ui/react-popover": "^1.0.7", 16 | "@radix-ui/react-select": "^2.0.0", 17 | "@radix-ui/react-separator": "^1.0.3", 18 | "@radix-ui/react-slot": "^1.0.2", 19 | "@tanstack/react-table": "^8.13.2", 20 | "class-variance-authority": "^0.7.0", 21 | "clsx": "^2.1.0", 22 | "cmdk": "^1.0.0", 23 | "lucide-react": "^0.358.0", 24 | "next": "14.1.3", 25 | "next-themes": "^0.3.0", 26 | "react": "^18", 27 | "react-dom": "^18", 28 | "tailwind-merge": "^2.2.1", 29 | "tailwindcss-animate": "^1.0.7", 30 | "zod": "^3.22.4" 31 | }, 32 | "devDependencies": { 33 | "@ianvs/prettier-plugin-sort-imports": "^4.2.1", 34 | "@types/node": "^20", 35 | "@types/react": "^18", 36 | "@types/react-dom": "^18", 37 | "autoprefixer": "^10.0.1", 38 | "eslint": "^8", 39 | "eslint-config-next": "14.1.3", 40 | "postcss": "^8", 41 | "tailwindcss": "^3.3.0", 42 | "typescript": "^5" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /components/ui/popover.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as PopoverPrimitive from "@radix-ui/react-popover" 5 | 6 | import { cn } from "@/lib/utils" 7 | 8 | const Popover = PopoverPrimitive.Root 9 | 10 | const PopoverTrigger = PopoverPrimitive.Trigger 11 | 12 | const PopoverContent = React.forwardRef< 13 | React.ElementRef, 14 | React.ComponentPropsWithoutRef 15 | >(({ className, align = "center", sideOffset = 4, ...props }, ref) => ( 16 | 17 | 27 | 28 | )) 29 | PopoverContent.displayName = PopoverPrimitive.Content.displayName 30 | 31 | export { Popover, PopoverTrigger, PopoverContent } 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). 2 | 3 | ## Getting Started 4 | 5 | First, run the development server: 6 | 7 | ```bash 8 | npm run dev 9 | # or 10 | yarn dev 11 | # or 12 | pnpm dev 13 | # or 14 | bun dev 15 | ``` 16 | 17 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 18 | 19 | You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. 20 | 21 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. 22 | 23 | ## Learn More 24 | 25 | To learn more about Next.js, take a look at the following resources: 26 | 27 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 28 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 29 | 30 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! 31 | 32 | ## Deploy on Vercel 33 | 34 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. 35 | 36 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. 37 | -------------------------------------------------------------------------------- /app/users/data-table-view-options.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { DropdownMenuTrigger } from "@radix-ui/react-dropdown-menu"; 4 | import { MixerHorizontalIcon } from "@radix-ui/react-icons"; 5 | import { Table } from "@tanstack/react-table"; 6 | 7 | import { Button } from "@/components/ui/button"; 8 | import { 9 | DropdownMenu, 10 | DropdownMenuCheckboxItem, 11 | DropdownMenuContent, 12 | DropdownMenuLabel, 13 | DropdownMenuSeparator, 14 | } from "@/components/ui/dropdown-menu"; 15 | 16 | interface DataTableViewOptionsProps { 17 | table: Table; 18 | } 19 | 20 | export function DataTableViewOptions({ table }: DataTableViewOptionsProps) { 21 | return ( 22 | 23 | 24 | 28 | 29 | 30 | {"Edit Columns"} 31 | 32 | {table 33 | .getAllColumns() 34 | .filter((column) => typeof column.accessorFn !== "undefined" && column.getCanHide()) 35 | .map((column) => { 36 | return ( 37 | column.toggleVisibility(!!value)}> 42 | {column.id} 43 | 44 | ); 45 | })} 46 | 47 | 48 | ); 49 | } 50 | -------------------------------------------------------------------------------- /app/users/data-table-toolbar.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { Cross2Icon } from "@radix-ui/react-icons"; 4 | import { Table } from "@tanstack/react-table"; 5 | 6 | import { Button } from "@/components/ui/button"; 7 | import { Input } from "@/components/ui/input"; 8 | import { usersStatus } from "@/app/users/definitions"; 9 | 10 | import { DataTableFacetedFilter } from "./data-table-faceted-filter"; 11 | import { DataTableViewOptions } from "./data-table-view-options"; 12 | 13 | interface DataTableToolbarProps { 14 | table: Table; 15 | } 16 | 17 | export function DataTableToolbar({ table }: DataTableToolbarProps) { 18 | const isFiltered = table.getState().columnFilters.length > 0; 19 | 20 | return ( 21 |
22 |
23 | table.getColumn("userName")?.setFilterValue(event.target.value)} 27 | className="h-8 w-[150px] lg:w-[250px]" 28 | /> 29 | 30 | {table.getColumn("status") && ( 31 | 36 | )} 37 | 38 | {isFiltered && ( 39 | 46 | )} 47 |
48 | 49 |
50 | ); 51 | } 52 | -------------------------------------------------------------------------------- /app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @layer base { 6 | :root { 7 | --background: 0 0% 100%; 8 | --foreground: 222.2 84% 4.9%; 9 | 10 | --card: 0 0% 100%; 11 | --card-foreground: 222.2 84% 4.9%; 12 | 13 | --popover: 0 0% 100%; 14 | --popover-foreground: 222.2 84% 4.9%; 15 | 16 | --primary: 222.2 47.4% 11.2%; 17 | --primary-foreground: 210 40% 98%; 18 | 19 | --secondary: 210 40% 96.1%; 20 | --secondary-foreground: 222.2 47.4% 11.2%; 21 | 22 | --muted: 210 40% 96.1%; 23 | --muted-foreground: 215.4 16.3% 46.9%; 24 | 25 | --accent: 210 40% 96.1%; 26 | --accent-foreground: 222.2 47.4% 11.2%; 27 | 28 | --destructive: 0 84.2% 60.2%; 29 | --destructive-foreground: 210 40% 98%; 30 | 31 | --border: 214.3 31.8% 91.4%; 32 | --input: 214.3 31.8% 91.4%; 33 | --ring: 222.2 84% 4.9%; 34 | 35 | --radius: 0.5rem; 36 | } 37 | 38 | .dark { 39 | --background: 222.2 84% 4.9%; 40 | --foreground: 210 40% 98%; 41 | 42 | --card: 222.2 84% 4.9%; 43 | --card-foreground: 210 40% 98%; 44 | 45 | --popover: 222.2 84% 4.9%; 46 | --popover-foreground: 210 40% 98%; 47 | 48 | --primary: 210 40% 98%; 49 | --primary-foreground: 222.2 47.4% 11.2%; 50 | 51 | --secondary: 217.2 32.6% 17.5%; 52 | --secondary-foreground: 210 40% 98%; 53 | 54 | --muted: 217.2 32.6% 17.5%; 55 | --muted-foreground: 215 20.2% 65.1%; 56 | 57 | --accent: 217.2 32.6% 17.5%; 58 | --accent-foreground: 210 40% 98%; 59 | 60 | --destructive: 0 62.8% 30.6%; 61 | --destructive-foreground: 210 40% 98%; 62 | 63 | --border: 217.2 32.6% 17.5%; 64 | --input: 217.2 32.6% 17.5%; 65 | --ring: 212.7 26.8% 83.9%; 66 | } 67 | } 68 | 69 | @layer base { 70 | * { 71 | @apply border-border; 72 | } 73 | body { 74 | @apply bg-background text-foreground; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /components/ui/button.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import { Slot } from "@radix-ui/react-slot" 3 | import { cva, type VariantProps } from "class-variance-authority" 4 | 5 | import { cn } from "@/lib/utils" 6 | 7 | const buttonVariants = cva( 8 | "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", 9 | { 10 | variants: { 11 | variant: { 12 | default: "bg-primary text-primary-foreground hover:bg-primary/90", 13 | destructive: 14 | "bg-destructive text-destructive-foreground hover:bg-destructive/90", 15 | outline: 16 | "border border-input bg-background hover:bg-accent hover:text-accent-foreground", 17 | secondary: 18 | "bg-secondary text-secondary-foreground hover:bg-secondary/80", 19 | ghost: "hover:bg-accent hover:text-accent-foreground", 20 | link: "text-primary underline-offset-4 hover:underline", 21 | }, 22 | size: { 23 | default: "h-10 px-4 py-2", 24 | sm: "h-9 rounded-md px-3", 25 | lg: "h-11 rounded-md px-8", 26 | icon: "h-10 w-10", 27 | }, 28 | }, 29 | defaultVariants: { 30 | variant: "default", 31 | size: "default", 32 | }, 33 | } 34 | ) 35 | 36 | export interface ButtonProps 37 | extends React.ButtonHTMLAttributes, 38 | VariantProps { 39 | asChild?: boolean 40 | } 41 | 42 | const Button = React.forwardRef( 43 | ({ className, variant, size, asChild = false, ...props }, ref) => { 44 | const Comp = asChild ? Slot : "button" 45 | return ( 46 | 51 | ) 52 | } 53 | ) 54 | Button.displayName = "Button" 55 | 56 | export { Button, buttonVariants } 57 | -------------------------------------------------------------------------------- /app/users/data-table-row-actions.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | // import { UserSchema } from "@/app/users/userSchema"; 4 | import Link from "next/link"; 5 | import { DotsHorizontalIcon } from "@radix-ui/react-icons"; 6 | import { Row } from "@tanstack/react-table"; 7 | import { Eye, Pencil, Trash2 } from "lucide-react"; 8 | 9 | import { Button } from "@/components/ui/button"; 10 | import { 11 | DropdownMenu, 12 | DropdownMenuContent, 13 | DropdownMenuItem, 14 | DropdownMenuTrigger, 15 | } from "@/components/ui/dropdown-menu"; 16 | 17 | interface DataTableRowActionsProps { 18 | row: Row; 19 | } 20 | 21 | export function DataTableRowActions({ row }: DataTableRowActionsProps) { 22 | // const user = UserSchema.parse(row.original); 23 | // console.log(user.id); // Note: use the id for any action (example: delete, view, edit) 24 | 25 | return ( 26 | 27 | 28 | 32 | 33 | 34 | 35 | 41 | 42 | 43 | 44 | 50 | 51 | 52 | 53 | 57 | 58 | 59 | 60 | ); 61 | } 62 | -------------------------------------------------------------------------------- /app/users/data-table-column-header.tsx: -------------------------------------------------------------------------------- 1 | import { ArrowDownIcon, ArrowUpIcon, CaretSortIcon, EyeNoneIcon } from "@radix-ui/react-icons"; 2 | import { Column } from "@tanstack/react-table"; 3 | 4 | import { cn } from "@/lib/utils"; 5 | import { Button } from "@/components/ui/button"; 6 | import { 7 | DropdownMenu, 8 | DropdownMenuContent, 9 | DropdownMenuItem, 10 | DropdownMenuSeparator, 11 | DropdownMenuTrigger, 12 | } from "@/components/ui/dropdown-menu"; 13 | 14 | interface DataTableColumnHeaderProps extends React.HTMLAttributes { 15 | column: Column; 16 | title: string; 17 | } 18 | 19 | export function DataTableColumnHeader({ 20 | column, 21 | title, 22 | className, 23 | }: DataTableColumnHeaderProps) { 24 | if (!column.getCanSort()) { 25 | return
{title}
; 26 | } 27 | 28 | return ( 29 |
30 | 31 | 32 | 42 | 43 | 44 | column.toggleSorting(false)}> 45 | 46 | {"Asc"} 47 | 48 | column.toggleSorting(true)}> 49 | 50 | {"Desc"} 51 | 52 | 53 | column.toggleVisibility(false)}> 54 | 55 | {"Hide"} 56 | 57 | 58 | 59 |
60 | ); 61 | } 62 | -------------------------------------------------------------------------------- /tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from "tailwindcss" 2 | 3 | const config = { 4 | darkMode: ["class"], 5 | content: [ 6 | './pages/**/*.{ts,tsx}', 7 | './components/**/*.{ts,tsx}', 8 | './app/**/*.{ts,tsx}', 9 | './src/**/*.{ts,tsx}', 10 | ], 11 | prefix: "", 12 | theme: { 13 | container: { 14 | center: true, 15 | padding: "2rem", 16 | screens: { 17 | "2xl": "1400px", 18 | }, 19 | }, 20 | extend: { 21 | colors: { 22 | border: "hsl(var(--border))", 23 | input: "hsl(var(--input))", 24 | ring: "hsl(var(--ring))", 25 | background: "hsl(var(--background))", 26 | foreground: "hsl(var(--foreground))", 27 | primary: { 28 | DEFAULT: "hsl(var(--primary))", 29 | foreground: "hsl(var(--primary-foreground))", 30 | }, 31 | secondary: { 32 | DEFAULT: "hsl(var(--secondary))", 33 | foreground: "hsl(var(--secondary-foreground))", 34 | }, 35 | destructive: { 36 | DEFAULT: "hsl(var(--destructive))", 37 | foreground: "hsl(var(--destructive-foreground))", 38 | }, 39 | muted: { 40 | DEFAULT: "hsl(var(--muted))", 41 | foreground: "hsl(var(--muted-foreground))", 42 | }, 43 | accent: { 44 | DEFAULT: "hsl(var(--accent))", 45 | foreground: "hsl(var(--accent-foreground))", 46 | }, 47 | popover: { 48 | DEFAULT: "hsl(var(--popover))", 49 | foreground: "hsl(var(--popover-foreground))", 50 | }, 51 | card: { 52 | DEFAULT: "hsl(var(--card))", 53 | foreground: "hsl(var(--card-foreground))", 54 | }, 55 | }, 56 | borderRadius: { 57 | lg: "var(--radius)", 58 | md: "calc(var(--radius) - 2px)", 59 | sm: "calc(var(--radius) - 4px)", 60 | }, 61 | keyframes: { 62 | "accordion-down": { 63 | from: { height: "0" }, 64 | to: { height: "var(--radix-accordion-content-height)" }, 65 | }, 66 | "accordion-up": { 67 | from: { height: "var(--radix-accordion-content-height)" }, 68 | to: { height: "0" }, 69 | }, 70 | }, 71 | animation: { 72 | "accordion-down": "accordion-down 0.2s ease-out", 73 | "accordion-up": "accordion-up 0.2s ease-out", 74 | }, 75 | }, 76 | }, 77 | plugins: [require("tailwindcss-animate")], 78 | } satisfies Config 79 | 80 | export default config -------------------------------------------------------------------------------- /app/users/columns.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { ColumnDef } from "@tanstack/react-table"; 4 | import clsx from "clsx"; 5 | 6 | import { DataTableColumnHeader } from "@/app/users/data-table-column-header"; 7 | import { DataTableRowActions } from "@/app/users/data-table-row-actions"; 8 | import { usersRole, usersStatus } from "@/app/users/definitions"; 9 | 10 | export const columns: ColumnDef[] = [ 11 | { 12 | accessorKey: "userName", 13 | header: ({ column }) => , 14 | cell: ({ row }) => { 15 | return
{row.getValue("userName")}
; 16 | }, 17 | }, 18 | { 19 | accessorKey: "phone", 20 | header: ({ column }) => , 21 | }, 22 | { 23 | accessorKey: "email", 24 | header: ({ column }) => , 25 | }, 26 | { 27 | accessorKey: "location", 28 | header: ({ column }) => , 29 | }, 30 | { 31 | accessorKey: "role", 32 | header: ({ column }) => , 33 | cell: ({ row }) => { 34 | const role = usersRole.find((role) => role.value === row.getValue("role")); 35 | 36 | if (!role) { 37 | // If a value is not what you expect or does not exist you can return null. 38 | return null; 39 | } 40 | 41 | return {role.label}; 42 | }, 43 | }, 44 | { 45 | accessorKey: "rtn", 46 | header: ({ column }) => , 47 | }, 48 | { 49 | accessorKey: "otherInformation", 50 | header: ({ column }) => , 51 | }, 52 | { 53 | accessorKey: "status", 54 | header: ({ column }) => , 55 | cell: ({ row }) => { 56 | const status = usersStatus.find((status) => status.value === row.getValue("status")); 57 | 58 | if (!status) { 59 | return null; 60 | } 61 | 62 | return ( 63 |
68 | {status.icon && } 69 | {status.label} 70 |
71 | ); 72 | }, 73 | filterFn: (row, id, value) => { 74 | return value.includes(row.getValue(id)); 75 | }, 76 | }, 77 | { 78 | id: "actions", 79 | cell: ({ row }) => , 80 | }, 81 | ]; 82 | -------------------------------------------------------------------------------- /components/ui/table.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | import { cn } from "@/lib/utils" 4 | 5 | const Table = React.forwardRef< 6 | HTMLTableElement, 7 | React.HTMLAttributes 8 | >(({ className, ...props }, ref) => ( 9 |
10 | 15 | 16 | )) 17 | Table.displayName = "Table" 18 | 19 | const TableHeader = React.forwardRef< 20 | HTMLTableSectionElement, 21 | React.HTMLAttributes 22 | >(({ className, ...props }, ref) => ( 23 | 24 | )) 25 | TableHeader.displayName = "TableHeader" 26 | 27 | const TableBody = React.forwardRef< 28 | HTMLTableSectionElement, 29 | React.HTMLAttributes 30 | >(({ className, ...props }, ref) => ( 31 | 36 | )) 37 | TableBody.displayName = "TableBody" 38 | 39 | const TableFooter = React.forwardRef< 40 | HTMLTableSectionElement, 41 | React.HTMLAttributes 42 | >(({ className, ...props }, ref) => ( 43 | tr]:last:border-b-0", 47 | className 48 | )} 49 | {...props} 50 | /> 51 | )) 52 | TableFooter.displayName = "TableFooter" 53 | 54 | const TableRow = React.forwardRef< 55 | HTMLTableRowElement, 56 | React.HTMLAttributes 57 | >(({ className, ...props }, ref) => ( 58 | 66 | )) 67 | TableRow.displayName = "TableRow" 68 | 69 | const TableHead = React.forwardRef< 70 | HTMLTableCellElement, 71 | React.ThHTMLAttributes 72 | >(({ className, ...props }, ref) => ( 73 |
81 | )) 82 | TableHead.displayName = "TableHead" 83 | 84 | const TableCell = React.forwardRef< 85 | HTMLTableCellElement, 86 | React.TdHTMLAttributes 87 | >(({ className, ...props }, ref) => ( 88 | 93 | )) 94 | TableCell.displayName = "TableCell" 95 | 96 | const TableCaption = React.forwardRef< 97 | HTMLTableCaptionElement, 98 | React.HTMLAttributes 99 | >(({ className, ...props }, ref) => ( 100 |
105 | )) 106 | TableCaption.displayName = "TableCaption" 107 | 108 | export { 109 | Table, 110 | TableHeader, 111 | TableBody, 112 | TableFooter, 113 | TableHead, 114 | TableRow, 115 | TableCell, 116 | TableCaption, 117 | } 118 | -------------------------------------------------------------------------------- /app/users/data-table.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import React from "react"; 4 | import { 5 | ColumnDef, 6 | ColumnFiltersState, 7 | flexRender, 8 | getCoreRowModel, 9 | getFilteredRowModel, 10 | getPaginationRowModel, 11 | getSortedRowModel, 12 | SortingState, 13 | useReactTable, 14 | VisibilityState, 15 | } from "@tanstack/react-table"; 16 | 17 | import { 18 | Table, 19 | TableBody, 20 | TableCell, 21 | TableHead, 22 | TableHeader, 23 | TableRow, 24 | } from "@/components/ui/table"; 25 | 26 | import { DataTablePagination } from "./data-table-pagination"; 27 | import { DataTableToolbar } from "./data-table-toolbar"; 28 | 29 | interface DataTableProps { 30 | columns: ColumnDef[]; 31 | data: TData[]; 32 | } 33 | 34 | export default function DataTable({ columns, data }: DataTableProps) { 35 | const [sorting, setSorting] = React.useState([]); 36 | const [columnVisibility, setColumnVisibility] = React.useState({ 37 | location: false, 38 | otherInformation: false, 39 | }); 40 | const [columnFilters, setColumnFilters] = React.useState([]); 41 | 42 | const table = useReactTable({ 43 | data, 44 | columns, 45 | state: { 46 | sorting, 47 | columnVisibility, 48 | columnFilters, 49 | }, 50 | enableRowSelection: true, 51 | getCoreRowModel: getCoreRowModel(), 52 | onSortingChange: setSorting, 53 | getSortedRowModel: getSortedRowModel(), 54 | onColumnVisibilityChange: setColumnVisibility, 55 | getFilteredRowModel: getFilteredRowModel(), 56 | getPaginationRowModel: getPaginationRowModel(), 57 | onColumnFiltersChange: setColumnFilters, 58 | }); 59 | 60 | return ( 61 |
62 | 63 |
64 | 65 | 66 | {table.getHeaderGroups().map((headerGroup) => ( 67 | 68 | {headerGroup.headers.map((header) => { 69 | return ( 70 | 71 | {header.isPlaceholder 72 | ? null 73 | : flexRender(header.column.columnDef.header, header.getContext())} 74 | 75 | ); 76 | })} 77 | 78 | ))} 79 | 80 | 81 | {table.getRowModel().rows?.length ? ( 82 | table.getRowModel().rows.map((row) => ( 83 | 84 | {row.getVisibleCells().map((cell) => ( 85 | 86 | {flexRender(cell.column.columnDef.cell, cell.getContext())} 87 | 88 | ))} 89 | 90 | )) 91 | ) : ( 92 | 93 | 94 | {"No data results"} 95 | 96 | 97 | )} 98 | 99 |
100 |
101 | 102 |
103 | ); 104 | } 105 | -------------------------------------------------------------------------------- /app/users/data-table-pagination.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | ChevronLeftIcon, 3 | ChevronRightIcon, 4 | DoubleArrowLeftIcon, 5 | DoubleArrowRightIcon, 6 | } from "@radix-ui/react-icons"; 7 | import { Table } from "@tanstack/react-table"; 8 | 9 | import { Button } from "@/components/ui/button"; 10 | import { 11 | Select, 12 | SelectContent, 13 | SelectItem, 14 | SelectTrigger, 15 | SelectValue, 16 | } from "@/components/ui/select"; 17 | 18 | interface DataTablePaginationProps { 19 | table: Table; 20 | } 21 | 22 | export function DataTablePagination({ table }: DataTablePaginationProps) { 23 | return ( 24 |
25 |
26 | {table.getFilteredSelectedRowModel().rows.length} of{" "} 27 | {table.getFilteredRowModel().rows.length} {"row(s) selected"}. 28 |
29 |
30 |
31 |

{"Rows per page"}

32 | 48 |
49 |
50 | {"Page"} {table.getState().pagination.pageIndex + 1} of {table.getPageCount()} 51 |
52 |
53 | 61 | 69 | 77 | 85 |
86 |
87 |
88 | ); 89 | } 90 | -------------------------------------------------------------------------------- /components/ui/dialog.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as DialogPrimitive from "@radix-ui/react-dialog" 5 | import { X } from "lucide-react" 6 | 7 | import { cn } from "@/lib/utils" 8 | 9 | const Dialog = DialogPrimitive.Root 10 | 11 | const DialogTrigger = DialogPrimitive.Trigger 12 | 13 | const DialogPortal = DialogPrimitive.Portal 14 | 15 | const DialogClose = DialogPrimitive.Close 16 | 17 | const DialogOverlay = React.forwardRef< 18 | React.ElementRef, 19 | React.ComponentPropsWithoutRef 20 | >(({ className, ...props }, ref) => ( 21 | 29 | )) 30 | DialogOverlay.displayName = DialogPrimitive.Overlay.displayName 31 | 32 | const DialogContent = React.forwardRef< 33 | React.ElementRef, 34 | React.ComponentPropsWithoutRef 35 | >(({ className, children, ...props }, ref) => ( 36 | 37 | 38 | 46 | {children} 47 | 48 | 49 | Close 50 | 51 | 52 | 53 | )) 54 | DialogContent.displayName = DialogPrimitive.Content.displayName 55 | 56 | const DialogHeader = ({ 57 | className, 58 | ...props 59 | }: React.HTMLAttributes) => ( 60 |
67 | ) 68 | DialogHeader.displayName = "DialogHeader" 69 | 70 | const DialogFooter = ({ 71 | className, 72 | ...props 73 | }: React.HTMLAttributes) => ( 74 |
81 | ) 82 | DialogFooter.displayName = "DialogFooter" 83 | 84 | const DialogTitle = React.forwardRef< 85 | React.ElementRef, 86 | React.ComponentPropsWithoutRef 87 | >(({ className, ...props }, ref) => ( 88 | 96 | )) 97 | DialogTitle.displayName = DialogPrimitive.Title.displayName 98 | 99 | const DialogDescription = React.forwardRef< 100 | React.ElementRef, 101 | React.ComponentPropsWithoutRef 102 | >(({ className, ...props }, ref) => ( 103 | 108 | )) 109 | DialogDescription.displayName = DialogPrimitive.Description.displayName 110 | 111 | export { 112 | Dialog, 113 | DialogPortal, 114 | DialogOverlay, 115 | DialogClose, 116 | DialogTrigger, 117 | DialogContent, 118 | DialogHeader, 119 | DialogFooter, 120 | DialogTitle, 121 | DialogDescription, 122 | } 123 | -------------------------------------------------------------------------------- /app/users/data-table-faceted-filter.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { CheckIcon, PlusCircledIcon } from "@radix-ui/react-icons"; 3 | import { Column } from "@tanstack/react-table"; 4 | 5 | import { cn } from "@/lib/utils"; 6 | import { Badge } from "@/components/ui/badge"; 7 | import { Button } from "@/components/ui/button"; 8 | import { 9 | Command, 10 | CommandEmpty, 11 | CommandGroup, 12 | CommandInput, 13 | CommandItem, 14 | CommandList, 15 | CommandSeparator, 16 | } from "@/components/ui/command"; 17 | import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; 18 | import { Separator } from "@/components/ui/separator"; 19 | 20 | interface DataTableFacetedFilterProps { 21 | column?: Column; 22 | title?: string; 23 | options: { 24 | label: string; 25 | value: string; 26 | icon?: React.ComponentType<{ className?: string }>; 27 | }[]; 28 | } 29 | 30 | export function DataTableFacetedFilter({ 31 | column, 32 | title, 33 | options, 34 | }: DataTableFacetedFilterProps) { 35 | const facets = column?.getFacetedUniqueValues(); 36 | const selectedValues = new Set(column?.getFilterValue() as string[]); 37 | 38 | return ( 39 | 40 | 41 | 71 | 72 | 73 | 74 | 75 | 76 | {"No Filter results"} 77 | 78 | {options.map((option) => { 79 | const isSelected = selectedValues.has(option.value); 80 | return ( 81 | { 84 | if (isSelected) { 85 | selectedValues.delete(option.value); 86 | } else { 87 | selectedValues.add(option.value); 88 | } 89 | const filterValues = Array.from(selectedValues); 90 | column?.setFilterValue(filterValues.length ? filterValues : undefined); 91 | }}> 92 |
99 | 100 |
101 | {option.icon && } 102 | {option.label} 103 | {facets?.get(option.value) && ( 104 | 105 | {facets.get(option.value)} 106 | 107 | )} 108 |
109 | ); 110 | })} 111 |
112 | {selectedValues.size > 0 && ( 113 | <> 114 | 115 | 116 | column?.setFilterValue(undefined)} 118 | className="justify-center text-center"> 119 | {"Clean Filters"} 120 | 121 | 122 | 123 | )} 124 |
125 |
126 |
127 |
128 | ); 129 | } 130 | -------------------------------------------------------------------------------- /components/ui/command.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import * as React from 'react'; 4 | import { type DialogProps } from '@radix-ui/react-dialog'; 5 | import { Command as CommandPrimitive } from 'cmdk'; 6 | import { Search } from 'lucide-react'; 7 | 8 | import { cn } from '@/lib/utils'; 9 | import { Dialog, DialogContent } from '@/components/ui/dialog'; 10 | 11 | const Command = React.forwardRef< 12 | React.ElementRef, 13 | React.ComponentPropsWithoutRef 14 | >(({ className, ...props }, ref) => ( 15 | 23 | )); 24 | Command.displayName = CommandPrimitive.displayName; 25 | 26 | interface CommandDialogProps extends DialogProps {} 27 | 28 | const CommandDialog = ({ children, ...props }: CommandDialogProps) => { 29 | return ( 30 | 31 | 32 | 33 | {children} 34 | 35 | 36 | 37 | ); 38 | }; 39 | 40 | const CommandInput = React.forwardRef< 41 | React.ElementRef, 42 | React.ComponentPropsWithoutRef 43 | >(({ className, ...props }, ref) => ( 44 |
47 | 48 | 56 |
57 | )); 58 | 59 | CommandInput.displayName = CommandPrimitive.Input.displayName; 60 | 61 | const CommandList = React.forwardRef< 62 | React.ElementRef, 63 | React.ComponentPropsWithoutRef 64 | >(({ className, ...props }, ref) => ( 65 | 70 | )); 71 | 72 | CommandList.displayName = CommandPrimitive.List.displayName; 73 | 74 | const CommandEmpty = React.forwardRef< 75 | React.ElementRef, 76 | React.ComponentPropsWithoutRef 77 | >((props, ref) => ( 78 | 83 | )); 84 | 85 | CommandEmpty.displayName = CommandPrimitive.Empty.displayName; 86 | 87 | const CommandGroup = React.forwardRef< 88 | React.ElementRef, 89 | React.ComponentPropsWithoutRef 90 | >(({ className, ...props }, ref) => ( 91 | 99 | )); 100 | 101 | CommandGroup.displayName = CommandPrimitive.Group.displayName; 102 | 103 | const CommandSeparator = React.forwardRef< 104 | React.ElementRef, 105 | React.ComponentPropsWithoutRef 106 | >(({ className, ...props }, ref) => ( 107 | 112 | )); 113 | CommandSeparator.displayName = CommandPrimitive.Separator.displayName; 114 | 115 | const CommandItem = React.forwardRef< 116 | React.ElementRef, 117 | React.ComponentPropsWithoutRef 118 | >(({ className, ...props }, ref) => ( 119 | 127 | )); 128 | 129 | CommandItem.displayName = CommandPrimitive.Item.displayName; 130 | 131 | const CommandShortcut = ({ className, ...props }: React.HTMLAttributes) => { 132 | return ( 133 | 137 | ); 138 | }; 139 | CommandShortcut.displayName = 'CommandShortcut'; 140 | 141 | export { 142 | Command, 143 | CommandDialog, 144 | CommandInput, 145 | CommandList, 146 | CommandEmpty, 147 | CommandGroup, 148 | CommandItem, 149 | CommandShortcut, 150 | CommandSeparator, 151 | }; 152 | -------------------------------------------------------------------------------- /components/ui/select.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as SelectPrimitive from "@radix-ui/react-select" 5 | import { Check, ChevronDown, ChevronUp } from "lucide-react" 6 | 7 | import { cn } from "@/lib/utils" 8 | 9 | const Select = SelectPrimitive.Root 10 | 11 | const SelectGroup = SelectPrimitive.Group 12 | 13 | const SelectValue = SelectPrimitive.Value 14 | 15 | const SelectTrigger = React.forwardRef< 16 | React.ElementRef, 17 | React.ComponentPropsWithoutRef 18 | >(({ className, children, ...props }, ref) => ( 19 | span]:line-clamp-1", 23 | className 24 | )} 25 | {...props} 26 | > 27 | {children} 28 | 29 | 30 | 31 | 32 | )) 33 | SelectTrigger.displayName = SelectPrimitive.Trigger.displayName 34 | 35 | const SelectScrollUpButton = React.forwardRef< 36 | React.ElementRef, 37 | React.ComponentPropsWithoutRef 38 | >(({ className, ...props }, ref) => ( 39 | 47 | 48 | 49 | )) 50 | SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName 51 | 52 | const SelectScrollDownButton = React.forwardRef< 53 | React.ElementRef, 54 | React.ComponentPropsWithoutRef 55 | >(({ className, ...props }, ref) => ( 56 | 64 | 65 | 66 | )) 67 | SelectScrollDownButton.displayName = 68 | SelectPrimitive.ScrollDownButton.displayName 69 | 70 | const SelectContent = React.forwardRef< 71 | React.ElementRef, 72 | React.ComponentPropsWithoutRef 73 | >(({ className, children, position = "popper", ...props }, ref) => ( 74 | 75 | 86 | 87 | 94 | {children} 95 | 96 | 97 | 98 | 99 | )) 100 | SelectContent.displayName = SelectPrimitive.Content.displayName 101 | 102 | const SelectLabel = React.forwardRef< 103 | React.ElementRef, 104 | React.ComponentPropsWithoutRef 105 | >(({ className, ...props }, ref) => ( 106 | 111 | )) 112 | SelectLabel.displayName = SelectPrimitive.Label.displayName 113 | 114 | const SelectItem = React.forwardRef< 115 | React.ElementRef, 116 | React.ComponentPropsWithoutRef 117 | >(({ className, children, ...props }, ref) => ( 118 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | {children} 133 | 134 | )) 135 | SelectItem.displayName = SelectPrimitive.Item.displayName 136 | 137 | const SelectSeparator = React.forwardRef< 138 | React.ElementRef, 139 | React.ComponentPropsWithoutRef 140 | >(({ className, ...props }, ref) => ( 141 | 146 | )) 147 | SelectSeparator.displayName = SelectPrimitive.Separator.displayName 148 | 149 | export { 150 | Select, 151 | SelectGroup, 152 | SelectValue, 153 | SelectTrigger, 154 | SelectContent, 155 | SelectLabel, 156 | SelectItem, 157 | SelectSeparator, 158 | SelectScrollUpButton, 159 | SelectScrollDownButton, 160 | } 161 | -------------------------------------------------------------------------------- /app/users/users.ts: -------------------------------------------------------------------------------- 1 | export const users: User[] = [ 2 | { 3 | id: "9953ed85-31a0-4db9-acc8-e25b76176443", 4 | userName: "John Miller", 5 | phone: "+1-555-0101", 6 | email: "john.miller@example.com", 7 | role: "client", 8 | status: "inactive", 9 | location: "4306 Highland Drive, Seattle, WA 98109", 10 | image: "john.miller.jpg", 11 | rtn: "US2347908701", 12 | otherInformation: "John Miller works in a tech startup in Seattle.", 13 | createdAt: new Date("2024-02-07T23:35:52.087Z"), 14 | updatedAt: new Date("2024-02-07T23:38:03.259Z"), 15 | }, 16 | { 17 | id: "328c2bef-d84b-44a2-b5ae-03bd6550c4c4", 18 | userName: "Elizabeth Smith", 19 | phone: "+44-020-8102", 20 | email: "elizabeth.smith@example.co.uk", 21 | role: "client", 22 | status: "active", 23 | location: "22 Camden Road, London, NW1 9DP", 24 | image: "elizabeth.smith.jpg", 25 | rtn: "UK6574829302", 26 | otherInformation: "Elizabeth Smith works in a financial consultancy in London.", 27 | createdAt: new Date("2024-02-07T23:35:52.087Z"), 28 | updatedAt: new Date("2024-02-07T23:38:03.259Z"), 29 | }, 30 | { 31 | id: "9543e3a4-99f2-4fcb-ba5d-f2aaebff6716", 32 | userName: "Noah Wilson", 33 | phone: "+61-8-9200-1234", 34 | email: "noah.wilson@example.com.au", 35 | role: "provider", 36 | status: "inactive", 37 | location: "305 Murray Street, Perth, WA 6000", 38 | image: "noah.wilson.jpg", 39 | rtn: "AU9085471203", 40 | otherInformation: "Noah Wilson is involved in the mining industry in Perth.", 41 | createdAt: new Date("2024-02-07T23:35:52.087Z"), 42 | updatedAt: new Date("2024-02-07T23:38:03.259Z"), 43 | }, 44 | { 45 | id: "bdcba306-57fa-4722-82e3-c4933b09e69b", 46 | userName: "Marie Dubois", 47 | phone: "+33-1-4533-0012", 48 | email: "marie.dubois@example.fr", 49 | role: "client", 50 | status: "active", 51 | location: "14 Rue de Rivoli, 75004 Paris", 52 | image: "marie.dubois.jpg", 53 | rtn: "FR21340987201", 54 | otherInformation: "Marie Dubois works in a fashion house in Paris.", 55 | createdAt: new Date("2024-02-07T23:35:52.087Z"), 56 | updatedAt: new Date("2024-02-07T23:38:03.259Z"), 57 | }, 58 | { 59 | id: "e643dbea-0ab2-4d3d-8bb8-63aedf027a66", 60 | userName: "Wang Wei", 61 | phone: "+86-20-8221-1234", 62 | email: "wang.wei@example.com.cn", 63 | role: "client", 64 | status: "inactive", 65 | location: "206 Huanshi E Rd, Yuexiu District, Guangzhou, Guangdong", 66 | image: "wang.wei.jpg", 67 | rtn: "CN9988321221", 68 | otherInformation: "Wang Wei works for an electronics manufacturing company in Guangzhou.", 69 | createdAt: new Date("2024-02-07T23:35:52.087Z"), 70 | updatedAt: new Date("2024-02-07T23:38:03.259Z"), 71 | }, 72 | { 73 | id: "94093200-c89f-410f-ba96-046f33fabb3e", 74 | userName: "Conor Murphy", 75 | phone: "+353-1-242-1000", 76 | email: "conor.murphy@example.ie", 77 | role: "provider", 78 | status: "active", 79 | location: "17 O'Connell Street, Dublin, D01 T9C2", 80 | image: "conor.murphy.jpg", 81 | rtn: "IE65432108701", 82 | otherInformation: "Conor Murphy works in a pharmaceutical company in Dublin.", 83 | createdAt: new Date("2024-02-07T23:35:52.087Z"), 84 | updatedAt: new Date("2024-02-07T23:38:03.259Z"), 85 | }, 86 | { 87 | id: "4174f655-5cb2-4bd9-a785-ce11f16cebb0", 88 | userName: "Emma Tremblay", 89 | phone: "+1 604-555-0122", 90 | email: "emma.tremblay@example.com", 91 | role: "client", 92 | status: "inactive", 93 | location: "1020 Mainland Street, Vancouver, BC V6B 2T4", 94 | image: "emma.tremblay.jpg", 95 | rtn: "07081999021280", 96 | otherInformation: "Emma Tremblay is engaged in the environmental sector in Canada.", 97 | createdAt: new Date("2024-02-13T15:35:02.010Z"), 98 | updatedAt: new Date("2024-02-13T15:37:03.020Z"), 99 | }, 100 | { 101 | id: "38d5126b-4473-40d2-8142-2e7049c07346", 102 | userName: "Maximilian Bauer", 103 | phone: "+49 30 567890", 104 | email: "maximilian.bauer@example.com", 105 | role: "client", 106 | status: "active", 107 | location: "Hauptstraße 5, 10178 Berlin", 108 | image: "maximilian.bauer.jpg", 109 | rtn: "08081999021280", 110 | otherInformation: "Maximilian Bauer works for an automobile company in Germany.", 111 | createdAt: new Date("2024-02-14T16:39:04.030Z"), 112 | updatedAt: new Date("2024-02-14T16:40:05.040Z"), 113 | }, 114 | { 115 | id: "cb3ae8be-e376-4d26-9cfc-5884348c22ec", 116 | userName: "Sofia Ricci", 117 | phone: "+39 06 12345678", 118 | email: "sofia.ricci@example.com", 119 | role: "provider", 120 | status: "inactive", 121 | location: "Via Roma 15, 00184 Rome", 122 | image: "sofia.ricci.jpg", 123 | rtn: "09081999021280", 124 | otherInformation: "Sofia Ricci is part of the culinary field in Italy.", 125 | createdAt: new Date("2024-02-15T17:41:06.050Z"), 126 | updatedAt: new Date("2024-02-15T17:42:07.060Z"), 127 | }, 128 | { 129 | id: "fa47c0f4-620c-40b3-a16a-b9afa9a88215", 130 | userName: "Arjun Patel", 131 | phone: "+91 22 2771 1234", 132 | email: "arjun.patel@example.com", 133 | role: "client", 134 | status: "active", 135 | location: "142 M.G. Road, Mumbai, Maharashtra 400001", 136 | image: "arjun.patel.jpg", 137 | rtn: "10081999021280", 138 | otherInformation: "Arjun Patel is active in the software industry in India.", 139 | createdAt: new Date("2024-02-16T18:43:08.070Z"), 140 | updatedAt: new Date("2024-02-16T18:44:09.080Z"), 141 | }, 142 | { 143 | id: "8ce5b4d9-5182-4cbf-9d48-f187b377e931", 144 | userName: "Sato Yuki", 145 | phone: "+81 3 3541 1234", 146 | email: "sato.yuki@example.com", 147 | role: "client", 148 | status: "inactive", 149 | location: "2-11-3 Meguro, Tokyo 153-0063", 150 | image: "sato.yuki.jpg", 151 | rtn: "11081999021280", 152 | otherInformation: "Sato Yuki is engaged in the electronics sector in Japan.", 153 | createdAt: new Date("2024-02-17T19:45:10.090Z"), 154 | updatedAt: new Date("2024-02-17T19:46:11.100Z"), 155 | }, 156 | { 157 | id: "cb2c15c3-7fc9-4d51-8b7b-3e636ac6195b", 158 | userName: "Lucas Silva", 159 | phone: "+55 11 9988-7766", 160 | email: "lucas.silva@example.com", 161 | role: "provider", 162 | status: "active", 163 | location: "Rua Oscar Freire, 379, São Paulo, SP 01426-001", 164 | image: "lucas.silva.jpg", 165 | rtn: "12081999021280", 166 | otherInformation: "Lucas Silva works in the agricultural business in Brazil.", 167 | createdAt: new Date("2024-02-18T20:47:12.110Z"), 168 | updatedAt: new Date("2024-02-18T20:48:13.120Z"), 169 | }, 170 | ]; 171 | -------------------------------------------------------------------------------- /components/ui/dropdown-menu.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu" 5 | import { Check, ChevronRight, Circle } from "lucide-react" 6 | 7 | import { cn } from "@/lib/utils" 8 | 9 | const DropdownMenu = DropdownMenuPrimitive.Root 10 | 11 | const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger 12 | 13 | const DropdownMenuGroup = DropdownMenuPrimitive.Group 14 | 15 | const DropdownMenuPortal = DropdownMenuPrimitive.Portal 16 | 17 | const DropdownMenuSub = DropdownMenuPrimitive.Sub 18 | 19 | const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup 20 | 21 | const DropdownMenuSubTrigger = React.forwardRef< 22 | React.ElementRef, 23 | React.ComponentPropsWithoutRef & { 24 | inset?: boolean 25 | } 26 | >(({ className, inset, children, ...props }, ref) => ( 27 | 36 | {children} 37 | 38 | 39 | )) 40 | DropdownMenuSubTrigger.displayName = 41 | DropdownMenuPrimitive.SubTrigger.displayName 42 | 43 | const DropdownMenuSubContent = React.forwardRef< 44 | React.ElementRef, 45 | React.ComponentPropsWithoutRef 46 | >(({ className, ...props }, ref) => ( 47 | 55 | )) 56 | DropdownMenuSubContent.displayName = 57 | DropdownMenuPrimitive.SubContent.displayName 58 | 59 | const DropdownMenuContent = React.forwardRef< 60 | React.ElementRef, 61 | React.ComponentPropsWithoutRef 62 | >(({ className, sideOffset = 4, ...props }, ref) => ( 63 | 64 | 73 | 74 | )) 75 | DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName 76 | 77 | const DropdownMenuItem = React.forwardRef< 78 | React.ElementRef, 79 | React.ComponentPropsWithoutRef & { 80 | inset?: boolean 81 | } 82 | >(({ className, inset, ...props }, ref) => ( 83 | 92 | )) 93 | DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName 94 | 95 | const DropdownMenuCheckboxItem = React.forwardRef< 96 | React.ElementRef, 97 | React.ComponentPropsWithoutRef 98 | >(({ className, children, checked, ...props }, ref) => ( 99 | 108 | 109 | 110 | 111 | 112 | 113 | {children} 114 | 115 | )) 116 | DropdownMenuCheckboxItem.displayName = 117 | DropdownMenuPrimitive.CheckboxItem.displayName 118 | 119 | const DropdownMenuRadioItem = React.forwardRef< 120 | React.ElementRef, 121 | React.ComponentPropsWithoutRef 122 | >(({ className, children, ...props }, ref) => ( 123 | 131 | 132 | 133 | 134 | 135 | 136 | {children} 137 | 138 | )) 139 | DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName 140 | 141 | const DropdownMenuLabel = React.forwardRef< 142 | React.ElementRef, 143 | React.ComponentPropsWithoutRef & { 144 | inset?: boolean 145 | } 146 | >(({ className, inset, ...props }, ref) => ( 147 | 156 | )) 157 | DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName 158 | 159 | const DropdownMenuSeparator = React.forwardRef< 160 | React.ElementRef, 161 | React.ComponentPropsWithoutRef 162 | >(({ className, ...props }, ref) => ( 163 | 168 | )) 169 | DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName 170 | 171 | const DropdownMenuShortcut = ({ 172 | className, 173 | ...props 174 | }: React.HTMLAttributes) => { 175 | return ( 176 | 180 | ) 181 | } 182 | DropdownMenuShortcut.displayName = "DropdownMenuShortcut" 183 | 184 | export { 185 | DropdownMenu, 186 | DropdownMenuTrigger, 187 | DropdownMenuContent, 188 | DropdownMenuItem, 189 | DropdownMenuCheckboxItem, 190 | DropdownMenuRadioItem, 191 | DropdownMenuLabel, 192 | DropdownMenuSeparator, 193 | DropdownMenuShortcut, 194 | DropdownMenuGroup, 195 | DropdownMenuPortal, 196 | DropdownMenuSub, 197 | DropdownMenuSubContent, 198 | DropdownMenuSubTrigger, 199 | DropdownMenuRadioGroup, 200 | } 201 | --------------------------------------------------------------------------------