import React, { useState, useRef } from 'react'; import { AlertCircle, CheckCircle, Clock, User } from 'lucide-react'; import Keyboard from "react-simple-keyboard"; import "react-simple-keyboard/build/css/index.css"; const API_BASE = `http://${window.location.hostname}:8000`; function App() { const [step, setStep] = useState('welcome'); const [formData, setFormData] = useState({ firstName: '', lastName: '', dob: '', symptoms: [], severity: 'moderate' }); const [dobDisplay, setDobDisplay] = useState(''); const [assignedBand, setAssignedBand] = useState(null); const [isSubmitting, setIsSubmitting] = useState(false); const [error, setError] = useState(null); const [showKeyboard, setShowKeyboard] = useState(false); const [activeField, setActiveField] = useState(null); const [layoutName, setLayoutName] = useState("default"); const keyboard = useRef(); const symptoms = [ 'Chest Pain', 'Difficulty Breathing', 'Severe Headache', 'Abdominal Pain', 'Fever', 'Nausea/Vomiting', 'Dizziness', 'Injury/Trauma', 'Other' ]; const fieldOrder = ['firstName', 'lastName', 'dob']; const handleSymptomToggle = (symptom) => { setFormData(prev => ({ ...prev, symptoms: prev.symptoms.includes(symptom) ? prev.symptoms.filter(s => s !== symptom) : [...prev.symptoms, symptom] })); }; const formatDateUS = (digits) => { // Only keep digits, max 8 const cleaned = digits.replace(/\D/g, '').slice(0, 8); let formatted = ''; // Add slashes as user types: MM/DD/YYYY if (cleaned.length >= 1) { formatted = cleaned.slice(0, 2); // MM } if (cleaned.length >= 3) { formatted += '/' + cleaned.slice(2, 4); // DD } if (cleaned.length >= 5) { formatted += '/' + cleaned.slice(4, 8); // YYYY } return formatted; }; const convertToISODate = (usDate) => { // Convert MM/DD/YYYY to YYYY-MM-DD for backend const match = usDate.match(/^(\d{2})\/(\d{2})\/(\d{4})$/); if (match) { const [_, month, day, year] = match; return `${year}-${month}-${day}`; } return ''; }; const getDatePreview = (usDate) => { const match = usDate.match(/^(\d{2})\/(\d{2})\/(\d{4})$/); if (match) { const [_, month, day, year] = match; const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; const monthIdx = parseInt(month) - 1; if (monthIdx >= 0 && monthIdx < 12) { const monthName = months[monthIdx]; return `${monthName} ${parseInt(day)}, ${year}`; } } return ''; }; const onKeyboardChange = (input) => { if (activeField === 'dob') { // Keep only the raw digits the user types const digitsOnly = input.replace(/\D/g, ''); const formatted = formatDateUS(digitsOnly); setDobDisplay(formatted); setFormData(prev => ({ ...prev, dob: convertToISODate(formatted) })); } else if (activeField) { setFormData(prev => ({ ...prev, [activeField]: input })); } }; const onKeyPress = (button) => { if (button === "{shift}" || button === "{lock}") { setLayoutName(layoutName === "default" ? "shift" : "default"); } if (button === "{tab}") { const currentIndex = fieldOrder.indexOf(activeField); const nextIndex = (currentIndex + 1) % fieldOrder.length; const nextField = fieldOrder[nextIndex]; setActiveField(nextField); setLayoutName(nextField === 'dob' ? 'numbers' : 'default'); setTimeout(() => { if (keyboard.current) { const value = nextField === 'dob' ? dobDisplay.replace(/\//g, '') : formData[nextField] || ''; keyboard.current.setInput(value); } }, 100); } if (button === "{enter}") { setShowKeyboard(false); setActiveField(null); } }; const handleInputFocus = (fieldName) => { setActiveField(fieldName); setShowKeyboard(true); setLayoutName(fieldName === 'dob' ? 'numbers' : 'default'); setTimeout(() => { if (keyboard.current) { // For date field, show only digits (no slashes) in keyboard input const value = fieldName === 'dob' ? dobDisplay.replace(/\//g, '') : formData[fieldName] || ''; keyboard.current.setInput(value); } }, 100); }; const handleSubmit = async () => { setIsSubmitting(true); setError(null); try { const response = await fetch(`${API_BASE}/api/checkin`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(formData), }); if (!response.ok) { throw new Error(`Server returned ${response.status}: ${response.statusText}`); } const data = await response.json(); setAssignedBand({ patientId: data.patient_id, bandId: data.band_id, station: Math.floor(Math.random() * 8) + 1 }); setStep('complete'); setShowKeyboard(false); } catch (error) { setError(error.message); alert(`Failed to check in: ${error.message}`); } finally { setIsSubmitting(false); } }; if (step === 'welcome') { return (

Welcome to VitalLink

Emergency Room Check-In

What to expect:

Answer a few questions about your condition

Receive a smart wristband to monitor your vitals

Wait comfortably while we track your condition

); } if (step === 'form') { const datePreview = getDatePreview(dobDisplay); return (

Patient Information

{error && (

Error: {error}

)}
handleInputFocus('firstName')} onChange={(e) => setFormData({...formData, firstName: e.target.value})} className="w-full px-6 py-4 border-2 border-gray-300 rounded-lg focus:border-blue-500 focus:outline-none text-xl font-semibold cursor-pointer" placeholder="Tap to type" />
handleInputFocus('lastName')} onChange={(e) => setFormData({...formData, lastName: e.target.value})} className="w-full px-6 py-4 border-2 border-gray-300 rounded-lg focus:border-blue-500 focus:outline-none text-xl font-semibold cursor-pointer" placeholder="Tap to type" />
handleInputFocus('dob')} readOnly className="w-full px-6 py-4 border-2 border-gray-300 rounded-lg focus:border-blue-500 focus:outline-none text-xl font-semibold cursor-pointer" placeholder="MM/DD/YYYY" /> {datePreview && (

{datePreview}

)}
{symptoms.map((symptom) => ( ))}
{['mild', 'moderate', 'severe'].map((level) => ( ))}
{/* Large Touchscreen Keyboard */} {showKeyboard && (

{activeField === 'firstName' ? '👤 First Name' : activeField === 'lastName' ? '👤 Last Name' : '📅 Date of Birth'}

{activeField === 'dob' && (

Type: {dobDisplay || 'MM/DD/YYYY'}

{datePreview && (

= {datePreview}

)}
)}
(keyboard.current = r)} layoutName={activeField === 'dob' ? 'numbers' : layoutName} onChange={onKeyboardChange} onKeyPress={onKeyPress} theme="hg-theme-default hg-layout-default kiosk-keyboard" display={{ '{bksp}': '⌫ Delete', '{enter}': '✓ Done', '{tab}': '→ Next', '{shift}': '⬆', '{space}': '_____ Space _____', }} layout={activeField === 'dob' ? { numbers: [ "1 2 3", "4 5 6", "7 8 9", "{bksp} 0 {tab}", "{enter}" ] } : { default: [ "Q W E R T Y U I O P {bksp}", "A S D F G H J K L", "{shift} Z X C V B N M {shift}", "{tab} {space} {enter}" ], shift: [ "Q W E R T Y U I O P {bksp}", "A S D F G H J K L", "{shift} Z X C V B N M {shift}", "{tab} {space} {enter}" ] }} />
)}
); } if (step === 'complete') { return (

Check-In Complete!

Your wristband has been assigned

Your Patient ID

{assignedBand?.patientId}

Wristband ID

{assignedBand?.bandId}

Next Steps:

1

Pick up your wristband from Station {assignedBand?.station}

2

Wear it on your wrist - make sure it's snug but comfortable

3

Take a seat in the waiting area - your vitals are being monitored

A nurse will call you when it's your turn

); } return null; } export default App;