feat(ui): Refactor Kiosk Landing Page with AuraUI Shell
- Extracted VisitorKioskShell from VisitorCheckIn - Updated VisitorKioskPage to use shell for Home, Returning, and Success modes - Implemented light-mode glassmorphism styles for Kiosk buttons - Ensured consistent user experience across the entire Kiosk flow
This commit is contained in:
parent
2a607b23b5
commit
df9432ac1a
2 changed files with 482 additions and 495 deletions
|
|
@ -1,4 +1,4 @@
|
|||
import { useRef, useState } from "react";
|
||||
import { useRef, useState, ReactNode } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import {
|
||||
CheckCircle,
|
||||
|
|
@ -31,6 +31,93 @@ const commonStyles = {
|
|||
link: "font-medium text-cyan-600 hover:text-cyan-700 transition-colors hover:underline",
|
||||
};
|
||||
|
||||
interface VisitorKioskShellProps {
|
||||
children: ReactNode;
|
||||
onBack?: () => void;
|
||||
title?: ReactNode;
|
||||
subtitle?: string;
|
||||
}
|
||||
|
||||
export const VisitorKioskShell = ({ children, onBack, title, subtitle }: VisitorKioskShellProps) => {
|
||||
return (
|
||||
<section className="relative bg-white min-h-screen">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 min-h-screen">
|
||||
{/* Left Hero Section - Fixed/Sticky on Desktop */}
|
||||
<div className="relative flex items-end px-6 pb-16 pt-20 lg:pt-60 lg:pb-24 bg-slate-900 lg:sticky lg:top-0 lg:h-screen overflow-hidden">
|
||||
<img
|
||||
src="https://images.unsplash.com/photo-1565553642973-6afe791aee33?q=80&w=2623&auto=format&fit=crop"
|
||||
alt="Modern Facility Hallway"
|
||||
className="absolute inset-0 w-full h-full object-cover opacity-40 mix-blend-overlay"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-slate-950 via-slate-900/80 to-transparent" />
|
||||
|
||||
<div className="relative z-10 max-w-lg">
|
||||
{onBack && (
|
||||
<button
|
||||
onClick={onBack}
|
||||
className="absolute -top-40 left-0 lg:hidden text-white/50 hover:text-white flex items-center gap-2 mb-4"
|
||||
>
|
||||
<ArrowLeft size={18} /> Back
|
||||
</button>
|
||||
)}
|
||||
|
||||
<div className="flex items-center gap-2 mb-6">
|
||||
<span className="badge badge-accent bg-cyan-500/10 text-cyan-400 border-cyan-500/20 shadow-[0_0_15px_rgba(6,182,212,0.3)]">SECURE FACILITY</span>
|
||||
</div>
|
||||
<h3 className="text-4xl sm:text-5xl font-bold text-white drop-shadow-2xl leading-tight">
|
||||
Welcome to <br />
|
||||
<span className="text-transparent bg-clip-text bg-gradient-to-r from-cyan-400 to-blue-500 animate-pulse-slow">
|
||||
777 Wolfpack
|
||||
</span>
|
||||
</h3>
|
||||
<p className="mt-4 text-lg text-slate-300 leading-relaxed">
|
||||
Please complete the check-in process to receive your digital visitor badge and safety clearance.
|
||||
</p>
|
||||
<ul className="mt-10 grid grid-cols-1 sm:grid-cols-2 gap-x-8 gap-y-5">
|
||||
{[
|
||||
"Digital NDA Signature",
|
||||
"Safety Briefing",
|
||||
"Instant Badge Issuance",
|
||||
"Escort Assignment",
|
||||
].map((item, i) => (
|
||||
<li
|
||||
key={i}
|
||||
className="flex items-center gap-3 text-slate-200 text-lg font-medium"
|
||||
>
|
||||
<CheckCircle className="text-cyan-500 w-5 h-5 flex-shrink-0" />
|
||||
{item}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Content Section - Scrollable */}
|
||||
<div className="flex items-center justify-center px-6 py-16 lg:py-24 bg-gradient-to-br from-white via-slate-50 to-white relative">
|
||||
{onBack && (
|
||||
<button
|
||||
onClick={onBack}
|
||||
className="absolute top-8 left-8 hidden lg:flex items-center gap-2 text-slate-400 hover:text-slate-600 transition-colors"
|
||||
>
|
||||
<ArrowLeft size={20} /> Back
|
||||
</button>
|
||||
)}
|
||||
|
||||
<div className="w-full max-w-md bg-white/80 backdrop-blur-xl shadow-2xl rounded-3xl p-8 lg:p-10 border border-white/50 ring-1 ring-slate-100">
|
||||
{(title || subtitle) && (
|
||||
<div className="mb-8">
|
||||
{title && <h2 className="text-3xl font-bold text-slate-900 sm:text-4xl tracking-tight">{title}</h2>}
|
||||
{subtitle && <p className="mt-2 text-sm text-slate-600">{subtitle}</p>}
|
||||
</div>
|
||||
)}
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
interface VisitorCheckInProps {
|
||||
onBack: () => void;
|
||||
onSuccess: (data: { badgeNumber: string; visitId: string; message: string }) => void;
|
||||
|
|
@ -153,84 +240,24 @@ const VisitorCheckIn = ({ onBack, onSuccess }: VisitorCheckInProps) => {
|
|||
};
|
||||
|
||||
return (
|
||||
<section className="relative bg-white min-h-screen">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 min-h-screen">
|
||||
{/* Left Hero Section - Fixed/Sticky on Desktop */}
|
||||
<div className="relative flex items-end px-6 pb-16 pt-20 lg:pt-60 lg:pb-24 bg-slate-900 lg:sticky lg:top-0 lg:h-screen overflow-hidden">
|
||||
<img
|
||||
src="https://images.unsplash.com/photo-1565553642973-6afe791aee33?q=80&w=2623&auto=format&fit=crop"
|
||||
alt="Modern Facility Hallway"
|
||||
className="absolute inset-0 w-full h-full object-cover opacity-40 mix-blend-overlay"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-slate-950 via-slate-900/80 to-transparent" />
|
||||
|
||||
{/* Animated Particles/Orbs could go here for more 'Aura' */}
|
||||
|
||||
<div className="relative z-10 max-w-lg">
|
||||
<button
|
||||
onClick={onBack}
|
||||
className="absolute -top-40 left-0 lg:hidden text-white/50 hover:text-white flex items-center gap-2 mb-4"
|
||||
>
|
||||
<ArrowLeft size={18} /> Back
|
||||
</button>
|
||||
|
||||
<div className="flex items-center gap-2 mb-6">
|
||||
<span className="badge badge-accent bg-cyan-500/10 text-cyan-400 border-cyan-500/20 shadow-[0_0_15px_rgba(6,182,212,0.3)]">SECURE FACILITY</span>
|
||||
</div>
|
||||
<h3 className="text-4xl sm:text-5xl font-bold text-white drop-shadow-2xl leading-tight">
|
||||
Welcome to <br />
|
||||
<span className="text-transparent bg-clip-text bg-gradient-to-r from-cyan-400 to-blue-500 animate-pulse-slow">
|
||||
777 Wolfpack
|
||||
</span>
|
||||
</h3>
|
||||
<p className="mt-4 text-lg text-slate-300 leading-relaxed">
|
||||
Please complete the check-in process to receive your digital visitor badge and safety clearance.
|
||||
</p>
|
||||
<ul className="mt-10 grid grid-cols-1 sm:grid-cols-2 gap-x-8 gap-y-5">
|
||||
{[
|
||||
"Digital NDA Signature",
|
||||
"Safety Briefing",
|
||||
"Instant Badge Issuance",
|
||||
"Escort Assignment",
|
||||
].map((item, i) => (
|
||||
<li
|
||||
key={i}
|
||||
className="flex items-center gap-3 text-slate-200 text-lg font-medium"
|
||||
>
|
||||
<CheckCircle className="text-cyan-500 w-5 h-5 flex-shrink-0" />
|
||||
{item}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Form Section - Scrollable */}
|
||||
<div className="flex items-center justify-center px-6 py-16 lg:py-24 bg-gradient-to-br from-white via-slate-50 to-white relative">
|
||||
<button
|
||||
onClick={onBack}
|
||||
className="absolute top-8 left-8 hidden lg:flex items-center gap-2 text-slate-400 hover:text-slate-600 transition-colors"
|
||||
>
|
||||
<ArrowLeft size={20} /> Back
|
||||
</button>
|
||||
|
||||
<div className="w-full max-w-md bg-white/80 backdrop-blur-xl shadow-2xl rounded-3xl p-8 lg:p-10 border border-white/50 ring-1 ring-slate-100">
|
||||
<h2 className="text-3xl font-bold text-slate-900 sm:text-4xl tracking-tight">
|
||||
<VisitorKioskShell
|
||||
onBack={onBack}
|
||||
title={
|
||||
<>
|
||||
Visitor <span className="text-cyan-600">Check-In</span>
|
||||
</h2>
|
||||
<p className="mt-2 text-sm text-slate-600">
|
||||
Create your visitor profile for today.
|
||||
</p>
|
||||
|
||||
</>
|
||||
}
|
||||
subtitle="Create your visitor profile for today."
|
||||
>
|
||||
{error && (
|
||||
<div className="mt-6 bg-red-50 text-red-600 px-4 py-3 rounded-xl text-sm flex items-start gap-3 border border-red-100">
|
||||
<div className="mb-6 bg-red-50 text-red-600 px-4 py-3 rounded-xl text-sm flex items-start gap-3 border border-red-100">
|
||||
<AlertTriangle className="w-5 h-5 flex-shrink-0 mt-0.5" />
|
||||
<span>{error}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Form */}
|
||||
<form className="mt-8 space-y-5" onSubmit={handleSubmit}>
|
||||
<form className="space-y-5" onSubmit={handleSubmit}>
|
||||
{/* Name */}
|
||||
<div className={commonStyles.inputWrapper}>
|
||||
<User className={commonStyles.inputIcon} />
|
||||
|
|
@ -440,10 +467,7 @@ const VisitorCheckIn = ({ onBack, onSuccess }: VisitorCheckInProps) => {
|
|||
)}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</VisitorKioskShell>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
import { useState, useEffect } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { QRCodeSVG as QRCode } from 'qrcode.react';
|
||||
import { User, Clock, CheckCircle, XCircle, UserPlus, LogOut, Search, Shield, AlertTriangle, Download, LayoutDashboard } from 'lucide-react';
|
||||
import { User, CheckCircle, XCircle, UserPlus, LogOut, Search, AlertTriangle } from 'lucide-react';
|
||||
import { visitorsApi, Visitor, ActiveVisitor } from '../lib/visitorsApi';
|
||||
import VisitorCheckIn from '../components/aura/VisitorCheckIn';
|
||||
import VisitorCheckIn, { VisitorKioskShell } from '../components/aura/VisitorCheckIn';
|
||||
|
||||
type KioskMode = 'home' | 'new-visitor' | 'returning' | 'check-in' | 'check-out' | 'success';
|
||||
|
||||
|
|
@ -11,7 +10,6 @@ export default function VisitorKioskPage() {
|
|||
const [mode, setMode] = useState<KioskMode>('home');
|
||||
const [activeVisitors, setActiveVisitors] = useState<ActiveVisitor[]>([]);
|
||||
const [searchResults, setSearchResults] = useState<Visitor[]>([]);
|
||||
const [selectedVisitor, setSelectedVisitor] = useState<Visitor | null>(null);
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
|
@ -84,7 +82,6 @@ export default function VisitorKioskPage() {
|
|||
|
||||
const resetToHome = () => {
|
||||
setMode('home');
|
||||
setSelectedVisitor(null);
|
||||
setSearchQuery('');
|
||||
setSearchResults([]);
|
||||
setError(null);
|
||||
|
|
@ -112,44 +109,30 @@ export default function VisitorKioskPage() {
|
|||
);
|
||||
}
|
||||
|
||||
// Dynamic Title and Subtitle based on mode
|
||||
let shellTitle = "Welcome";
|
||||
let shellSubtitle = "Please select an option to continue";
|
||||
|
||||
if (mode === 'returning') {
|
||||
shellTitle = "Returning Visitor";
|
||||
shellSubtitle = "Quick check-in for registered visitors";
|
||||
} else if (mode === 'success') {
|
||||
shellTitle = "Success";
|
||||
shellSubtitle = "Action completed successfully";
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900 flex flex-col">
|
||||
{/* Header */}
|
||||
<header className="bg-slate-800/50 border-b border-white/10 p-4">
|
||||
<div className="flex items-center justify-between max-w-4xl mx-auto">
|
||||
<div className="flex items-center gap-3">
|
||||
<Shield className="text-emerald-400" size={28} />
|
||||
<div>
|
||||
<h1 className="text-xl font-bold text-white">Visitor Check-In</h1>
|
||||
<p className="text-xs text-slate-400">777 Wolfpack Facility</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex items-center gap-2 text-slate-400 text-sm">
|
||||
<Clock size={16} />
|
||||
<span>{new Date().toLocaleTimeString()}</span>
|
||||
</div>
|
||||
<Link
|
||||
to="/"
|
||||
className="flex items-center gap-2 text-slate-400 hover:text-emerald-400 text-sm transition-colors px-3 py-1.5 rounded-lg hover:bg-white/5"
|
||||
<VisitorKioskShell
|
||||
onBack={mode !== 'home' ? resetToHome : undefined}
|
||||
title={mode !== 'success' ? shellTitle : undefined}
|
||||
subtitle={mode !== 'success' ? shellSubtitle : undefined}
|
||||
>
|
||||
<LayoutDashboard size={16} />
|
||||
<span className="hidden sm:inline">Dashboard</span>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Main Content */}
|
||||
<main className="flex-1 flex items-center justify-center p-8">
|
||||
<div className="w-full max-w-2xl">
|
||||
|
||||
{/* Error Alert */}
|
||||
{error && (
|
||||
<div className="mb-6 bg-red-500/10 border border-red-500/20 rounded-xl p-4 flex items-center gap-3">
|
||||
<AlertTriangle className="text-red-400" size={20} />
|
||||
<span className="text-red-400">{error}</span>
|
||||
<button onClick={() => setError(null)} className="ml-auto text-red-400 hover:text-red-300">
|
||||
<div className="mb-6 bg-red-50 text-red-600 border border-red-100 rounded-xl p-4 flex items-center gap-3">
|
||||
<AlertTriangle className="text-red-500" size={20} />
|
||||
<span>{error}</span>
|
||||
<button onClick={() => setError(null)} className="ml-auto text-red-400 hover:text-red-500">
|
||||
<XCircle size={18} />
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -157,52 +140,55 @@ export default function VisitorKioskPage() {
|
|||
|
||||
{/* Home Screen */}
|
||||
{mode === 'home' && (
|
||||
<div className="text-center">
|
||||
<h2 className="text-3xl font-bold text-white mb-2">Welcome</h2>
|
||||
<p className="text-slate-400 mb-8">Please select an option to continue</p>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div className="space-y-4">
|
||||
<button
|
||||
onClick={() => setMode('new-visitor')}
|
||||
className="bg-emerald-500/10 hover:bg-emerald-500/20 border border-emerald-500/30 rounded-2xl p-8 text-left transition-all group"
|
||||
className="w-full bg-white hover:bg-slate-50 border border-slate-200 hover:border-cyan-400 rounded-2xl p-6 text-left transition-all group shadow-sm hover:shadow-md flex items-center gap-5"
|
||||
>
|
||||
<UserPlus className="text-emerald-400 mb-4" size={48} />
|
||||
<h3 className="text-xl font-bold text-white mb-2">First Time Visitor</h3>
|
||||
<p className="text-sm text-slate-400">Register and check in for your first visit</p>
|
||||
<div className="w-14 h-14 rounded-full bg-cyan-50 text-cyan-500 flex items-center justify-center group-hover:scale-110 transition-transform">
|
||||
<UserPlus size={28} />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-lg font-bold text-slate-800 group-hover:text-cyan-600 transition-colors">First Time Visitor</h3>
|
||||
<p className="text-sm text-slate-500">Register and check in for your first visit</p>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={() => setMode('returning')}
|
||||
className="bg-blue-500/10 hover:bg-blue-500/20 border border-blue-500/30 rounded-2xl p-8 text-left transition-all group"
|
||||
className="w-full bg-white hover:bg-slate-50 border border-slate-200 hover:border-blue-400 rounded-2xl p-6 text-left transition-all group shadow-sm hover:shadow-md flex items-center gap-5"
|
||||
>
|
||||
<User className="text-blue-400 mb-4" size={48} />
|
||||
<h3 className="text-xl font-bold text-white mb-2">Returning Visitor</h3>
|
||||
<p className="text-sm text-slate-400">Quick check-in for registered visitors</p>
|
||||
</button>
|
||||
<div className="w-14 h-14 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center group-hover:scale-110 transition-transform">
|
||||
<User size={28} />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-lg font-bold text-slate-800 group-hover:text-blue-600 transition-colors">Returning Visitor</h3>
|
||||
<p className="text-sm text-slate-500">Quick check-in for registered visitors</p>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
{/* Check Out Section */}
|
||||
{activeVisitors.length > 0 && (
|
||||
<div className="mt-8 pt-8 border-t border-white/10">
|
||||
<h3 className="text-lg font-bold text-white mb-4 flex items-center justify-center gap-2">
|
||||
<LogOut size={20} />
|
||||
Check Out ({activeVisitors.length} visitors on-site)
|
||||
<div className="mt-8 pt-8 border-t border-slate-100">
|
||||
<h3 className="text-sm font-semibold text-slate-400 uppercase tracking-wider mb-4 flex items-center justify-center gap-2">
|
||||
<LogOut size={16} />
|
||||
Check Out ({activeVisitors.length} on-site)
|
||||
</h3>
|
||||
<div className="grid gap-3 max-h-48 overflow-y-auto">
|
||||
<div className="grid gap-2 max-h-48 overflow-y-auto pr-1">
|
||||
{activeVisitors.map(visitor => (
|
||||
<button
|
||||
key={visitor.logId}
|
||||
onClick={() => handleCheckOut(visitor)}
|
||||
className="bg-slate-800 hover:bg-slate-700 rounded-xl p-4 flex items-center justify-between transition-colors"
|
||||
className="w-full bg-slate-50 hover:bg-red-50 hover:border-red-200 border border-transparent rounded-xl p-3 flex items-center justify-between transition-colors group"
|
||||
>
|
||||
<div className="text-left">
|
||||
<span className="text-white font-medium">{visitor.name}</span>
|
||||
<span className="text-slate-700 font-medium group-hover:text-red-700">{visitor.name}</span>
|
||||
{visitor.company && (
|
||||
<span className="text-slate-400 ml-2 text-sm">({visitor.company})</span>
|
||||
<span className="text-slate-400 ml-2 text-xs">({visitor.company})</span>
|
||||
)}
|
||||
</div>
|
||||
<span className="text-xs bg-emerald-500/20 text-emerald-400 px-2 py-1 rounded">
|
||||
Badge: {visitor.badgeNumber}
|
||||
<span className="text-xs bg-white text-slate-500 px-2 py-1 rounded border border-slate-100 group-hover:border-red-100">
|
||||
#{visitor.badgeNumber}
|
||||
</span>
|
||||
</button>
|
||||
))}
|
||||
|
|
@ -215,13 +201,7 @@ export default function VisitorKioskPage() {
|
|||
{/* Returning Visitor Search */}
|
||||
{mode === 'returning' && (
|
||||
<div>
|
||||
<button onClick={resetToHome} className="text-slate-400 hover:text-white mb-6 flex items-center gap-2">
|
||||
← Back
|
||||
</button>
|
||||
|
||||
<h2 className="text-2xl font-bold text-white mb-6">Find Your Record</h2>
|
||||
|
||||
<div className="flex gap-3 mb-6">
|
||||
<div className="flex gap-2 mb-6">
|
||||
<div className="flex-1 relative">
|
||||
<Search className="absolute left-4 top-1/2 -translate-y-1/2 text-slate-400" size={20} />
|
||||
<input
|
||||
|
|
@ -229,14 +209,15 @@ export default function VisitorKioskPage() {
|
|||
value={searchQuery}
|
||||
onChange={e => setSearchQuery(e.target.value)}
|
||||
onKeyDown={e => e.key === 'Enter' && handleSearch()}
|
||||
className="w-full bg-slate-800 border border-slate-700 rounded-xl pl-12 pr-4 py-3 text-white text-lg focus:border-emerald-500 outline-none"
|
||||
placeholder="Search by name, email, or company..."
|
||||
className="w-full bg-slate-50 border border-slate-200 rounded-xl pl-12 pr-4 py-3 text-slate-900 text-lg focus:border-cyan-500 outline-none focus:ring-2 focus:ring-cyan-500/20 transition-all"
|
||||
placeholder="Search by name..."
|
||||
autoFocus
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
onClick={handleSearch}
|
||||
disabled={loading}
|
||||
className="bg-blue-500 hover:bg-blue-600 px-6 py-3 rounded-xl text-white font-medium"
|
||||
className="bg-blue-600 hover:bg-blue-700 px-6 py-3 rounded-xl text-white font-medium shadow-md shadow-blue-500/20 transition-all"
|
||||
>
|
||||
Search
|
||||
</button>
|
||||
|
|
@ -248,24 +229,24 @@ export default function VisitorKioskPage() {
|
|||
<button
|
||||
key={visitor.id}
|
||||
onClick={() => handleReturningVisitorCheckIn(visitor)}
|
||||
className="w-full bg-slate-800 hover:bg-slate-700 rounded-xl p-4 flex items-center justify-between transition-colors text-left"
|
||||
className="w-full bg-white hover:bg-cyan-50 border border-slate-100 hover:border-cyan-200 rounded-xl p-4 flex items-center justify-between transition-all group shadow-sm"
|
||||
>
|
||||
<div>
|
||||
<div className="text-white font-medium">{visitor.name}</div>
|
||||
<div className="text-sm text-slate-400">
|
||||
<div className="text-slate-800 font-bold group-hover:text-cyan-700">{visitor.name}</div>
|
||||
<div className="text-sm text-slate-500 group-hover:text-cyan-600/80">
|
||||
{visitor.company && `${visitor.company} • `}
|
||||
{visitor.type}
|
||||
</div>
|
||||
</div>
|
||||
<CheckCircle className="text-emerald-400" size={24} />
|
||||
<CheckCircle className="text-gray-200 group-hover:text-cyan-500 transition-colors" size={24} />
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{searchQuery && searchResults.length === 0 && !loading && (
|
||||
<div className="text-center py-8 text-slate-400">
|
||||
No visitors found. Try a different search or register as a new visitor.
|
||||
<div className="text-center py-12 text-slate-400 bg-slate-50 rounded-xl border border-dashed border-slate-200">
|
||||
No visitors found. <br />Try a different search or register as new.
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -273,36 +254,36 @@ export default function VisitorKioskPage() {
|
|||
|
||||
{/* Success Screen */}
|
||||
{mode === 'success' && successData && (
|
||||
<div className="text-center py-12">
|
||||
<div className="w-24 h-24 mx-auto mb-6 bg-emerald-500/20 rounded-full flex items-center justify-center">
|
||||
<CheckCircle className="text-emerald-400" size={48} />
|
||||
<div className="text-center py-6">
|
||||
<div className="w-20 h-20 mx-auto mb-6 bg-emerald-100 text-emerald-600 rounded-full flex items-center justify-center animate-bounce-slow">
|
||||
<CheckCircle size={40} />
|
||||
</div>
|
||||
<h2 className="text-3xl font-bold text-white mb-2">{successData.message}</h2>
|
||||
<h2 className="text-2xl font-bold text-slate-900 mb-2">{successData.message}</h2>
|
||||
|
||||
{successData.badgeNumber && ( // Only show QR for Check-IN, not Check-OUT
|
||||
<div className="mt-8 space-y-6">
|
||||
<div className="inline-block bg-white p-8 rounded-3xl shadow-2xl">
|
||||
<div className="inline-block bg-white p-4 rounded-3xl shadow-lg border border-slate-100">
|
||||
<QRCode
|
||||
size={320}
|
||||
style={{ height: 320, width: 320 }}
|
||||
size={240}
|
||||
style={{ height: 240, width: 240 }}
|
||||
value={`${window.location.origin}/badges/${successData.visitId || 'demo-token'}`}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p className="text-white font-bold text-lg mb-1">Scan for Digital Badge</p>
|
||||
<p className="text-slate-400 text-sm">Use your phone to carry your badge with you.</p>
|
||||
<p className="text-slate-800 font-bold text-lg mb-1">Scan for Digital Badge</p>
|
||||
<p className="text-slate-500 text-sm">Use your phone to carry your badge with you.</p>
|
||||
</div>
|
||||
|
||||
<div className="bg-slate-800 rounded-2xl p-4 inline-block">
|
||||
<p className="text-slate-400 text-xs mb-1">Badge Number</p>
|
||||
<p className="text-2xl font-bold font-mono text-emerald-400">{successData.badgeNumber}</p>
|
||||
<div className="bg-slate-50 border border-slate-200 rounded-2xl p-4 inline-block">
|
||||
<p className="text-slate-400 text-xs mb-1 uppercase tracking-wider font-semibold">Badge Number</p>
|
||||
<p className="text-3xl font-mono font-bold text-slate-800">{successData.badgeNumber}</p>
|
||||
</div>
|
||||
|
||||
<div className="pt-8">
|
||||
<div className="pt-4">
|
||||
<button
|
||||
onClick={resetToHome}
|
||||
className="bg-emerald-600 hover:bg-emerald-700 text-white font-bold text-lg px-8 py-4 rounded-xl transition-colors shadow-lg hover:shadow-emerald-500/20"
|
||||
className="w-full bg-emerald-600 hover:bg-emerald-700 text-white font-bold text-lg px-8 py-4 rounded-xl transition-colors shadow-lg hover:shadow-emerald-500/20"
|
||||
>
|
||||
I have my badge
|
||||
</button>
|
||||
|
|
@ -315,24 +296,6 @@ export default function VisitorKioskPage() {
|
|||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
</div>
|
||||
</main>
|
||||
|
||||
{/* Footer */}
|
||||
<footer className="bg-slate-800/50 border-t border-white/10 p-4 text-center text-slate-500 text-sm flex flex-col md:flex-row justify-center items-center gap-4">
|
||||
<p>By signing in, you agree to follow all facility rules and regulations.</p>
|
||||
<div className="hidden md:block w-px h-4 bg-white/10" />
|
||||
<a
|
||||
href="/visitorkiosk.apk"
|
||||
className="text-emerald-500/80 hover:text-emerald-400 font-medium transition-colors flex items-center gap-2 px-3 py-1 rounded-lg hover:bg-emerald-500/10"
|
||||
download
|
||||
title="Download the Android App"
|
||||
>
|
||||
<Download size={14} />
|
||||
Download Kiosk App
|
||||
</a>
|
||||
</footer>
|
||||
</div>
|
||||
</VisitorKioskShell>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue