feat: temp

This commit is contained in:
tiennm99 committed 2025-08-03 18:21:30 +07:00
1 parent 2db93c38b9
commit e387961c59
9 files changed
+608 -120

No files matched your search

+1
View File
@@ -23,6 +23,7 @@
"tailwind-merge": "^3.3.1"
},
"devDependencies": {
"@tailwindcss/postcss": "^4.1.11",
"@testing-library/jest-dom": "^6.6.4",
"@testing-library/react": "^16.3.0",
"@testing-library/user-event": "^14.6.1",
+402 -47
View File
@@ -36,6 +36,9 @@ importers:
specifier: ^3.3.1
version: 3.3.1
devDependencies:
'@tailwindcss/postcss':
specifier: ^4.1.11
version: 4.1.11
'@testing-library/jest-dom':
specifier: ^6.6.4
version: 6.6.4
@@ -50,10 +53,10 @@ importers:
version: 10.4.21(postcss@8.5.6)
eslint:
specifier: ^9.0.0
version: 9.32.0
version: 9.32.0(jiti@2.5.1)
eslint-config-next:
specifier: ^15.0.0
version: 15.4.5(eslint@9.32.0)(typescript@5.8.3)
version: 15.4.5(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)
jest:
specifier: ^30.0.5
version: 30.0.5(@types/node@14.18.63)
@@ -75,6 +78,10 @@ packages:
'@adobe/css-tools@4.4.3':
resolution: {integrity: sha512-VQKMkwriZbaOgVCby1UDY/LDk5fIjhQicCvVPFqfe+69fWaPWydbWJ3wRt59/YzIwda1I81loas3oCoHxnqvdA==}
'@alloc/quick-lru@5.2.0':
resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
engines: {node: '>=10'}
'@ampproject/remapping@2.3.0':
resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
engines: {node: '>=6.0.0'}
@@ -472,6 +479,10 @@ packages:
resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
engines: {node: '>=12'}
'@isaacs/fs-minipass@4.0.1':
resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==}
engines: {node: '>=18.0.0'}
'@istanbuljs/load-nyc-config@1.1.0':
resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==}
engines: {node: '>=8'}
@@ -712,6 +723,94 @@ packages:
'@swc/helpers@0.5.15':
resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==}
'@tailwindcss/node@4.1.11':
resolution: {integrity: sha512-yzhzuGRmv5QyU9qLNg4GTlYI6STedBWRE7NjxP45CsFYYq9taI0zJXZBMqIC/c8fViNLhmrbpSFS57EoxUmD6Q==}
'@tailwindcss/oxide-android-arm64@4.1.11':
resolution: {integrity: sha512-3IfFuATVRUMZZprEIx9OGDjG3Ou3jG4xQzNTvjDoKmU9JdmoCohQJ83MYd0GPnQIu89YoJqvMM0G3uqLRFtetg==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [android]
'@tailwindcss/oxide-darwin-arm64@4.1.11':
resolution: {integrity: sha512-ESgStEOEsyg8J5YcMb1xl8WFOXfeBmrhAwGsFxxB2CxY9evy63+AtpbDLAyRkJnxLy2WsD1qF13E97uQyP1lfQ==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [darwin]
'@tailwindcss/oxide-darwin-x64@4.1.11':
resolution: {integrity: sha512-EgnK8kRchgmgzG6jE10UQNaH9Mwi2n+yw1jWmof9Vyg2lpKNX2ioe7CJdf9M5f8V9uaQxInenZkOxnTVL3fhAw==}
engines: {node: '>= 10'}
cpu: [x64]
os: [darwin]
'@tailwindcss/oxide-freebsd-x64@4.1.11':
resolution: {integrity: sha512-xdqKtbpHs7pQhIKmqVpxStnY1skuNh4CtbcyOHeX1YBE0hArj2romsFGb6yUmzkq/6M24nkxDqU8GYrKrz+UcA==}
engines: {node: '>= 10'}
cpu: [x64]
os: [freebsd]
'@tailwindcss/oxide-linux-arm-gnueabihf@4.1.11':
resolution: {integrity: sha512-ryHQK2eyDYYMwB5wZL46uoxz2zzDZsFBwfjssgB7pzytAeCCa6glsiJGjhTEddq/4OsIjsLNMAiMlHNYnkEEeg==}
engines: {node: '>= 10'}
cpu: [arm]
os: [linux]
'@tailwindcss/oxide-linux-arm64-gnu@4.1.11':
resolution: {integrity: sha512-mYwqheq4BXF83j/w75ewkPJmPZIqqP1nhoghS9D57CLjsh3Nfq0m4ftTotRYtGnZd3eCztgbSPJ9QhfC91gDZQ==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
'@tailwindcss/oxide-linux-arm64-musl@4.1.11':
resolution: {integrity: sha512-m/NVRFNGlEHJrNVk3O6I9ggVuNjXHIPoD6bqay/pubtYC9QIdAMpS+cswZQPBLvVvEF6GtSNONbDkZrjWZXYNQ==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
'@tailwindcss/oxide-linux-x64-gnu@4.1.11':
resolution: {integrity: sha512-YW6sblI7xukSD2TdbbaeQVDysIm/UPJtObHJHKxDEcW2exAtY47j52f8jZXkqE1krdnkhCMGqP3dbniu1Te2Fg==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
'@tailwindcss/oxide-linux-x64-musl@4.1.11':
resolution: {integrity: sha512-e3C/RRhGunWYNC3aSF7exsQkdXzQ/M+aYuZHKnw4U7KQwTJotnWsGOIVih0s2qQzmEzOFIJ3+xt7iq67K/p56Q==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
'@tailwindcss/oxide-wasm32-wasi@4.1.11':
resolution: {integrity: sha512-Xo1+/GU0JEN/C/dvcammKHzeM6NqKovG+6921MR6oadee5XPBaKOumrJCXvopJ/Qb5TH7LX/UAywbqrP4lax0g==}
engines: {node: '>=14.0.0'}
cpu: [wasm32]
bundledDependencies:
- '@napi-rs/wasm-runtime'
- '@emnapi/core'
- '@emnapi/runtime'
- '@tybys/wasm-util'
- '@emnapi/wasi-threads'
- tslib
'@tailwindcss/oxide-win32-arm64-msvc@4.1.11':
resolution: {integrity: sha512-UgKYx5PwEKrac3GPNPf6HVMNhUIGuUh4wlDFR2jYYdkX6pL/rn73zTq/4pzUm8fOjAn5L8zDeHp9iXmUGOXZ+w==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [win32]
'@tailwindcss/oxide-win32-x64-msvc@4.1.11':
resolution: {integrity: sha512-YfHoggn1j0LK7wR82TOucWc5LDCguHnoS879idHekmmiR7g9HUtMw9MI0NHatS28u/Xlkfi9w5RJWgz2Dl+5Qg==}
engines: {node: '>= 10'}
cpu: [x64]
os: [win32]
'@tailwindcss/oxide@4.1.11':
resolution: {integrity: sha512-Q69XzrtAhuyfHo+5/HMgr1lAiPP/G40OMFAnws7xcFEYqcypZmdW8eGXaOUIeOl1dzPJBPENXgbjsOyhg2nkrg==}
engines: {node: '>= 10'}
'@tailwindcss/postcss@4.1.11':
resolution: {integrity: sha512-q/EAIIpF6WpLhKEuQSEVMZNMIY8KhWoAemZ9eylNAih9jxMGAYPPWBn3I9QL/2jZ+e7OEz/tZkX5HwbBR4HohA==}
'@tanstack/query-core@5.83.1':
resolution: {integrity: sha512-OG69LQgT7jSp+5pPuCfzltq/+7l2xoweggjme9vlbCPa/d7D7zaqv5vN/S82SzSYZ4EDLTxNO1PWrv49RAS64Q==}
@@ -1202,6 +1301,10 @@ packages:
resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==}
engines: {node: '>=10'}
chownr@3.0.0:
resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==}
engines: {node: '>=18'}
ci-info@4.3.0:
resolution: {integrity: sha512-l+2bNRMiQgcfILUi33labAZYIWlH1kWDp+ecNo5iisRKrbm0xcRyCww71/YU0Fkw0mAFpz9bJayXPjey6vkmaQ==}
engines: {node: '>=8'}
@@ -1374,6 +1477,10 @@ packages:
emoji-regex@9.2.2:
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
enhanced-resolve@5.18.2:
resolution: {integrity: sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ==}
engines: {node: '>=10.13.0'}
entities@6.0.1:
resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==}
engines: {node: '>=0.12'}
@@ -2122,6 +2229,10 @@ packages:
node-notifier:
optional: true
jiti@2.5.1:
resolution: {integrity: sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==}
hasBin: true
js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
@@ -2200,6 +2311,70 @@ packages:
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
engines: {node: '>= 0.8.0'}
lightningcss-darwin-arm64@1.30.1:
resolution: {integrity: sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [darwin]
lightningcss-darwin-x64@1.30.1:
resolution: {integrity: sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [darwin]
lightningcss-freebsd-x64@1.30.1:
resolution: {integrity: sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [freebsd]
lightningcss-linux-arm-gnueabihf@1.30.1:
resolution: {integrity: sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==}
engines: {node: '>= 12.0.0'}
cpu: [arm]
os: [linux]
lightningcss-linux-arm64-gnu@1.30.1:
resolution: {integrity: sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [linux]
lightningcss-linux-arm64-musl@1.30.1:
resolution: {integrity: sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [linux]
lightningcss-linux-x64-gnu@1.30.1:
resolution: {integrity: sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [linux]
lightningcss-linux-x64-musl@1.30.1:
resolution: {integrity: sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [linux]
lightningcss-win32-arm64-msvc@1.30.1:
resolution: {integrity: sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [win32]
lightningcss-win32-x64-msvc@1.30.1:
resolution: {integrity: sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [win32]
lightningcss@1.30.1:
resolution: {integrity: sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==}
engines: {node: '>= 12.0.0'}
lines-and-columns@1.2.4:
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
@@ -2235,6 +2410,9 @@ packages:
resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==}
hasBin: true
magic-string@0.30.17:
resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==}
make-dir@4.0.0:
resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==}
engines: {node: '>=10'}
@@ -2292,6 +2470,15 @@ packages:
resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
engines: {node: '>=16 || 14 >=14.17'}
minizlib@3.0.2:
resolution: {integrity: sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==}
engines: {node: '>= 18'}
mkdirp@3.0.1:
resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==}
engines: {node: '>=10'}
hasBin: true
ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
@@ -2852,6 +3039,14 @@ packages:
tailwindcss@4.1.11:
resolution: {integrity: sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA==}
tapable@2.2.2:
resolution: {integrity: sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==}
engines: {node: '>=6'}
tar@7.4.3:
resolution: {integrity: sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==}
engines: {node: '>=18'}
test-exclude@6.0.0:
resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==}
engines: {node: '>=8'}
@@ -3069,6 +3264,10 @@ packages:
yallist@3.1.1:
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
yallist@5.0.0:
resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==}
engines: {node: '>=18'}
yargs-parser@21.1.1:
resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
engines: {node: '>=12'}
@@ -3085,6 +3284,8 @@ snapshots:
'@adobe/css-tools@4.4.3': {}
'@alloc/quick-lru@5.2.0': {}
'@ampproject/remapping@2.3.0':
dependencies:
'@jridgewell/gen-mapping': 0.3.12
@@ -3325,9 +3526,9 @@ snapshots:
tslib: 2.8.1
optional: true
'@eslint-community/eslint-utils@4.7.0(eslint@9.32.0)':
'@eslint-community/eslint-utils@4.7.0(eslint@9.32.0(jiti@2.5.1))':
dependencies:
eslint: 9.32.0
eslint: 9.32.0(jiti@2.5.1)
eslint-visitor-keys: 3.4.3
'@eslint-community/regexpp@4.12.1': {}
@@ -3477,6 +3678,10 @@ snapshots:
wrap-ansi: 8.1.0
wrap-ansi-cjs: wrap-ansi@7.0.0
'@isaacs/fs-minipass@4.0.1':
dependencies:
minipass: 7.1.2
'@istanbuljs/load-nyc-config@1.1.0':
dependencies:
camelcase: 5.3.1
@@ -3794,6 +3999,78 @@ snapshots:
dependencies:
tslib: 2.8.1
'@tailwindcss/node@4.1.11':
dependencies:
'@ampproject/remapping': 2.3.0
enhanced-resolve: 5.18.2
jiti: 2.5.1
lightningcss: 1.30.1
magic-string: 0.30.17
source-map-js: 1.2.1
tailwindcss: 4.1.11
'@tailwindcss/oxide-android-arm64@4.1.11':
optional: true
'@tailwindcss/oxide-darwin-arm64@4.1.11':
optional: true
'@tailwindcss/oxide-darwin-x64@4.1.11':
optional: true
'@tailwindcss/oxide-freebsd-x64@4.1.11':
optional: true
'@tailwindcss/oxide-linux-arm-gnueabihf@4.1.11':
optional: true
'@tailwindcss/oxide-linux-arm64-gnu@4.1.11':
optional: true
'@tailwindcss/oxide-linux-arm64-musl@4.1.11':
optional: true
'@tailwindcss/oxide-linux-x64-gnu@4.1.11':
optional: true
'@tailwindcss/oxide-linux-x64-musl@4.1.11':
optional: true
'@tailwindcss/oxide-wasm32-wasi@4.1.11':
optional: true
'@tailwindcss/oxide-win32-arm64-msvc@4.1.11':
optional: true
'@tailwindcss/oxide-win32-x64-msvc@4.1.11':
optional: true
'@tailwindcss/oxide@4.1.11':
dependencies:
detect-libc: 2.0.4
tar: 7.4.3
optionalDependencies:
'@tailwindcss/oxide-android-arm64': 4.1.11
'@tailwindcss/oxide-darwin-arm64': 4.1.11
'@tailwindcss/oxide-darwin-x64': 4.1.11
'@tailwindcss/oxide-freebsd-x64': 4.1.11
'@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.11
'@tailwindcss/oxide-linux-arm64-gnu': 4.1.11
'@tailwindcss/oxide-linux-arm64-musl': 4.1.11
'@tailwindcss/oxide-linux-x64-gnu': 4.1.11
'@tailwindcss/oxide-linux-x64-musl': 4.1.11
'@tailwindcss/oxide-wasm32-wasi': 4.1.11
'@tailwindcss/oxide-win32-arm64-msvc': 4.1.11
'@tailwindcss/oxide-win32-x64-msvc': 4.1.11
'@tailwindcss/postcss@4.1.11':
dependencies:
'@alloc/quick-lru': 5.2.0
'@tailwindcss/node': 4.1.11
'@tailwindcss/oxide': 4.1.11
postcss: 8.5.6
tailwindcss: 4.1.11
'@tanstack/query-core@5.83.1': {}
'@tanstack/react-query@5.84.1(react@19.1.1)':
@@ -3927,15 +4204,15 @@ snapshots:
dependencies:
'@types/yargs-parser': 21.0.3
'@typescript-eslint/eslint-plugin@8.38.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0)(typescript@5.8.3))(eslint@9.32.0)(typescript@5.8.3)':
'@typescript-eslint/eslint-plugin@8.38.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3))(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)':
dependencies:
'@eslint-community/regexpp': 4.12.1
'@typescript-eslint/parser': 8.38.0(eslint@9.32.0)(typescript@5.8.3)
'@typescript-eslint/parser': 8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)
'@typescript-eslint/scope-manager': 8.38.0
'@typescript-eslint/type-utils': 8.38.0(eslint@9.32.0)(typescript@5.8.3)
'@typescript-eslint/utils': 8.38.0(eslint@9.32.0)(typescript@5.8.3)
'@typescript-eslint/type-utils': 8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)
'@typescript-eslint/utils': 8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)
'@typescript-eslint/visitor-keys': 8.38.0
eslint: 9.32.0
eslint: 9.32.0(jiti@2.5.1)
graphemer: 1.4.0
ignore: 7.0.5
natural-compare: 1.4.0
@@ -3944,14 +4221,14 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@typescript-eslint/parser@8.38.0(eslint@9.32.0)(typescript@5.8.3)':
'@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)':
dependencies:
'@typescript-eslint/scope-manager': 8.38.0
'@typescript-eslint/types': 8.38.0
'@typescript-eslint/typescript-estree': 8.38.0(typescript@5.8.3)
'@typescript-eslint/visitor-keys': 8.38.0
debug: 4.4.1
eslint: 9.32.0
eslint: 9.32.0(jiti@2.5.1)
typescript: 5.8.3
transitivePeerDependencies:
- supports-color
@@ -3974,13 +4251,13 @@ snapshots:
dependencies:
typescript: 5.8.3
'@typescript-eslint/type-utils@8.38.0(eslint@9.32.0)(typescript@5.8.3)':
'@typescript-eslint/type-utils@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)':
dependencies:
'@typescript-eslint/types': 8.38.0
'@typescript-eslint/typescript-estree': 8.38.0(typescript@5.8.3)
'@typescript-eslint/utils': 8.38.0(eslint@9.32.0)(typescript@5.8.3)
'@typescript-eslint/utils': 8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)
debug: 4.4.1
eslint: 9.32.0
eslint: 9.32.0(jiti@2.5.1)
ts-api-utils: 2.1.0(typescript@5.8.3)
typescript: 5.8.3
transitivePeerDependencies:
@@ -4004,13 +4281,13 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@typescript-eslint/utils@8.38.0(eslint@9.32.0)(typescript@5.8.3)':
'@typescript-eslint/utils@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)':
dependencies:
'@eslint-community/eslint-utils': 4.7.0(eslint@9.32.0)
'@eslint-community/eslint-utils': 4.7.0(eslint@9.32.0(jiti@2.5.1))
'@typescript-eslint/scope-manager': 8.38.0
'@typescript-eslint/types': 8.38.0
'@typescript-eslint/typescript-estree': 8.38.0(typescript@5.8.3)
eslint: 9.32.0
eslint: 9.32.0(jiti@2.5.1)
typescript: 5.8.3
transitivePeerDependencies:
- supports-color
@@ -4336,6 +4613,8 @@ snapshots:
char-regex@1.0.2: {}
chownr@3.0.0: {}
ci-info@4.3.0: {}
cjs-module-lexer@2.1.0: {}
@@ -4448,8 +4727,7 @@ snapshots:
dequal@2.0.3: {}
detect-libc@2.0.4:
optional: true
detect-libc@2.0.4: {}
detect-newline@3.1.0: {}
@@ -4483,6 +4761,11 @@ snapshots:
emoji-regex@9.2.2: {}
enhanced-resolve@5.18.2:
dependencies:
graceful-fs: 4.2.11
tapable: 2.2.2
entities@6.0.1: {}
error-ex@1.3.2:
@@ -4602,19 +4885,19 @@ snapshots:
escape-string-regexp@4.0.0: {}
eslint-config-next@15.4.5(eslint@9.32.0)(typescript@5.8.3):
eslint-config-next@15.4.5(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3):
dependencies:
'@next/eslint-plugin-next': 15.4.5
'@rushstack/eslint-patch': 1.12.0
'@typescript-eslint/eslint-plugin': 8.38.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0)(typescript@5.8.3))(eslint@9.32.0)(typescript@5.8.3)
'@typescript-eslint/parser': 8.38.0(eslint@9.32.0)(typescript@5.8.3)
eslint: 9.32.0
'@typescript-eslint/eslint-plugin': 8.38.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3))(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)
'@typescript-eslint/parser': 8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)
eslint: 9.32.0(jiti@2.5.1)
eslint-import-resolver-node: 0.3.9
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.32.0)
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0)(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.32.0)
eslint-plugin-jsx-a11y: 6.10.2(eslint@9.32.0)
eslint-plugin-react: 7.37.5(eslint@9.32.0)
eslint-plugin-react-hooks: 5.2.0(eslint@9.32.0)
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.32.0(jiti@2.5.1))
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.32.0(jiti@2.5.1))
eslint-plugin-jsx-a11y: 6.10.2(eslint@9.32.0(jiti@2.5.1))
eslint-plugin-react: 7.37.5(eslint@9.32.0(jiti@2.5.1))
eslint-plugin-react-hooks: 5.2.0(eslint@9.32.0(jiti@2.5.1))
optionalDependencies:
typescript: 5.8.3
transitivePeerDependencies:
@@ -4630,33 +4913,33 @@ snapshots:
transitivePeerDependencies:
- supports-color
eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.32.0):
eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.32.0(jiti@2.5.1)):
dependencies:
'@nolyfill/is-core-module': 1.0.39
debug: 4.4.1
eslint: 9.32.0
eslint: 9.32.0(jiti@2.5.1)
get-tsconfig: 4.10.1
is-bun-module: 2.0.0
stable-hash: 0.0.5
tinyglobby: 0.2.14
unrs-resolver: 1.11.1
optionalDependencies:
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0)(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.32.0)
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.32.0(jiti@2.5.1))
transitivePeerDependencies:
- supports-color
eslint-module-utils@2.12.1(@typescript-eslint/parser@8.38.0(eslint@9.32.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.32.0):
eslint-module-utils@2.12.1(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.32.0(jiti@2.5.1)):
dependencies:
debug: 3.2.7
optionalDependencies:
'@typescript-eslint/parser': 8.38.0(eslint@9.32.0)(typescript@5.8.3)
eslint: 9.32.0
'@typescript-eslint/parser': 8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)
eslint: 9.32.0(jiti@2.5.1)
eslint-import-resolver-node: 0.3.9
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.32.0)
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.32.0(jiti@2.5.1))
transitivePeerDependencies:
- supports-color
eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0)(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.32.0):
eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.32.0(jiti@2.5.1)):
dependencies:
'@rtsao/scc': 1.1.0
array-includes: 3.1.9
@@ -4665,9 +4948,9 @@ snapshots:
array.prototype.flatmap: 1.3.3
debug: 3.2.7
doctrine: 2.1.0
eslint: 9.32.0
eslint: 9.32.0(jiti@2.5.1)
eslint-import-resolver-node: 0.3.9
eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.38.0(eslint@9.32.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.32.0)
eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.32.0(jiti@2.5.1))
hasown: 2.0.2
is-core-module: 2.16.1
is-glob: 4.0.3
@@ -4679,13 +4962,13 @@ snapshots:
string.prototype.trimend: 1.0.9
tsconfig-paths: 3.15.0
optionalDependencies:
'@typescript-eslint/parser': 8.38.0(eslint@9.32.0)(typescript@5.8.3)
'@typescript-eslint/parser': 8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)
transitivePeerDependencies:
- eslint-import-resolver-typescript
- eslint-import-resolver-webpack
- supports-color
eslint-plugin-jsx-a11y@6.10.2(eslint@9.32.0):
eslint-plugin-jsx-a11y@6.10.2(eslint@9.32.0(jiti@2.5.1)):
dependencies:
aria-query: 5.3.2
array-includes: 3.1.9
@@ -4695,7 +4978,7 @@ snapshots:
axobject-query: 4.1.0
damerau-levenshtein: 1.0.8
emoji-regex: 9.2.2
eslint: 9.32.0
eslint: 9.32.0(jiti@2.5.1)
hasown: 2.0.2
jsx-ast-utils: 3.3.5
language-tags: 1.0.9
@@ -4704,11 +4987,11 @@ snapshots:
safe-regex-test: 1.1.0
string.prototype.includes: 2.0.1
eslint-plugin-react-hooks@5.2.0(eslint@9.32.0):
eslint-plugin-react-hooks@5.2.0(eslint@9.32.0(jiti@2.5.1)):
dependencies:
eslint: 9.32.0
eslint: 9.32.0(jiti@2.5.1)
eslint-plugin-react@7.37.5(eslint@9.32.0):
eslint-plugin-react@7.37.5(eslint@9.32.0(jiti@2.5.1)):
dependencies:
array-includes: 3.1.9
array.prototype.findlast: 1.2.5
@@ -4716,7 +4999,7 @@ snapshots:
array.prototype.tosorted: 1.1.4
doctrine: 2.1.0
es-iterator-helpers: 1.2.1
eslint: 9.32.0
eslint: 9.32.0(jiti@2.5.1)
estraverse: 5.3.0
hasown: 2.0.2
jsx-ast-utils: 3.3.5
@@ -4739,9 +5022,9 @@ snapshots:
eslint-visitor-keys@4.2.1: {}
eslint@9.32.0:
eslint@9.32.0(jiti@2.5.1):
dependencies:
'@eslint-community/eslint-utils': 4.7.0(eslint@9.32.0)
'@eslint-community/eslint-utils': 4.7.0(eslint@9.32.0(jiti@2.5.1))
'@eslint-community/regexpp': 4.12.1
'@eslint/config-array': 0.21.0
'@eslint/config-helpers': 0.3.0
@@ -4776,6 +5059,8 @@ snapshots:
minimatch: 3.1.2
natural-compare: 1.4.0
optionator: 0.9.4
optionalDependencies:
jiti: 2.5.1
transitivePeerDependencies:
- supports-color
@@ -5590,6 +5875,8 @@ snapshots:
- supports-color
- ts-node
jiti@2.5.1: {}
js-tokens@4.0.0: {}
js-yaml@3.14.1:
@@ -5674,6 +5961,51 @@ snapshots:
prelude-ls: 1.2.1
type-check: 0.4.0
lightningcss-darwin-arm64@1.30.1:
optional: true
lightningcss-darwin-x64@1.30.1:
optional: true
lightningcss-freebsd-x64@1.30.1:
optional: true
lightningcss-linux-arm-gnueabihf@1.30.1:
optional: true
lightningcss-linux-arm64-gnu@1.30.1:
optional: true
lightningcss-linux-arm64-musl@1.30.1:
optional: true
lightningcss-linux-x64-gnu@1.30.1:
optional: true
lightningcss-linux-x64-musl@1.30.1:
optional: true
lightningcss-win32-arm64-msvc@1.30.1:
optional: true
lightningcss-win32-x64-msvc@1.30.1:
optional: true
lightningcss@1.30.1:
dependencies:
detect-libc: 2.0.4
optionalDependencies:
lightningcss-darwin-arm64: 1.30.1
lightningcss-darwin-x64: 1.30.1
lightningcss-freebsd-x64: 1.30.1
lightningcss-linux-arm-gnueabihf: 1.30.1
lightningcss-linux-arm64-gnu: 1.30.1
lightningcss-linux-arm64-musl: 1.30.1
lightningcss-linux-x64-gnu: 1.30.1
lightningcss-linux-x64-musl: 1.30.1
lightningcss-win32-arm64-msvc: 1.30.1
lightningcss-win32-x64-msvc: 1.30.1
lines-and-columns@1.2.4: {}
locate-path@5.0.0:
@@ -5702,6 +6034,10 @@ snapshots:
lz-string@1.5.0: {}
magic-string@0.30.17:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.4
make-dir@4.0.0:
dependencies:
semver: 7.7.2
@@ -5797,6 +6133,12 @@ snapshots:
minipass@7.1.2: {}
minizlib@3.0.2:
dependencies:
minipass: 7.1.2
mkdirp@3.0.1: {}
ms@2.1.3: {}
murmurhash-js@1.0.0: {}
@@ -6398,6 +6740,17 @@ snapshots:
tailwindcss@4.1.11: {}
tapable@2.2.2: {}
tar@7.4.3:
dependencies:
'@isaacs/fs-minipass': 4.0.1
chownr: 3.0.0
minipass: 7.1.2
minizlib: 3.0.2
mkdirp: 3.0.1
yallist: 5.0.0
test-exclude@6.0.0:
dependencies:
'@istanbuljs/schema': 0.1.3
@@ -6664,6 +7017,8 @@ snapshots:
yallist@3.1.1: {}
yallist@5.0.0: {}
yargs-parser@21.1.1: {}
yargs@17.7.2:
+1 -1
View File
@@ -1,6 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
'@tailwindcss/postcss': {},
autoprefixer: {},
},
}
+2 -2
View File
@@ -24,8 +24,8 @@ export async function POST(request) {
}
const sessionId = `${username}_${Date.now()}`;
const distance = calculateDistance(guessLat, guessLng, trueLat, trueLng);
const points = calculatePoints(distance);
const distance = await calculateDistance(guessLat, guessLng, trueLat, trueLng);
const points = calculatePoints(distance / 1000); // Convert to km for points calculation
const sessionData = {
username,
+4 -4
View File
@@ -103,26 +103,26 @@ function GamePageContent() {
setShowResultModal(true);
} else {
console.error('Failed to submit score');
const fallbackDistance = calculateDistance(
const fallbackDistance = await calculateDistance(
guessLocation[0],
guessLocation[1],
trueLocation[0],
trueLocation[1]
);
setDistance(fallbackDistance);
setPoints(calculatePoints(fallbackDistance));
setPoints(calculatePoints(fallbackDistance / 1000)); // Convert to km
setShowResultModal(true);
}
} catch (error) {
console.error('Error submitting score:', error);
const fallbackDistance = calculateDistance(
const fallbackDistance = await calculateDistance(
guessLocation[0],
guessLocation[1],
trueLocation[0],
trueLocation[1]
);
setDistance(fallbackDistance);
setPoints(calculatePoints(fallbackDistance));
setPoints(calculatePoints(fallbackDistance / 1000)); // Convert to km
setShowResultModal(true);
}
};
+12 -3
View File
@@ -1,7 +1,16 @@
@import url('https://fonts.googleapis.com/css2?family=Jersey+15:wght@400&display=swap');
@tailwind base;
@tailwind components;
@tailwind utilities;
@import "tailwindcss";
@theme {
--color-primary: #05A38C;
--color-primary-hover: #048a76;
--color-background: #1a1a1a;
--color-background-secondary: #24453D;
--color-text: #e6f4e6;
--color-text-secondary: #ccc;
--font-jersey: "Jersey 15", cursive;
--font-courier: "Courier", monospace;
}
@layer base {
* {
+92 -15
View File
@@ -7,17 +7,96 @@ import 'mapillary-js/dist/mapillary.css';
export default function PanoViewer({ imageData, isLoading, onPreciseLocationLoad }) {
const panoRef = useRef(null);
const viewerRef = useRef(null);
const currentImageIdRef = useRef(null);
const isInitializingRef = useRef(false);
// Clean up viewer on unmount only
useEffect(() => {
if (!panoRef.current || !imageData) return;
return () => {
if (viewerRef.current) {
try {
// Only remove if navigable to avoid the error
if (viewerRef.current.isNavigable) {
viewerRef.current.remove();
} else {
// If not navigable, just clear the reference
console.log('Viewer not navigable during cleanup, skipping remove()');
}
} catch (error) {
// Ignore cleanup errors on unmount
}
viewerRef.current = null;
}
};
}, []);
// Handle image changes
useEffect(() => {
if (!panoRef.current || !imageData || isLoading) return;
// Skip if same image
if (currentImageIdRef.current === imageData.id) return;
// Skip if already initializing
if (isInitializingRef.current) return;
const initOrUpdateViewer = async () => {
isInitializingRef.current = true;
currentImageIdRef.current = imageData.id;
const initViewer = async () => {
try {
// If viewer exists, try to navigate to new image instead of recreating
if (viewerRef.current) {
viewerRef.current.remove();
try {
// Check if viewer is navigable before attempting moveTo
if (viewerRef.current.isNavigable) {
await viewerRef.current.moveTo(imageData.id);
isInitializingRef.current = false;
return;
} else {
console.log('Viewer is not navigable, will recreate');
// Wait for navigation to be possible or recreate
const checkNavigable = () => {
if (viewerRef.current && viewerRef.current.isNavigable) {
viewerRef.current.moveTo(imageData.id).then(() => {
isInitializingRef.current = false;
}).catch(() => {
// If moveTo fails, recreate viewer
if (viewerRef.current) {
viewerRef.current.remove();
viewerRef.current = null;
initOrUpdateViewer(); // Recreate
}
});
} else {
// Viewer still not navigable, recreate immediately
if (viewerRef.current) {
viewerRef.current.remove();
viewerRef.current = null;
}
// Continue with new viewer creation below
}
};
// Give it a brief moment to become navigable
setTimeout(checkNavigable, 100);
return;
}
} catch (error) {
console.log('Navigation failed, will recreate viewer:', error.message);
// Only remove if viewer is navigable or wait for it to be
if (viewerRef.current && viewerRef.current.isNavigable) {
viewerRef.current.remove();
viewerRef.current = null;
} else {
console.log('Cannot remove non-navigable viewer, will replace');
viewerRef.current = null;
}
}
}
viewerRef.current = new Viewer({
// Create new viewer
const newViewer = new Viewer({
accessToken: process.env.NEXT_PUBLIC_MAPILLARY_ACCESS_TOKEN || 'MLY|24113623194974280|5bf83fa202912f1cc3210b2cf968fb65',
container: panoRef.current,
imageId: imageData.id,
@@ -41,12 +120,14 @@ export default function PanoViewer({ imageData, isLoading, onPreciseLocationLoad
}
});
viewerRef.current.on('image', async (image) => {
viewerRef.current = newViewer;
newViewer.on('image', async (image) => {
console.log('MapillaryJS image loaded successfully:', image.id);
try {
// Get precise position from MapillaryJS
const precisePosition = await viewerRef.current.getPosition();
const precisePosition = await newViewer.getPosition();
console.log('Precise MapillaryJS coordinates:', precisePosition);
// Notify parent component of precise coordinates
@@ -62,7 +143,7 @@ export default function PanoViewer({ imageData, isLoading, onPreciseLocationLoad
}
});
viewerRef.current.on('error', (error) => {
newViewer.on('error', (error) => {
console.error('Error loading MapillaryJS image:', error);
// Fallback to regular image
if (panoRef.current) {
@@ -77,6 +158,7 @@ export default function PanoViewer({ imageData, isLoading, onPreciseLocationLoad
onPreciseLocationLoad(imageData.lat, imageData.lng);
}
});
} catch (error) {
console.error('Error initializing MapillaryJS:', error);
// Fallback to regular image
@@ -91,17 +173,12 @@ export default function PanoViewer({ imageData, isLoading, onPreciseLocationLoad
if (onPreciseLocationLoad) {
onPreciseLocationLoad(imageData.lat, imageData.lng);
}
} finally {
isInitializingRef.current = false;
}
};
initViewer();
return () => {
if (viewerRef.current) {
viewerRef.current.remove();
viewerRef.current = null;
}
};
initOrUpdateViewer();
}, [imageData, onPreciseLocationLoad]);
// Expose viewer instance for potential coordinate synchronization
+94 -17
View File
@@ -1,34 +1,111 @@
/**
* Calculates the distance between two geographic coordinates using the Haversine formula
* Calculates straight-line distance between two coordinates using Haversine formula
* @param {number} lat1 - Latitude of first point
* @param {number} lon1 - Longitude of first point
* @param {number} lat2 - Latitude of second point
* @param {number} lon2 - Longitude of second point
* @returns {number} Distance in meters, rounded to 1 decimal place
*/
export function calculateDistance(lat1, lon1, lat2, lon2) {
function calculateHaversineDistance(lat1, lon1, lat2, lon2) {
if (lat1 === lat2 && lon1 === lon2) {
return 0;
}
const radlat1 = Math.PI * lat1 / 180;
const radlat2 = Math.PI * lat2 / 180;
const theta = lon1 - lon2;
const radtheta = Math.PI * theta / 180;
let dist = Math.sin(radlat1) * Math.sin(radlat2) +
Math.cos(radlat1) * Math.cos(radlat2) * Math.cos(radtheta);
if (dist > 1) {
dist = 1;
const R = 6371000; // Earth's radius in meters
const φ1 = lat1 * Math.PI / 180;
const φ2 = lat2 * Math.PI / 180;
const Δφ = (lat2 - lat1) * Math.PI / 180;
const Δλ = (lon2 - lon1) * Math.PI / 180;
const a = Math.sin(Δφ / 2) * Math.sin(Δφ / 2) +
Math.cos(φ1) * Math.cos(φ2) *
Math.sin(Δλ / 2) * Math.sin(Δλ / 2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
const distance = R * c;
return parseFloat(distance.toFixed(1));
}
/**
* Validates coordinates using OpenStreetMap Nominatim API
* @param {number} lat - Latitude
* @param {number} lon - Longitude
* @returns {Promise<boolean>} True if coordinates are valid and on land
*/
async function validateCoordinatesWithOSM(lat, lon) {
try {
const url = `https://nominatim.openstreetmap.org/reverse?format=json&lat=${lat}&lon=${lon}&zoom=10&addressdetails=0`;
const response = await fetch(url, {
headers: {
'User-Agent': 'VIGEOGUESSR/1.0 (https://vigeoguessr.com)'
}
});
if (!response.ok) {
return false;
}
const data = await response.json();
// Check if we got a valid response (not water or empty)
return data && data.display_name && !data.error;
} catch (error) {
console.warn('OSM validation failed:', error);
return true; // Assume valid if validation fails
}
}
/**
* Calculates straight-line distance between two geographic coordinates
* Uses OpenStreetMap for coordinate validation and Haversine formula for distance
* @param {number} lat1 - Latitude of first point
* @param {number} lon1 - Longitude of first point
* @param {number} lat2 - Latitude of second point
* @param {number} lon2 - Longitude of second point
* @returns {Promise<number>} Distance in meters, rounded to 1 decimal place
*/
export async function calculateDistance(lat1, lon1, lat2, lon2) {
if (lat1 === lat2 && lon1 === lon2) {
return 0;
}
dist = Math.acos(dist);
dist = dist * 180 / Math.PI;
dist = dist * 60 * 1.1515;
dist = dist * 1609.34; // Convert to meters
// Validate input coordinates
if (!isValidCoordinate(lat1, lon1) || !isValidCoordinate(lat2, lon2)) {
throw new Error('Invalid coordinates provided');
}
return parseFloat(dist.toFixed(1));
try {
// Validate coordinates with OpenStreetMap (parallel requests)
const [valid1, valid2] = await Promise.all([
validateCoordinatesWithOSM(lat1, lon1),
validateCoordinatesWithOSM(lat2, lon2)
]);
if (!valid1 || !valid2) {
console.warn('One or both coordinates may be invalid (water/empty area)');
}
} catch (error) {
console.warn('OSM validation failed, proceeding with calculation:', error);
}
// Calculate straight-line distance using Haversine formula
return calculateHaversineDistance(lat1, lon1, lat2, lon2);
}
/**
* Validates if coordinates are within valid ranges
* @param {number} lat - Latitude
* @param {number} lon - Longitude
* @returns {boolean} True if coordinates are valid
*/
function isValidCoordinate(lat, lon) {
return typeof lat === 'number' && typeof lon === 'number' &&
lat >= -90 && lat <= 90 &&
lon >= -180 && lon <= 180 &&
!isNaN(lat) && !isNaN(lon);
}
/**
-31
View File
@@ -1,31 +0,0 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./src/pages/**/*.{js,ts,jsx,tsx,mdx}',
'./src/components/**/*.{js,ts,jsx,tsx,mdx}',
'./src/app/**/*.{js,ts,jsx,tsx,mdx}',
],
theme: {
extend: {
colors: {
primary: {
DEFAULT: '#05A38C',
hover: '#048a76',
},
background: {
DEFAULT: '#1a1a1a',
secondary: '#24453D',
},
text: {
DEFAULT: '#e6f4e6',
secondary: '#ccc',
}
},
fontFamily: {
'jersey': ['Jersey 15', 'cursive'],
'courier': ['Courier', 'monospace'],
},
},
},
plugins: [],
}