better keyboard better gui simplekeyboard

This commit is contained in:
2025-11-20 11:18:33 -05:00
parent ad267720ed
commit 08d48abffc
11 changed files with 240 additions and 428 deletions
+13 -3
View File
@@ -10,7 +10,8 @@
"dependencies": {
"lucide-react": "^0.546.0",
"react": "^19.1.1",
"react-dom": "^19.1.1"
"react-dom": "^19.1.1",
"react-simple-keyboard": "^3.8.137"
},
"devDependencies": {
"@eslint/js": "^9.36.0",
@@ -2498,7 +2499,6 @@
"integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==",
"dev": true,
"license": "MIT",
"peer": true,
"bin": {
"jiti": "lib/jiti-cli.mjs"
}
@@ -2600,7 +2600,6 @@
"integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==",
"dev": true,
"license": "MPL-2.0",
"peer": true,
"dependencies": {
"detect-libc": "^2.0.3"
},
@@ -3130,6 +3129,7 @@
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz",
"integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"scheduler": "^0.27.0"
},
@@ -3147,6 +3147,16 @@
"node": ">=0.10.0"
}
},
"node_modules/react-simple-keyboard": {
"version": "3.8.137",
"resolved": "https://registry.npmjs.org/react-simple-keyboard/-/react-simple-keyboard-3.8.137.tgz",
"integrity": "sha512-3U/qY3/7fAgBQu+M7zsLgBOCl7sad0My1fEiMmRHgDGxqy3CVmRV/q67sRjXQWX51NxxkzySJpWqmEeXvSpXzg==",
"license": "MIT",
"peerDependencies": {
"react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
"react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/resolve-from": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+2 -1
View File
@@ -12,7 +12,8 @@
"dependencies": {
"lucide-react": "^0.546.0",
"react": "^19.1.1",
"react-dom": "^19.1.1"
"react-dom": "^19.1.1",
"react-simple-keyboard": "^3.8.137"
},
"devDependencies": {
"@eslint/js": "^9.36.0",
+84 -91
View File
@@ -1,5 +1,7 @@
import React, { useState } from 'react';
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://localhost:8000';
@@ -17,6 +19,8 @@ function App() {
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',
@@ -33,38 +37,31 @@ function App() {
}));
};
const handleKeyPress = (key) => {
if (!activeField) return;
const onKeyboardChange = (input) => {
if (activeField) {
setFormData(prev => ({
...prev,
[activeField]: input
}));
}
};
if (key === 'BACKSPACE') {
setFormData(prev => ({
...prev,
[activeField]: prev[activeField].slice(0, -1)
}));
} else if (key === 'SPACE') {
setFormData(prev => ({
...prev,
[activeField]: prev[activeField] + ' '
}));
} else if (key === 'CLEAR') {
setFormData(prev => ({
...prev,
[activeField]: ''
}));
} else if (key === 'DONE') {
const onKeyPress = (button) => {
if (button === "{shift}" || button === "{lock}") {
setLayoutName(layoutName === "default" ? "shift" : "default");
}
if (button === "{close}") {
setShowKeyboard(false);
setActiveField(null);
} else {
setFormData(prev => ({
...prev,
[activeField]: prev[activeField] + key
}));
}
};
const handleInputFocus = (fieldName) => {
setActiveField(fieldName);
setShowKeyboard(true);
if (keyboard.current) {
keyboard.current.setInput(formData[fieldName]);
}
};
const handleSubmit = async () => {
@@ -72,8 +69,6 @@ function App() {
setError(null);
try {
console.log('Submitting check-in data:', formData);
const response = await fetch(`${API_BASE}/api/checkin`, {
method: 'POST',
headers: {
@@ -87,7 +82,6 @@ function App() {
}
const data = await response.json();
console.log('Check-in successful:', data);
setAssignedBand({
patientId: data.patient_id,
@@ -98,9 +92,8 @@ function App() {
setStep('complete');
setShowKeyboard(false);
} catch (error) {
console.error('Check-in failed:', error);
setError(error.message);
alert(`Failed to check in: ${error.message}\n\nMake sure the backend is running at ${API_BASE}`);
alert(`Failed to check in: ${error.message}`);
} finally {
setIsSubmitting(false);
}
@@ -147,7 +140,7 @@ function App() {
if (step === 'form') {
return (
<div className={`min-h-screen bg-gradient-to-br from-blue-50 to-blue-100 p-4 ${showKeyboard ? 'pb-96' : ''}`}>
<div className={`min-h-screen bg-gradient-to-br from-blue-50 to-blue-100 p-4 transition-all ${showKeyboard ? 'pb-[450px]' : ''}`}>
<div className="max-w-3xl mx-auto pt-8">
<div className="bg-white rounded-2xl shadow-2xl p-8">
<h2 className="text-3xl font-bold text-gray-800 mb-6">Patient Information</h2>
@@ -171,8 +164,8 @@ function App() {
type="text"
value={formData.firstName}
onFocus={() => handleInputFocus('firstName')}
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 bg-white"
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"
/>
</div>
@@ -184,8 +177,8 @@ function App() {
type="text"
value={formData.lastName}
onFocus={() => handleInputFocus('lastName')}
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 bg-white"
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"
/>
</div>
@@ -272,71 +265,71 @@ function App() {
</div>
</div>
{/* On-Screen Keyboard - ONLY ONE DECLARATION */}
{/* react-simple-keyboard */}
{showKeyboard && (
<div className="fixed bottom-0 left-0 right-0 bg-gradient-to-t from-gray-900 to-gray-800 border-t-4 border-blue-500 shadow-2xl p-6 z-50">
<div className="max-w-5xl mx-auto">
<div className="fixed bottom-0 left-0 right-0 bg-white border-t-4 border-blue-500 shadow-2xl p-6 z-50">
<div className="max-w-6xl mx-auto">
<div className="flex justify-between items-center mb-4">
<div className="text-white">
<p className="text-sm opacity-75">Currently typing:</p>
<p className="font-bold text-xl">
<div>
<p className="text-sm text-gray-600">Currently typing:</p>
<p className="font-bold text-2xl text-gray-800">
{activeField === 'firstName' ? 'First Name' : 'Last Name'}
</p>
</div>
<div className="bg-gray-700 px-6 py-3 rounded-lg">
<p className="text-white font-mono text-2xl min-w-[200px]">
{formData[activeField] || '_'}
</p>
</div>
<button
onClick={() => {
setShowKeyboard(false);
setActiveField(null);
}}
className="bg-red-500 text-white px-6 py-3 rounded-lg text-xl font-bold hover:bg-red-600 active:scale-95"
>
Close
</button>
</div>
<div className="space-y-3">
{[
['Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P'],
['A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L'],
['Z', 'X', 'C', 'V', 'B', 'N', 'M', 'BACKSPACE'],
['SPACE', 'CLEAR', 'DONE']
].map((row, rowIndex) => (
<div key={rowIndex} className="flex gap-2 justify-center">
{row.map((key) => {
const isDone = key === 'DONE';
const isBackspace = key === 'BACKSPACE';
const isSpace = key === 'SPACE';
const isClear = key === 'CLEAR';
return (
<button
key={key}
onClick={() => handleKeyPress(key)}
className={`
font-bold rounded-xl transition-all active:scale-95 shadow-lg
${isDone
? 'bg-green-600 text-white hover:bg-green-700 px-12 py-5 text-xl'
: isBackspace
? 'bg-red-600 text-white hover:bg-red-700 px-8 py-5 text-xl'
: isSpace
? 'bg-gray-600 text-white hover:bg-gray-700 px-32 py-5'
: isClear
? 'bg-orange-600 text-white hover:bg-orange-700 px-8 py-5'
: 'bg-blue-600 text-white hover:bg-blue-700 min-w-[60px] py-5 text-2xl'
}
`}
>
{key === 'BACKSPACE' ? '⌫ Delete' :
key === 'SPACE' ? '_____ Space _____' :
key === 'CLEAR' ? '✕ Clear' :
key === 'DONE' ? '✓ Done' :
key}
</button>
);
})}
</div>
))}
<div className="bg-gray-100 p-4 rounded-lg mb-4">
<p className="text-gray-600 text-sm mb-1">Preview:</p>
<p className="text-2xl font-mono font-bold text-gray-800">
{formData[activeField] || '(empty)'}
</p>
</div>
<p className="text-center text-gray-400 text-sm mt-4">
Tap any key to type Tap Done when finished
</p>
<Keyboard
keyboardRef={r => (keyboard.current = r)}
layoutName={layoutName}
onChange={onKeyboardChange}
onKeyPress={onKeyPress}
theme="hg-theme-default hg-layout-default vitallink-keyboard"
display={{
'{bksp}': '⌫ backspace',
'{enter}': 'enter',
'{shift}': '⬆ shift',
'{lock}': 'caps',
'{tab}': 'tab',
'{space}': 'space',
'{close}': '✓ Done'
}}
buttonTheme={[
{
class: "hg-close-button",
buttons: "{close}"
}
]}
layout={{
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}",
"{space} {close}"
],
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}",
"{space} {close}"
]
}}
/>
</div>
</div>
)}