From 965c4136fcfb8adf57f1481b6ea2439cf217e2e9 Mon Sep 17 00:00:00 2001 From: steinhelge Date: Sun, 23 Nov 2025 21:33:49 +0100 Subject: [PATCH] Replace Tailwind with Bootstrap for improved UI styling --- src/hospitality-web/package-lock.json | 311 +++++++++++++++++- src/hospitality-web/package.json | 3 + src/hospitality-web/src/App.tsx | 51 ++- src/hospitality-web/src/index.css | 11 +- src/hospitality-web/src/main.tsx | 1 + .../src/pages/admin/EventsPage.tsx | 163 ++++----- .../src/pages/guest/GuestQrPage.tsx | 137 ++++---- .../src/pages/staff/ScannerPage.tsx | 159 +++++---- src/hospitality-web/tailwind.config.js | 11 - 9 files changed, 584 insertions(+), 263 deletions(-) delete mode 100644 src/hospitality-web/tailwind.config.js diff --git a/src/hospitality-web/package-lock.json b/src/hospitality-web/package-lock.json index a964dd2..576105d 100644 --- a/src/hospitality-web/package-lock.json +++ b/src/hospitality-web/package-lock.json @@ -8,13 +8,16 @@ "name": "hospitality-web", "version": "0.0.0", "dependencies": { + "@popperjs/core": "^2.11.8", "@tanstack/react-query": "^5.90.10", "axios": "^1.13.2", + "bootstrap": "^5.3.8", "date-fns": "^4.1.0", "html5-qrcode": "^2.3.8", "lucide-react": "^0.554.0", "qrcode.react": "^4.2.0", "react": "^19.2.0", + "react-bootstrap": "^2.10.10", "react-dom": "^19.2.0", "react-router-dom": "^7.9.6" }, @@ -271,6 +274,15 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/runtime": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz", + "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/template": { "version": "7.27.2", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", @@ -1058,6 +1070,85 @@ "node": ">= 8" } }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@react-aria/ssr": { + "version": "3.9.10", + "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.10.tgz", + "integrity": "sha512-hvTm77Pf+pMBhuBm760Li0BVIO38jv1IBws1xFm1NoL26PU+fe+FMW5+VZWyANR6nYL65joaJKZqOdTQMkO9IQ==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@restart/hooks": { + "version": "0.4.16", + "resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.16.tgz", + "integrity": "sha512-f7aCv7c+nU/3mF7NWLtVVr0Ra80RqsO89hO72r+Y/nvQr5+q0UFGkocElTH6MJApvReVh6JHUFYn2cw1WdHF3w==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.3" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@restart/ui": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@restart/ui/-/ui-1.9.4.tgz", + "integrity": "sha512-N4C7haUc3vn4LTwVUPlkJN8Ach/+yIMvRuTVIhjilNHqegY60SGLrzud6errOMNJwSnmYFnt1J0H/k8FE3A4KA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.26.0", + "@popperjs/core": "^2.11.8", + "@react-aria/ssr": "^3.5.0", + "@restart/hooks": "^0.5.0", + "@types/warning": "^3.0.3", + "dequal": "^2.0.3", + "dom-helpers": "^5.2.0", + "uncontrollable": "^8.0.4", + "warning": "^4.0.3" + }, + "peerDependencies": { + "react": ">=16.14.0", + "react-dom": ">=16.14.0" + } + }, + "node_modules/@restart/ui/node_modules/@restart/hooks": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.5.1.tgz", + "integrity": "sha512-EMoH04NHS1pbn07iLTjIjgttuqb7qu4+/EyhAx27MHpoENcB2ZdSsLTNxmKD+WEPnZigo62Qc8zjGnNxoSE/5Q==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.3" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@restart/ui/node_modules/uncontrollable": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-8.0.4.tgz", + "integrity": "sha512-ulRWYWHvscPFc0QQXvyJjY6LIXU56f0h8pQFvhxiKk5V1fcI8gp9Ht9leVAhrVjzqMw0BgjspBINx9r6oyJUvQ==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.14.0" + } + }, "node_modules/@rolldown/pluginutils": { "version": "1.0.0-beta.47", "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.47.tgz", @@ -1373,6 +1464,15 @@ "win32" ] }, + "node_modules/@swc/helpers": { + "version": "0.5.17", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz", + "integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, "node_modules/@tanstack/query-core": { "version": "5.90.10", "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.90.10.tgz", @@ -1468,6 +1568,12 @@ "undici-types": "~7.16.0" } }, + "node_modules/@types/prop-types": { + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "license": "MIT" + }, "node_modules/@types/qrcode.react": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/qrcode.react/-/qrcode.react-1.0.5.tgz", @@ -1482,7 +1588,6 @@ "version": "19.2.6", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.6.tgz", "integrity": "sha512-p/jUvulfgU7oKtj6Xpk8cA2Y1xKTtICGpJYeJXz2YVO2UcvjQgeRMLDGfDeqeRW2Ta+0QNFwcc8X3GH8SxZz6w==", - "dev": true, "license": "MIT", "dependencies": { "csstype": "^3.2.2" @@ -1498,6 +1603,21 @@ "@types/react": "^19.2.0" } }, + "node_modules/@types/react-transition-group": { + "version": "4.4.12", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz", + "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/warning": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.3.tgz", + "integrity": "sha512-D1XC7WK8K+zZEveUPY+cf4+kgauk8N4eHr/XIHXGlGYkHLud6hK9lYfZk1ry1TNh798cZUCgb6MqGEG8DkJt6Q==", + "license": "MIT" + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.47.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.47.0.tgz", @@ -1925,6 +2045,25 @@ "baseline-browser-mapping": "dist/cli.js" } }, + "node_modules/bootstrap": { + "version": "5.3.8", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.8.tgz", + "integrity": "sha512-HP1SZDqaLDPwsNiqRqi5NcP0SSXciX2s9E+RyqJIIqGo+vJeN5AJVM98CXmW/Wux0nQ5L7jeWUdplCEf0Ee+tg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/twbs" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + } + ], + "license": "MIT", + "peerDependencies": { + "@popperjs/core": "^2.11.8" + } + }, "node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", @@ -2044,6 +2183,12 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==", + "license": "MIT" + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -2118,7 +2263,6 @@ "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "dev": true, "license": "MIT" }, "node_modules/date-fns": { @@ -2165,6 +2309,25 @@ "node": ">=0.4.0" } }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -2880,6 +3043,15 @@ "node": ">=0.8.19" } }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -2924,7 +3096,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, "license": "MIT" }, "node_modules/js-yaml": { @@ -3034,6 +3205,18 @@ "dev": true, "license": "MIT" }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -3170,6 +3353,15 @@ "node": ">=0.10.0" } }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -3319,6 +3511,30 @@ "node": ">= 0.8.0" } }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types-extra": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/prop-types-extra/-/prop-types-extra-1.1.1.tgz", + "integrity": "sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew==", + "license": "MIT", + "dependencies": { + "react-is": "^16.3.2", + "warning": "^4.0.0" + }, + "peerDependencies": { + "react": ">=0.14.0" + } + }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", @@ -3374,6 +3590,37 @@ "node": ">=0.10.0" } }, + "node_modules/react-bootstrap": { + "version": "2.10.10", + "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-2.10.10.tgz", + "integrity": "sha512-gMckKUqn8aK/vCnfwoBpBVFUGT9SVQxwsYrp9yDHt0arXMamxALerliKBxr1TPbntirK/HGrUAHYbAeQTa9GHQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.24.7", + "@restart/hooks": "^0.4.9", + "@restart/ui": "^1.9.4", + "@types/prop-types": "^15.7.12", + "@types/react-transition-group": "^4.4.6", + "classnames": "^2.3.2", + "dom-helpers": "^5.2.1", + "invariant": "^2.2.4", + "prop-types": "^15.8.1", + "prop-types-extra": "^1.1.0", + "react-transition-group": "^4.4.5", + "uncontrollable": "^7.2.1", + "warning": "^4.0.3" + }, + "peerDependencies": { + "@types/react": ">=16.14.8", + "react": ">=16.14.0", + "react-dom": ">=16.14.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/react-dom": { "version": "19.2.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz", @@ -3386,6 +3633,18 @@ "react": "^19.2.0" } }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==", + "license": "MIT" + }, "node_modules/react-refresh": { "version": "0.18.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz", @@ -3434,6 +3693,22 @@ "react-dom": ">=18" } }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "license": "BSD-3-Clause", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -3683,6 +3958,12 @@ "typescript": ">=4.8.4" } }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -3734,6 +4015,21 @@ "typescript": ">=4.8.4 <6.0.0" } }, + "node_modules/uncontrollable": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-7.2.1.tgz", + "integrity": "sha512-svtcfoTADIB0nT9nltgjujTi7BzVmwjZClOmskKu/E8FW9BXzg9os8OLr4f8Dlnk0rYWJIWr4wv9eKUXiQvQwQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.6.3", + "@types/react": ">=16.9.11", + "invariant": "^2.2.4", + "react-lifecycles-compat": "^3.0.4" + }, + "peerDependencies": { + "react": ">=15.0.0" + } + }, "node_modules/undici-types": { "version": "7.16.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", @@ -3888,6 +4184,15 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/src/hospitality-web/package.json b/src/hospitality-web/package.json index cbd6526..b4546a2 100644 --- a/src/hospitality-web/package.json +++ b/src/hospitality-web/package.json @@ -10,13 +10,16 @@ "preview": "vite preview" }, "dependencies": { + "@popperjs/core": "^2.11.8", "@tanstack/react-query": "^5.90.10", "axios": "^1.13.2", + "bootstrap": "^5.3.8", "date-fns": "^4.1.0", "html5-qrcode": "^2.3.8", "lucide-react": "^0.554.0", "qrcode.react": "^4.2.0", "react": "^19.2.0", + "react-bootstrap": "^2.10.10", "react-dom": "^19.2.0", "react-router-dom": "^7.9.6" }, diff --git a/src/hospitality-web/src/App.tsx b/src/hospitality-web/src/App.tsx index 4affffc..3b9b0eb 100644 --- a/src/hospitality-web/src/App.tsx +++ b/src/hospitality-web/src/App.tsx @@ -1,5 +1,6 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { BrowserRouter, Routes, Route, Link } from 'react-router-dom'; +import { Navbar, Nav, Container } from 'react-bootstrap'; import EventsPage from './pages/admin/EventsPage'; import EventDetailPage from './pages/admin/EventDetailPage'; import GroupDetailPage from './pages/admin/GroupDetailPage'; @@ -12,35 +13,29 @@ function App() { return ( -
- + + + Hospitality System + + + + + + -
- - } /> - } /> - } /> - } /> - } /> - -
-
+ + + } /> + } /> + } /> + } /> + } /> + +
); diff --git a/src/hospitality-web/src/index.css b/src/hospitality-web/src/index.css index b5c61c9..6655a50 100644 --- a/src/hospitality-web/src/index.css +++ b/src/hospitality-web/src/index.css @@ -1,3 +1,8 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; +/* Custom styles */ +body { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', + 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', + sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} diff --git a/src/hospitality-web/src/main.tsx b/src/hospitality-web/src/main.tsx index bef5202..2ed1c95 100644 --- a/src/hospitality-web/src/main.tsx +++ b/src/hospitality-web/src/main.tsx @@ -1,5 +1,6 @@ import { StrictMode } from 'react' import { createRoot } from 'react-dom/client' +import 'bootstrap/dist/css/bootstrap.min.css' import './index.css' import App from './App.tsx' diff --git a/src/hospitality-web/src/pages/admin/EventsPage.tsx b/src/hospitality-web/src/pages/admin/EventsPage.tsx index 7ceca52..c450c14 100644 --- a/src/hospitality-web/src/pages/admin/EventsPage.tsx +++ b/src/hospitality-web/src/pages/admin/EventsPage.tsx @@ -1,5 +1,6 @@ import { useState } from 'react'; import { useNavigate } from 'react-router-dom'; +import { Button, Card, Row, Col, Form, Modal } from 'react-bootstrap'; import { useEvents, useCreateEvent } from '../../hooks/useEvents'; import type { CreateEventRequest } from '../../lib/types'; @@ -7,7 +8,7 @@ export default function EventsPage() { const navigate = useNavigate(); const { data: events, isLoading } = useEvents(); const createEvent = useCreateEvent(); - const [showForm, setShowForm] = useState(false); + const [showModal, setShowModal] = useState(false); const [formData, setFormData] = useState({ name: '', startDate: '', @@ -19,7 +20,7 @@ export default function EventsPage() { e.preventDefault(); try { const result = await createEvent.mutateAsync(formData); - setShowForm(false); + setShowModal(false); setFormData({ name: '', startDate: '', endDate: '', location: '' }); navigate(`/events/${result.id}`); } catch (error) { @@ -27,96 +28,102 @@ export default function EventsPage() { } }; - if (isLoading) return
Loading...
; + if (isLoading) return
Loading...
; return ( -
-
-

Events

- + <> +
+

Events

+
- {showForm && ( -
-

Create New Event

-
-
- - + {events?.map((event) => ( + + navigate(`/events/${event.id}`)} + > + + {event.name} + {event.location} + + + {new Date(event.startDate).toLocaleDateString()} + + +
+ {event.groupCount || 0} groups + {event.productCount || 0} products +
+
+
+ + ))} + + + setShowModal(false)}> + + Create New Event + + + + + Event Name + setFormData({ ...formData, name: e.target.value })} - className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent" /> -
-
-
- - setFormData({ ...formData, startDate: e.target.value })} - className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent" - /> -
-
- - setFormData({ ...formData, endDate: e.target.value })} - className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent" - /> -
-
-
- - + + + + Start Date + setFormData({ ...formData, startDate: e.target.value })} + /> + + + + + End Date + setFormData({ ...formData, endDate: e.target.value })} + /> + + + + + Location + setFormData({ ...formData, location: e.target.value })} - className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent" /> + +
+ +
- - -
- )} - -
- {events?.map((event) => ( -
navigate(`/events/${event.id}`)} - className="bg-white p-6 rounded-lg shadow hover:shadow-lg transition-shadow cursor-pointer" - > -

{event.name}

-

{event.location}

-
-

{new Date(event.startDate).toLocaleDateString()}

-
- {event.groupCount || 0} groups - {event.productCount || 0} products -
-
-
- ))} -
-
+ + + + ); } diff --git a/src/hospitality-web/src/pages/guest/GuestQrPage.tsx b/src/hospitality-web/src/pages/guest/GuestQrPage.tsx index 3d26cc8..3ce823f 100644 --- a/src/hospitality-web/src/pages/guest/GuestQrPage.tsx +++ b/src/hospitality-web/src/pages/guest/GuestQrPage.tsx @@ -1,4 +1,5 @@ import { useState } from 'react'; +import { Card, Form, Button, ProgressBar, ListGroup } from 'react-bootstrap'; import { QRCodeSVG } from 'qrcode.react'; import { usePersonByQrCode } from '../../hooks/useQrCode'; @@ -13,79 +14,79 @@ export default function GuestQrPage() { }; return ( -
-

Guest View

+
+
+

Guest View

- {!showQr ? ( -
-
-
- - setQrCode(e.target.value)} - placeholder="Your QR code (GUID)" - className="w-full px-4 py-3 border border-gray-300 rounded-lg text-lg" - required - /> -
- -
-
- ) : ( -
- {person && ( - <> -

{person.name}

- -
-
- -
-
+ {!showQr ? ( + + +
+ + Enter Your QR Code + setQrCode(e.target.value)} + placeholder="Your QR code (GUID)" + required + /> + + +
+
+
+ ) : ( + + + {person && ( + <> +

{person.name}

-

Your Quotas

-
- {person.quotas?.map((quota) => ( -
-
-
-

{quota.productName}

-

{quota.productType}

-
-
-

{quota.remainingAmount}

-

of {quota.initialAmount} remaining

-
-
-
-
+
+
+
- ))} -
- - - )} -
- )} +

Your Quotas

+ + {person.quotas?.map((quota) => ( + +
+
+
{quota.productName}
+ {quota.productType} +
+
+
{quota.remainingAmount}
+ of {quota.initialAmount} remaining +
+
+ +
+ ))} +
+ + + + )} + + + )} +
); } diff --git a/src/hospitality-web/src/pages/staff/ScannerPage.tsx b/src/hospitality-web/src/pages/staff/ScannerPage.tsx index 8fc6a2e..c903822 100644 --- a/src/hospitality-web/src/pages/staff/ScannerPage.tsx +++ b/src/hospitality-web/src/pages/staff/ScannerPage.tsx @@ -1,101 +1,116 @@ import { useState } from 'react'; +import { Card, Form, Button, Badge, ListGroup, Alert } from 'react-bootstrap'; import { usePersonByQrCode, useCreateTransaction } from '../../hooks/useQrCode'; export default function ScannerPage() { const [qrCode, setQrCode] = useState(''); - const [selectedProduct, setSelectedProduct] = useState(''); - const { data: person, isLoading } = usePersonByQrCode(qrCode); + const [searchQr, setSearchQr] = useState(''); + const { data: person, isLoading } = usePersonByQrCode(searchQr); const createTransaction = useCreateTransaction(); const handleScan = (e: React.FormEvent) => { e.preventDefault(); - // QR code is set, person data will load automatically + setSearchQr(qrCode); }; const handleTransaction = async (productId: string, amount: number) => { try { await createTransaction.mutateAsync({ - qrCode, + qrCode: searchQr, productId, amount, }); - alert('Transaction recorded!'); + alert('Transaction recorded successfully!'); } catch (error: any) { alert(error.response?.data || 'Failed to record transaction'); } }; return ( -
-

Staff Scanner

+
+
+

Staff Scanner

-
-
-
- - setQrCode(e.target.value)} - placeholder="Enter QR code (GUID)" - className="w-full px-4 py-3 border border-gray-300 rounded-lg text-lg focus:ring-2 focus:ring-blue-500" - /> + + + + + Scan or Enter QR Code + setQrCode(e.target.value)} + placeholder="Enter QR code (GUID)" + /> + + + + + + + {isLoading && ( +
+
+ Loading... +
- - + )} + + {person && ( + + +

{person.name}

+ {person.email && {person.email}} +
+ +
Available Quotas
+ {person.quotas && person.quotas.length > 0 ? ( + + {person.quotas.map((quota) => ( + +
+
+
{quota.productName}
+ {quota.productType} +
+
+

{quota.remainingAmount}

+ of {quota.initialAmount} +
+
+
+ + {quota.remainingAmount >= 2 && ( + + )} +
+
+ ))} +
+ ) : ( + No quotas assigned to this person. + )} +
+
+ )}
- - {isLoading &&
Loading...
} - - {person && ( -
-

{person.name}

- {person.email &&

{person.email}

} - -

Available Quotas

-
- {person.quotas?.map((quota) => ( -
-
-
-

{quota.productName}

-

{quota.productType}

-
-
-

{quota.remainingAmount}

-

of {quota.initialAmount}

-
-
-
- - {quota.remainingAmount >= 2 && ( - - )} -
-
- ))} -
-
- )}
); } diff --git a/src/hospitality-web/tailwind.config.js b/src/hospitality-web/tailwind.config.js deleted file mode 100644 index dca8ba0..0000000 --- a/src/hospitality-web/tailwind.config.js +++ /dev/null @@ -1,11 +0,0 @@ -/** @type {import('tailwindcss').Config} */ -export default { - content: [ - "./index.html", - "./src/**/*.{js,ts,jsx,tsx}", - ], - theme: { - extend: {}, - }, - plugins: [], -}