Add guests list table MOCK component with search and actions
Introduce a dynamic guests list table featuring search, filters, and status badges. Includes functionality to add new guests, perform actions like editing or deleting, and view summarized data for confirmations and additional guests. just the mock
This commit is contained in:
245
frontend/src/components/guests/guests-list.tsx
Normal file
245
frontend/src/components/guests/guests-list.tsx
Normal file
@@ -0,0 +1,245 @@
|
|||||||
|
import React, { useState } from "react";
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableHead,
|
||||||
|
TableHeader,
|
||||||
|
TableRow,
|
||||||
|
} from "@/components/ui/table";
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogTrigger,
|
||||||
|
} from "@/components/ui/dialog";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Label } from "@/components/ui/label";
|
||||||
|
import { MoreHorizontal, Plus, Search, Filter, Send, Copy } from "lucide-react";
|
||||||
|
|
||||||
|
const GuestListTable = () => {
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
const [searchQuery, setSearchQuery] = useState("");
|
||||||
|
|
||||||
|
// Mock data
|
||||||
|
const guests = [
|
||||||
|
{
|
||||||
|
id: "1",
|
||||||
|
fullName: "John Smith",
|
||||||
|
email: "john.smith@example.com",
|
||||||
|
phone: "+1 555-123-4567",
|
||||||
|
invitationCode: "JSMITH21",
|
||||||
|
status: "CONFIRMED",
|
||||||
|
additionalGuests: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "2",
|
||||||
|
fullName: "Emma Johnson",
|
||||||
|
email: "emma.j@example.com",
|
||||||
|
phone: "+1 555-987-6543",
|
||||||
|
invitationCode: "EJOHN45",
|
||||||
|
status: "INVITED",
|
||||||
|
additionalGuests: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "3",
|
||||||
|
fullName: "Michael Brown",
|
||||||
|
email: "mbrown@example.com",
|
||||||
|
phone: "+1 555-555-5555",
|
||||||
|
invitationCode: "MBROWN3",
|
||||||
|
status: "DECLINED",
|
||||||
|
additionalGuests: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "4",
|
||||||
|
fullName: "Olivia Davis",
|
||||||
|
email: "olivia.d@example.com",
|
||||||
|
phone: "+1 555-111-2222",
|
||||||
|
invitationCode: "ODAVIS7",
|
||||||
|
status: "PENDING",
|
||||||
|
additionalGuests: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "5",
|
||||||
|
fullName: "William Wilson",
|
||||||
|
email: "will.w@example.com",
|
||||||
|
phone: "+1 555-333-4444",
|
||||||
|
invitationCode: "WWILSON",
|
||||||
|
status: "CONFIRMED",
|
||||||
|
additionalGuests: 3,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const getStatusBadge = (status) => {
|
||||||
|
const statusStyles = {
|
||||||
|
INVITED: "bg-blue-100 text-blue-800",
|
||||||
|
PENDING: "bg-yellow-100 text-yellow-800",
|
||||||
|
CONFIRMED: "bg-green-100 text-green-800",
|
||||||
|
DECLINED: "bg-red-100 text-red-800",
|
||||||
|
WAITLISTED: "bg-purple-100 text-purple-800",
|
||||||
|
CANCELLED: "bg-gray-100 text-gray-800",
|
||||||
|
};
|
||||||
|
|
||||||
|
return <Badge className={statusStyles[status]}>{status}</Badge>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const filteredGuests = guests.filter(
|
||||||
|
(guest) =>
|
||||||
|
guest.fullName.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||||
|
guest.email.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||||
|
guest.invitationCode.toLowerCase().includes(searchQuery.toLowerCase()),
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="space-y-4 w-full">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<h2 className="text-2xl font-bold">Guest List</h2>
|
||||||
|
<Dialog open={open} onOpenChange={setOpen}>
|
||||||
|
<DialogTrigger asChild>
|
||||||
|
<Button className="bg-blue-600 hover:bg-blue-700">
|
||||||
|
<Plus className="mr-2 h-4 w-4" /> Add Guest
|
||||||
|
</Button>
|
||||||
|
</DialogTrigger>
|
||||||
|
<DialogContent>
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Add New Guest</DialogTitle>
|
||||||
|
</DialogHeader>
|
||||||
|
<div className="grid gap-4 py-4">
|
||||||
|
<div className="grid grid-cols-4 items-center gap-4">
|
||||||
|
<Label htmlFor="name" className="text-right">
|
||||||
|
Full Name
|
||||||
|
</Label>
|
||||||
|
<Input id="name" className="col-span-3" />
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-4 items-center gap-4">
|
||||||
|
<Label htmlFor="email" className="text-right">
|
||||||
|
Email
|
||||||
|
</Label>
|
||||||
|
<Input id="email" type="email" className="col-span-3" />
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-4 items-center gap-4">
|
||||||
|
<Label htmlFor="phone" className="text-right">
|
||||||
|
Phone
|
||||||
|
</Label>
|
||||||
|
<Input id="phone" className="col-span-3" />
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-4 items-center gap-4">
|
||||||
|
<Label htmlFor="additionalGuests" className="text-right">
|
||||||
|
Additional Guests
|
||||||
|
</Label>
|
||||||
|
<Input
|
||||||
|
id="additionalGuests"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
className="col-span-3"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-end gap-2">
|
||||||
|
<Button variant="outline" onClick={() => setOpen(false)}>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button className="bg-blue-600 hover:bg-blue-700">
|
||||||
|
Add Guest
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<div className="relative w-64">
|
||||||
|
<Search className="absolute left-2 top-2.5 h-4 w-4 text-gray-500" />
|
||||||
|
<Input
|
||||||
|
placeholder="Search guests..."
|
||||||
|
className="pl-8"
|
||||||
|
value={searchQuery}
|
||||||
|
onChange={(e) => setSearchQuery(e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<Button variant="outline" size="sm">
|
||||||
|
<Filter className="mr-2 h-4 w-4" /> Filter
|
||||||
|
</Button>
|
||||||
|
<Button variant="outline" size="sm">
|
||||||
|
<Send className="mr-2 h-4 w-4" /> Send Invites
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="rounded-md border">
|
||||||
|
<Table>
|
||||||
|
<TableHeader>
|
||||||
|
<TableRow>
|
||||||
|
<TableHead>Name</TableHead>
|
||||||
|
<TableHead>Email</TableHead>
|
||||||
|
<TableHead>Phone</TableHead>
|
||||||
|
<TableHead>Invitation Code</TableHead>
|
||||||
|
<TableHead>Status</TableHead>
|
||||||
|
<TableHead>Additional Guests</TableHead>
|
||||||
|
<TableHead className="text-right">Actions</TableHead>
|
||||||
|
</TableRow>
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{filteredGuests.map((guest) => (
|
||||||
|
<TableRow key={guest.id}>
|
||||||
|
<TableCell className="font-medium">{guest.fullName}</TableCell>
|
||||||
|
<TableCell>{guest.email}</TableCell>
|
||||||
|
<TableCell>{guest.phone}</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
{guest.invitationCode}
|
||||||
|
<Button variant="ghost" size="icon" className="h-6 w-6">
|
||||||
|
<Copy className="h-3 w-3" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>{getStatusBadge(guest.status)}</TableCell>
|
||||||
|
<TableCell>{guest.additionalGuests}</TableCell>
|
||||||
|
<TableCell className="text-right">
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button variant="ghost" size="icon" className="h-8 w-8">
|
||||||
|
<MoreHorizontal className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent align="end">
|
||||||
|
<DropdownMenuItem>Edit</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem>Resend Invitation</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem className="text-red-600">
|
||||||
|
Delete
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex justify-between text-sm text-gray-500">
|
||||||
|
<div>
|
||||||
|
Showing {filteredGuests.length} of {guests.length} guests
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
Total Confirmed:{" "}
|
||||||
|
{guests.filter((g) => g.status === "CONFIRMED").length} | Total
|
||||||
|
Additional Guests:{" "}
|
||||||
|
{guests.reduce((acc, g) => acc + g.additionalGuests, 0)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default GuestListTable;
|
||||||
Reference in New Issue
Block a user