new src change

This commit is contained in:
priyanshuvish
2025-10-16 21:21:43 +11:00
parent 950bba7127
commit 580cc841cd
9 changed files with 268 additions and 79 deletions

View File

@@ -7,7 +7,7 @@ import { Mail, Lock, Eye, EyeOff, Search, Users, Calendar, TrendingUp, Clock, Us
import { useState } from "react"; import { useState } from "react";
import { motion, AnimatePresence } from "motion/react"; import { motion, AnimatePresence } from "motion/react";
import FlightBookingRafiki from "./imports/FlightBookingRafiki"; import FlightBookingRafiki from "./imports/FlightBookingRafiki";
import cityCardsLogo from './assets/city-card-logo.png'; import cityCardsLogo from './assets/cityLogo.png';
import { ImageWithFallback } from "./components/figma/ImageWithFallback"; import { ImageWithFallback } from "./components/figma/ImageWithFallback";
import Sidebar from "./components/Sidebar"; import Sidebar from "./components/Sidebar";
import Header from "./components/Header"; import Header from "./components/Header";
@@ -19,6 +19,7 @@ import BookingManagementPage from "./components/BookingManagementPage";
import RecurringBlockPage from "./components/RecurringBlockPage"; import RecurringBlockPage from "./components/RecurringBlockPage";
import NotificationsPage from "./components/NotificationsPage"; import NotificationsPage from "./components/NotificationsPage";
import ProfilePage from "./components/ProfilePage"; import ProfilePage from "./components/ProfilePage";
import HowItWorksPage from "./components/HowItWorksPage";
export default function App() { export default function App() {
const [showPassword, setShowPassword] = useState(false); const [showPassword, setShowPassword] = useState(false);
@@ -95,6 +96,7 @@ export default function App() {
{activeNavItem === "support" && <SupportPage />} {activeNavItem === "support" && <SupportPage />}
{activeNavItem === "notifications" && <NotificationsPage />} {activeNavItem === "notifications" && <NotificationsPage />}
{activeNavItem === "profile" && <ProfilePage />} {activeNavItem === "profile" && <ProfilePage />}
{activeNavItem === "how-it-works" && <HowItWorksPage onNavigate={setActiveNavItem} />}
</motion.div> </motion.div>
</div> </div>
</div> </div>
@@ -487,4 +489,4 @@ export default function App() {
</div> </div>
</div> </div>
); );
} }

View File

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 35 KiB

View File

@@ -5,6 +5,7 @@ import CalendarView from "./CalendarView";
import BookingDetailView from "./BookingDetailView"; import BookingDetailView from "./BookingDetailView";
import { Button } from "./ui/button"; import { Button } from "./ui/button";
import { Input } from "./ui/input"; import { Input } from "./ui/input";
import { Checkbox } from "./ui/checkbox";
import { import {
DropdownMenu, DropdownMenu,
DropdownMenuContent, DropdownMenuContent,
@@ -29,6 +30,7 @@ export default function BookingManagementPage({ activeView, onNavigateToRecurrin
const [searchQuery, setSearchQuery] = useState(""); const [searchQuery, setSearchQuery] = useState("");
const [selectedBooking, setSelectedBooking] = useState<any>(null); const [selectedBooking, setSelectedBooking] = useState<any>(null);
const [showDetailView, setShowDetailView] = useState(false); const [showDetailView, setShowDetailView] = useState(false);
const [selectedBookings, setSelectedBookings] = useState<string[]>([]);
// Sample booking data // Sample booking data
const bookings = [ const bookings = [
@@ -152,6 +154,24 @@ export default function BookingManagementPage({ activeView, onNavigateToRecurrin
setSelectedBooking(null); setSelectedBooking(null);
}; };
// Handle checkbox selection
const handleSelectBooking = (bookingId: string) => {
setSelectedBookings(prev =>
prev.includes(bookingId)
? prev.filter(id => id !== bookingId)
: [...prev, bookingId]
);
};
// Handle select all
const handleSelectAll = () => {
if (selectedBookings.length === filteredBookings.length) {
setSelectedBookings([]);
} else {
setSelectedBookings(filteredBookings.map(booking => booking.id));
}
};
// Show detail view if a booking is selected // Show detail view if a booking is selected
if (showDetailView && selectedBooking) { if (showDetailView && selectedBooking) {
return <BookingDetailView bookingData={selectedBooking} onBack={handleBackFromDetail} />; return <BookingDetailView bookingData={selectedBooking} onBack={handleBackFromDetail} />;
@@ -225,6 +245,12 @@ export default function BookingManagementPage({ activeView, onNavigateToRecurrin
<Table> <Table>
<TableHeader> <TableHeader>
<TableRow className="bg-gray-50"> <TableRow className="bg-gray-50">
<TableHead className="text-center font-medium text-gray-900 w-12">
<Checkbox
checked={selectedBookings.length === filteredBookings.length && filteredBookings.length > 0}
onCheckedChange={handleSelectAll}
/>
</TableHead>
<TableHead className="text-center font-medium text-gray-900">Full Name</TableHead> <TableHead className="text-center font-medium text-gray-900">Full Name</TableHead>
<TableHead className="text-center font-medium text-gray-900">Email</TableHead> <TableHead className="text-center font-medium text-gray-900">Email</TableHead>
<TableHead className="text-center font-medium text-gray-900">Card Type</TableHead> <TableHead className="text-center font-medium text-gray-900">Card Type</TableHead>
@@ -237,6 +263,12 @@ export default function BookingManagementPage({ activeView, onNavigateToRecurrin
<TableBody> <TableBody>
{filteredBookings.map((booking, index) => ( {filteredBookings.map((booking, index) => (
<TableRow key={booking.id} className="hover:bg-gray-50"> <TableRow key={booking.id} className="hover:bg-gray-50">
<TableCell className="text-center">
<Checkbox
checked={selectedBookings.includes(booking.id)}
onCheckedChange={() => handleSelectBooking(booking.id)}
/>
</TableCell>
<TableCell className="text-center text-gray-900">{booking.fullName}</TableCell> <TableCell className="text-center text-gray-900">{booking.fullName}</TableCell>
<TableCell className="text-center text-gray-600">{booking.email}</TableCell> <TableCell className="text-center text-gray-600">{booking.email}</TableCell>
<TableCell className="text-center text-gray-900">{booking.cardType}</TableCell> <TableCell className="text-center text-gray-900">{booking.cardType}</TableCell>

View File

@@ -15,24 +15,31 @@ export default function Dashboard() {
const [startDate, setStartDate] = useState<Date>(); const [startDate, setStartDate] = useState<Date>();
const [endDate, setEndDate] = useState<Date>(); const [endDate, setEndDate] = useState<Date>();
const [exportFormat, setExportFormat] = useState<'pdf' | 'excel' | null>(null); const [exportFormat, setExportFormat] = useState<'pdf' | 'excel' | null>(null);
const [dateRangeFilter, setDateRangeFilter] = useState<'today' | 'lastWeek' | 'all' | 'custom'>('custom'); const [dateRangeFilter, setDateRangeFilter] = useState<'last24Hours' | 'last7Days' | 'last30Days' | 'last3Months' | 'custom'>('custom');
const handleDateRangeFilterChange = (filter: 'today' | 'lastWeek' | 'all' | 'custom') => { const handleDateRangeFilterChange = (filter: 'last24Hours' | 'last7Days' | 'last30Days' | 'last3Months' | 'custom') => {
setDateRangeFilter(filter); setDateRangeFilter(filter);
const today = new Date(); const today = new Date();
if (filter === 'today') { if (filter === 'last24Hours') {
setStartDate(today); const yesterday = new Date();
yesterday.setDate(today.getDate() - 1);
setStartDate(yesterday);
setEndDate(today); setEndDate(today);
} else if (filter === 'lastWeek') { } else if (filter === 'last7Days') {
const lastWeek = new Date(); const last7Days = new Date();
lastWeek.setDate(today.getDate() - 7); last7Days.setDate(today.getDate() - 7);
setStartDate(lastWeek); setStartDate(last7Days);
setEndDate(today); setEndDate(today);
} else if (filter === 'all') { } else if (filter === 'last30Days') {
// Set a far past date for "all" const last30Days = new Date();
const allStart = new Date(2020, 0, 1); last30Days.setDate(today.getDate() - 30);
setStartDate(allStart); setStartDate(last30Days);
setEndDate(today);
} else if (filter === 'last3Months') {
const last3Months = new Date();
last3Months.setMonth(today.getMonth() - 3);
setStartDate(last3Months);
setEndDate(today); setEndDate(today);
} else if (filter === 'custom') { } else if (filter === 'custom') {
// Clear dates for custom selection // Clear dates for custom selection
@@ -110,16 +117,17 @@ export default function Dashboard() {
<label className="text-base font-medium text-gray-900">Date Range</label> <label className="text-base font-medium text-gray-900">Date Range</label>
<Select <Select
value={dateRangeFilter} value={dateRangeFilter}
onValueChange={(value) => handleDateRangeFilterChange(value as 'today' | 'lastWeek' | 'all' | 'custom')} onValueChange={(value) => handleDateRangeFilterChange(value as 'last24Hours' | 'last7Days' | 'last30Days' | 'last3Months' | 'custom')}
> >
<SelectTrigger className="w-48 bg-gray-50 border-gray-200 hover:bg-gray-100"> <SelectTrigger className="w-48 bg-gray-50 border-gray-200 hover:bg-gray-100">
<SelectValue placeholder="Select date range" /> <SelectValue placeholder="Select date range" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
<SelectItem value="today">Today</SelectItem> <SelectItem value="last24Hours">Last 24 Hours</SelectItem>
<SelectItem value="lastWeek">Last Week</SelectItem> <SelectItem value="last7Days">Last 7 Days</SelectItem>
<SelectItem value="all">All</SelectItem> <SelectItem value="last30Days">Last 30 Days</SelectItem>
<SelectItem value="custom">Custom</SelectItem> <SelectItem value="last3Months">Last 3 Months</SelectItem>
<SelectItem value="custom">Custom Date Range</SelectItem>
</SelectContent> </SelectContent>
</Select> </Select>
</div> </div>

View File

@@ -0,0 +1,89 @@
import { BookOpen } from "lucide-react";
import { motion } from "motion/react";
import { ImageWithFallback } from "./figma/ImageWithFallback";
interface HowItWorksPageProps {
onNavigate: (page: string) => void;
}
export default function HowItWorksPage({ onNavigate }: HowItWorksPageProps) {
return (
<div className="flex-1 overflow-auto bg-gray-50">
<div className="max-w-7xl mx-auto p-8">
{/* Header */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.3 }}
className="mb-8"
>
<div className="flex items-center gap-3 mb-2">
<div className="h-12 w-12 rounded-xl bg-gradient-to-br from-blue-500 to-purple-500 flex items-center justify-center">
<BookOpen className="h-6 w-6 text-white" />
</div>
<h1 className="text-gray-900">How It Works</h1>
</div>
</motion.div>
{/* Main Content */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4, delay: 0.1 }}
className="bg-white rounded-xl border border-gray-200 shadow-sm overflow-hidden"
>
{/* Image */}
<div className="w-full h-96 overflow-hidden bg-gray-100">
<ImageWithFallback
src="https://images.unsplash.com/photo-1758983308742-f4ba1f8c8cb4?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxoZWxwJTIwZ3VpZGUlMjB0dXRvcmlhbHxlbnwxfHx8fDE3NjA2MDkzNzZ8MA&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral"
alt="How It Works Guide"
className="w-full h-full object-cover"
/>
</div>
{/* Text Content */}
<div className="p-8">
<h2 className="text-gray-900 mb-4">Welcome to CityCards Partner Web</h2>
<div className="space-y-4 text-gray-700">
<p>
CityCards Partner Web is your comprehensive dashboard for managing customer bookings,
processing redemptions, and overseeing your team operations. This platform streamlines
your daily tasks and provides powerful analytics to help grow your business.
</p>
<p>
<strong className="text-gray-900">Managing Bookings:</strong> View and manage customer bookings
through an intuitive calendar or table view. Check-in customers, handle walk-ins, and set up
recurring blocked time periods for holidays or maintenance.
</p>
<p>
<strong className="text-gray-900">Processing Redemptions:</strong> Quickly scan and verify
CityCard passes for your customers. Process both single and multi-day passes, handle redemption
confirmations, and access complete redemption history at your fingertips.
</p>
<p>
<strong className="text-gray-900">Staff Management:</strong> Add and remove team members,
assign roles and permissions, track staff activity, and manage schedules efficiently.
Keep your team organized and ensure smooth operations.
</p>
<p>
<strong className="text-gray-900">Reports & Analytics:</strong> Generate comprehensive reports
in PDF or Excel format. View redemption analytics, track booking trends, and monitor staff
performance to make data-driven decisions for your business.
</p>
<p>
<strong className="text-gray-900">Support:</strong> Need help? Our support team is always
available to assist you with any questions or issues you may encounter while using the platform.
</p>
</div>
</div>
</motion.div>
</div>
</div>
);
}

View File

@@ -3,6 +3,7 @@ import { motion } from "motion/react";
import { Input } from "./ui/input"; import { Input } from "./ui/input";
import { Button } from "./ui/button"; import { Button } from "./ui/button";
import { Badge } from "./ui/badge"; import { Badge } from "./ui/badge";
import { Checkbox } from "./ui/checkbox";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "./ui/table"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "./ui/table";
import { useState } from "react"; import { useState } from "react";
import CustomerDetailView from "./CustomerDetailView"; import CustomerDetailView from "./CustomerDetailView";
@@ -12,6 +13,7 @@ export default function RedemptionsPage() {
const [searchQuery, setSearchQuery] = useState(""); const [searchQuery, setSearchQuery] = useState("");
const [selectedCustomer, setSelectedCustomer] = useState(null); const [selectedCustomer, setSelectedCustomer] = useState(null);
const [showDetailView, setShowDetailView] = useState(false); const [showDetailView, setShowDetailView] = useState(false);
const [selectedRedemptions, setSelectedRedemptions] = useState<string[]>([]);
// Mock data for redemption logs // Mock data for redemption logs
const redemptionData = [ const redemptionData = [
@@ -270,6 +272,24 @@ export default function RedemptionsPage() {
setSelectedCustomer(null); setSelectedCustomer(null);
}; };
// Handle checkbox selection
const handleSelectRedemption = (bookingId: string) => {
setSelectedRedemptions(prev =>
prev.includes(bookingId)
? prev.filter(id => id !== bookingId)
: [...prev, bookingId]
);
};
// Handle select all
const handleSelectAll = () => {
if (selectedRedemptions.length === currentData.length) {
setSelectedRedemptions([]);
} else {
setSelectedRedemptions(currentData.map(item => item.bookingId));
}
};
// If showing detail view, render the CustomerDetailView component // If showing detail view, render the CustomerDetailView component
if (showDetailView && selectedCustomer) { if (showDetailView && selectedCustomer) {
return ( return (
@@ -330,6 +350,12 @@ export default function RedemptionsPage() {
<Table> <Table>
<TableHeader className="sticky top-0 z-10"> <TableHeader className="sticky top-0 z-10">
<TableRow className="border-b border-gray-200 bg-gray-50"> <TableRow className="border-b border-gray-200 bg-gray-50">
<TableHead className="font-medium text-gray-900 py-4 text-center w-[50px]">
<Checkbox
checked={selectedRedemptions.length === currentData.length && currentData.length > 0}
onCheckedChange={handleSelectAll}
/>
</TableHead>
<TableHead className="font-medium text-gray-900 py-4 text-left w-[120px]">Booking ID</TableHead> <TableHead className="font-medium text-gray-900 py-4 text-left w-[120px]">Booking ID</TableHead>
<TableHead className="font-medium text-gray-900 text-left w-[100px]">Pass ID</TableHead> <TableHead className="font-medium text-gray-900 text-left w-[100px]">Pass ID</TableHead>
<TableHead className="font-medium text-gray-900 text-left w-[160px]">Customer Name</TableHead> <TableHead className="font-medium text-gray-900 text-left w-[160px]">Customer Name</TableHead>
@@ -348,6 +374,12 @@ export default function RedemptionsPage() {
transition={{ delay: index * 0.05 }} transition={{ delay: index * 0.05 }}
className="border-b border-gray-100 hover:bg-gray-50" className="border-b border-gray-100 hover:bg-gray-50"
> >
<TableCell className="text-center w-[50px]">
<Checkbox
checked={selectedRedemptions.includes(item.bookingId)}
onCheckedChange={() => handleSelectRedemption(item.bookingId)}
/>
</TableCell>
<TableCell className="font-medium text-gray-900 py-4 text-left w-[120px]">{item.bookingId}</TableCell> <TableCell className="font-medium text-gray-900 py-4 text-left w-[120px]">{item.bookingId}</TableCell>
<TableCell className="text-gray-600 text-left w-[100px]">{item.passId}</TableCell> <TableCell className="text-gray-600 text-left w-[100px]">{item.passId}</TableCell>
<TableCell className="text-gray-900 text-left w-[160px]">{item.customerName}</TableCell> <TableCell className="text-gray-900 text-left w-[160px]">{item.customerName}</TableCell>

View File

@@ -3,7 +3,7 @@ import { motion } from "motion/react";
import { Avatar, AvatarFallback, AvatarImage } from "./ui/avatar"; import { Avatar, AvatarFallback, AvatarImage } from "./ui/avatar";
import { useState } from "react"; import { useState } from "react";
import { ImageWithFallback } from "./figma/ImageWithFallback"; import { ImageWithFallback } from "./figma/ImageWithFallback";
import cityCardsLogo from '../assets/city-card-logo.png'; import cityCardsLogo from '../assets/cityLogo.png';
interface SidebarProps { interface SidebarProps {
activeItem: string; activeItem: string;
@@ -12,7 +12,6 @@ interface SidebarProps {
} }
export default function Sidebar({ activeItem, onItemSelect, isNotificationsOpen = false }: SidebarProps) { export default function Sidebar({ activeItem, onItemSelect, isNotificationsOpen = false }: SidebarProps) {
const [isProfileOpen, setIsProfileOpen] = useState(false);
const [isBookingExpanded, setIsBookingExpanded] = useState(activeItem === 'booking-table' || activeItem === 'booking-calendar'); const [isBookingExpanded, setIsBookingExpanded] = useState(activeItem === 'booking-table' || activeItem === 'booking-calendar');
const navigationItems = [ const navigationItems = [
@@ -172,69 +171,34 @@ export default function Sidebar({ activeItem, onItemSelect, isNotificationsOpen
</div> </div>
</nav> </nav>
{/* Profile Section */} {/* How It Works Section */}
<div className="p-4 border-t border-gray-100"> <div className="p-4 border-t border-gray-100">
<motion.button <motion.button
onClick={() => setIsProfileOpen(!isProfileOpen)} onClick={() => onItemSelect('how-it-works')}
className="w-full flex items-center gap-3 p-3 rounded-lg hover:bg-gray-50 transition-colors" className={`w-full flex items-center gap-3 p-3 rounded-lg transition-colors ${
activeItem === 'how-it-works'
? 'bg-[#F95F62] text-white'
: 'hover:bg-gray-50'
}`}
whileTap={{ scale: 0.98 }} whileTap={{ scale: 0.98 }}
whileHover={{ x: activeItem === 'how-it-works' ? 0 : 4 }}
> >
<div className="h-10 w-10 rounded-lg bg-gradient-to-br from-blue-500 to-purple-500 flex items-center justify-center"> <div className={`h-10 w-10 rounded-lg flex items-center justify-center ${
activeItem === 'how-it-works'
? 'bg-white/20'
: 'bg-gradient-to-br from-blue-500 to-purple-500'
}`}>
<HelpCircle className="h-5 w-5 text-white" /> <HelpCircle className="h-5 w-5 text-white" />
</div> </div>
<div className="flex-1 text-left"> <div className="flex-1 text-left">
<p className="font-medium text-gray-900">How it Works</p> <p className={`font-medium ${
<p className="text-sm text-gray-500">Help & Guides</p> activeItem === 'how-it-works' ? 'text-white' : 'text-gray-900'
}`}>How it Works</p>
<p className={`text-sm ${
activeItem === 'how-it-works' ? 'text-white/80' : 'text-gray-500'
}`}>Help & Guides</p>
</div> </div>
<motion.div
animate={{ rotate: isProfileOpen ? 180 : 0 }}
transition={{ duration: 0.2 }}
>
<ChevronDown className="h-4 w-4 text-gray-400" />
</motion.div>
</motion.button> </motion.button>
{/* How it Works Dropdown */}
<motion.div
initial={{ height: 0, opacity: 0 }}
animate={{
height: isProfileOpen ? 'auto' : 0,
opacity: isProfileOpen ? 1 : 0
}}
transition={{ duration: 0.2 }}
className="overflow-hidden"
>
<div className="pt-2 space-y-1">
<motion.button
whileHover={{ x: 4 }}
className="w-full flex items-center gap-3 px-3 py-2 text-left text-gray-700 hover:bg-gray-50 rounded-lg transition-colors"
>
<Calendar className="h-4 w-4 text-gray-500" />
<span className="text-sm">Managing Bookings</span>
</motion.button>
<motion.button
whileHover={{ x: 4 }}
className="w-full flex items-center gap-3 px-3 py-2 text-left text-gray-700 hover:bg-gray-50 rounded-lg transition-colors"
>
<CreditCard className="h-4 w-4 text-gray-500" />
<span className="text-sm">Redemption Process</span>
</motion.button>
<motion.button
whileHover={{ x: 4 }}
className="w-full flex items-center gap-3 px-3 py-2 text-left text-gray-700 hover:bg-gray-50 rounded-lg transition-colors"
>
<Users className="h-4 w-4 text-gray-500" />
<span className="text-sm">Staff Management</span>
</motion.button>
<motion.button
whileHover={{ x: 4 }}
className="w-full flex items-center gap-3 px-3 py-2 text-left text-gray-700 hover:bg-gray-50 rounded-lg transition-colors"
>
<Table2 className="h-4 w-4 text-gray-500" />
<span className="text-sm">Reports & Analytics</span>
</motion.button>
</div>
</motion.div>
</div> </div>
</div> </div>
); );

View File

@@ -4,6 +4,7 @@ import { Input } from "./ui/input";
import { Button } from "./ui/button"; import { Button } from "./ui/button";
import { Badge } from "./ui/badge"; import { Badge } from "./ui/badge";
import { Switch } from "./ui/switch"; import { Switch } from "./ui/switch";
import { Checkbox } from "./ui/checkbox";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "./ui/table"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "./ui/table";
import { Label } from "./ui/label"; import { Label } from "./ui/label";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./ui/select"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./ui/select";
@@ -15,6 +16,7 @@ export default function StaffManagementPage() {
const [isEditPanelOpen, setIsEditPanelOpen] = useState(false); const [isEditPanelOpen, setIsEditPanelOpen] = useState(false);
const [isAddPanelOpen, setIsAddPanelOpen] = useState(false); const [isAddPanelOpen, setIsAddPanelOpen] = useState(false);
const [editingStaff, setEditingStaff] = useState(null); const [editingStaff, setEditingStaff] = useState(null);
const [selectedStaff, setSelectedStaff] = useState<string[]>([]);
const [editForm, setEditForm] = useState({ const [editForm, setEditForm] = useState({
fullName: "", fullName: "",
phone: "", phone: "",
@@ -199,6 +201,24 @@ export default function StaffManagementPage() {
); );
}; };
// Handle checkbox selection
const handleSelectStaff = (idNo: string) => {
setSelectedStaff(prev =>
prev.includes(idNo)
? prev.filter(id => id !== idNo)
: [...prev, idNo]
);
};
// Handle select all
const handleSelectAll = () => {
if (selectedStaff.length === currentData.length) {
setSelectedStaff([]);
} else {
setSelectedStaff(currentData.map(staff => staff.idNo));
}
};
const handleEditStaff = (idNo: string) => { const handleEditStaff = (idNo: string) => {
const staffMember = staffData.find(staff => staff.idNo === idNo); const staffMember = staffData.find(staff => staff.idNo === idNo);
if (staffMember) { if (staffMember) {
@@ -526,6 +546,12 @@ export default function StaffManagementPage() {
<Table> <Table>
<TableHeader className="sticky top-0 z-10"> <TableHeader className="sticky top-0 z-10">
<TableRow className="border-b border-gray-200 bg-gray-50"> <TableRow className="border-b border-gray-200 bg-gray-50">
<TableHead className="font-medium text-gray-900 py-4 text-center w-[50px]">
<Checkbox
checked={selectedStaff.length === currentData.length && currentData.length > 0}
onCheckedChange={handleSelectAll}
/>
</TableHead>
<TableHead className="font-medium text-gray-900 py-4 text-center w-[100px]">ID No.</TableHead> <TableHead className="font-medium text-gray-900 py-4 text-center w-[100px]">ID No.</TableHead>
<TableHead className="font-medium text-gray-900 text-center w-[160px]">Full Name</TableHead> <TableHead className="font-medium text-gray-900 text-center w-[160px]">Full Name</TableHead>
<TableHead className="font-medium text-gray-900 text-center w-[140px]">Phone</TableHead> <TableHead className="font-medium text-gray-900 text-center w-[140px]">Phone</TableHead>
@@ -544,6 +570,12 @@ export default function StaffManagementPage() {
transition={{ delay: index * 0.05 }} transition={{ delay: index * 0.05 }}
className="border-b border-gray-100 hover:bg-gray-50" className="border-b border-gray-100 hover:bg-gray-50"
> >
<TableCell className="text-center w-[50px]">
<Checkbox
checked={selectedStaff.includes(item.idNo)}
onCheckedChange={() => handleSelectStaff(item.idNo)}
/>
</TableCell>
<TableCell className="font-medium text-gray-900 py-4 text-center w-[100px]">{item.idNo}</TableCell> <TableCell className="font-medium text-gray-900 py-4 text-center w-[100px]">{item.idNo}</TableCell>
<TableCell className="text-gray-900 text-center w-[160px]">{item.fullName}</TableCell> <TableCell className="text-gray-900 text-center w-[160px]">{item.fullName}</TableCell>
<TableCell className="text-gray-600 text-center w-[140px]">{item.phone}</TableCell> <TableCell className="text-gray-600 text-center w-[140px]">{item.phone}</TableCell>

View File

@@ -913,6 +913,10 @@
height: calc(var(--spacing) * 64); height: calc(var(--spacing) * 64);
} }
.h-96 {
height: calc(var(--spacing) * 96);
}
.h-\[1\.15rem\] { .h-\[1\.15rem\] {
height: 1.15rem; height: 1.15rem;
} }
@@ -1033,6 +1037,10 @@
width: calc(var(--spacing) * 96); width: calc(var(--spacing) * 96);
} }
.w-\[50px\] {
width: 50px;
}
.w-\[65px\] { .w-\[65px\] {
width: 65px; width: 65px;
} }
@@ -1129,6 +1137,10 @@
flex-shrink: 0; flex-shrink: 0;
} }
.grow {
flex-grow: 1;
}
.caption-bottom { .caption-bottom {
caption-side: bottom; caption-side: bottom;
} }
@@ -1364,6 +1376,10 @@
overflow: hidden; overflow: hidden;
} }
.overflow-auto {
overflow: auto;
}
.overflow-clip { .overflow-clip {
overflow: clip; overflow: clip;
} }
@@ -1712,6 +1728,16 @@
background-color: var(--color-white); background-color: var(--color-white);
} }
.bg-white\/20 {
background-color: #fff3;
}
@supports (color: color-mix(in lab, red, red)) {
.bg-white\/20 {
background-color: color-mix(in oklab, var(--color-white) 20%, transparent);
}
}
.bg-gradient-to-br { .bg-gradient-to-br {
--tw-gradient-position: to bottom right in oklab; --tw-gradient-position: to bottom right in oklab;
background-image: linear-gradient(var(--tw-gradient-stops)); background-image: linear-gradient(var(--tw-gradient-stops));
@@ -1735,6 +1761,10 @@
object-fit: contain; object-fit: contain;
} }
.object-cover {
object-fit: cover;
}
.p-0 { .p-0 {
padding: calc(var(--spacing) * 0); padding: calc(var(--spacing) * 0);
} }
@@ -1767,6 +1797,10 @@
padding: calc(var(--spacing) * 6); padding: calc(var(--spacing) * 6);
} }
.p-8 {
padding: calc(var(--spacing) * 8);
}
.p-12 { .p-12 {
padding: calc(var(--spacing) * 12); padding: calc(var(--spacing) * 12);
} }
@@ -1827,10 +1861,6 @@
padding-top: calc(var(--spacing) * 1); padding-top: calc(var(--spacing) * 1);
} }
.pt-2 {
padding-top: calc(var(--spacing) * 2);
}
.pt-4 { .pt-4 {
padding-top: calc(var(--spacing) * 4); padding-top: calc(var(--spacing) * 4);
} }