working prototype
This commit is contained in:
45
README.md
45
README.md
@@ -1,36 +1,25 @@
|
|||||||
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
|
# Oldeneuf
|
||||||
|
|
||||||
## Getting Started
|
App for checking BankID ID card QR codes using the [Stø ID Card API](https://developer.idcard.stoe.no/).
|
||||||
|
|
||||||
First, run the development server:
|
## Development
|
||||||
|
|
||||||
|
[mise](https://mise.jdx.dev/) is used for tool management. Install Node.js and dependencies:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mise trust
|
||||||
|
mise install
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
Run the development server:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm run dev
|
npm run dev
|
||||||
# or
|
|
||||||
yarn dev
|
|
||||||
# or
|
|
||||||
pnpm dev
|
|
||||||
# or
|
|
||||||
bun dev
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
The camera stream is only accessible in a secure context. Test via a Cloudflare Tunnel:
|
||||||
|
|
||||||
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
|
```bash
|
||||||
|
npm run tunnel
|
||||||
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
|
```
|
||||||
|
|
||||||
## Learn More
|
|
||||||
|
|
||||||
To learn more about Next.js, take a look at the following resources:
|
|
||||||
|
|
||||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
|
||||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
|
||||||
|
|
||||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
|
|
||||||
|
|
||||||
## Deploy on Vercel
|
|
||||||
|
|
||||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
|
||||||
|
|
||||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
|
|
||||||
|
425
package-lock.json
generated
425
package-lock.json
generated
@@ -8,18 +8,20 @@
|
|||||||
"name": "oldeneuf",
|
"name": "oldeneuf",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"next": "15.3.3",
|
"@tanstack/react-query": "^5.84.2",
|
||||||
"react": "^19.0.0",
|
"next": "15.4.6",
|
||||||
"react-dom": "^19.0.0"
|
"qr-scanner": "^1.4.2",
|
||||||
|
"react": "^19.1.1",
|
||||||
|
"react-dom": "^19.1.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/eslintrc": "^3",
|
"@eslint/eslintrc": "^3",
|
||||||
"@tailwindcss/postcss": "^4",
|
"@tailwindcss/postcss": "^4",
|
||||||
"@types/node": "^20",
|
"@types/node": "^24",
|
||||||
"@types/react": "^19",
|
"@types/react": "^19",
|
||||||
"@types/react-dom": "^19",
|
"@types/react-dom": "^19",
|
||||||
"eslint": "^9",
|
"eslint": "^9",
|
||||||
"eslint-config-next": "15.3.3",
|
"eslint-config-next": "15.4.6",
|
||||||
"tailwindcss": "^4",
|
"tailwindcss": "^4",
|
||||||
"typescript": "^5"
|
"typescript": "^5"
|
||||||
}
|
}
|
||||||
@@ -64,9 +66,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@emnapi/runtime": {
|
"node_modules/@emnapi/runtime": {
|
||||||
"version": "1.4.3",
|
"version": "1.4.5",
|
||||||
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.5.tgz",
|
||||||
"integrity": "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==",
|
"integrity": "sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -305,9 +307,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@img/sharp-darwin-arm64": {
|
"node_modules/@img/sharp-darwin-arm64": {
|
||||||
"version": "0.34.2",
|
"version": "0.34.3",
|
||||||
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.2.tgz",
|
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.3.tgz",
|
||||||
"integrity": "sha512-OfXHZPppddivUJnqyKoi5YVeHRkkNE2zUFT2gbpKxp/JZCFYEYubnMg+gOp6lWfasPrTS+KPosKqdI+ELYVDtg==",
|
"integrity": "sha512-ryFMfvxxpQRsgZJqBd4wsttYQbCxsJksrv9Lw/v798JcQ8+w84mBWuXwl+TT0WJ/WrYOLaYpwQXi3sA9nTIaIg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -323,13 +325,13 @@
|
|||||||
"url": "https://opencollective.com/libvips"
|
"url": "https://opencollective.com/libvips"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@img/sharp-libvips-darwin-arm64": "1.1.0"
|
"@img/sharp-libvips-darwin-arm64": "1.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@img/sharp-darwin-x64": {
|
"node_modules/@img/sharp-darwin-x64": {
|
||||||
"version": "0.34.2",
|
"version": "0.34.3",
|
||||||
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.2.tgz",
|
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.3.tgz",
|
||||||
"integrity": "sha512-dYvWqmjU9VxqXmjEtjmvHnGqF8GrVjM2Epj9rJ6BUIXvk8slvNDJbhGFvIoXzkDhrJC2jUxNLz/GUjjvSzfw+g==",
|
"integrity": "sha512-yHpJYynROAj12TA6qil58hmPmAwxKKC7reUqtGLzsOHfP7/rniNGTL8tjWX6L3CTV4+5P4ypcS7Pp+7OB+8ihA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -345,13 +347,13 @@
|
|||||||
"url": "https://opencollective.com/libvips"
|
"url": "https://opencollective.com/libvips"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@img/sharp-libvips-darwin-x64": "1.1.0"
|
"@img/sharp-libvips-darwin-x64": "1.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@img/sharp-libvips-darwin-arm64": {
|
"node_modules/@img/sharp-libvips-darwin-arm64": {
|
||||||
"version": "1.1.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.0.tgz",
|
||||||
"integrity": "sha512-HZ/JUmPwrJSoM4DIQPv/BfNh9yrOA8tlBbqbLz4JZ5uew2+o22Ik+tHQJcih7QJuSa0zo5coHTfD5J8inqj9DA==",
|
"integrity": "sha512-sBZmpwmxqwlqG9ueWFXtockhsxefaV6O84BMOrhtg/YqbTaRdqDE7hxraVE3y6gVM4eExmfzW4a8el9ArLeEiQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -365,9 +367,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@img/sharp-libvips-darwin-x64": {
|
"node_modules/@img/sharp-libvips-darwin-x64": {
|
||||||
"version": "1.1.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.0.tgz",
|
||||||
"integrity": "sha512-Xzc2ToEmHN+hfvsl9wja0RlnXEgpKNmftriQp6XzY/RaSfwD9th+MSh0WQKzUreLKKINb3afirxW7A0fz2YWuQ==",
|
"integrity": "sha512-M64XVuL94OgiNHa5/m2YvEQI5q2cl9d/wk0qFTDVXcYzi43lxuiFTftMR1tOnFQovVXNZJ5TURSDK2pNe9Yzqg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -381,9 +383,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@img/sharp-libvips-linux-arm": {
|
"node_modules/@img/sharp-libvips-linux-arm": {
|
||||||
"version": "1.1.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.0.tgz",
|
||||||
"integrity": "sha512-s8BAd0lwUIvYCJyRdFqvsj+BJIpDBSxs6ivrOPm/R7piTs5UIwY5OjXrP2bqXC9/moGsyRa37eYWYCOGVXxVrA==",
|
"integrity": "sha512-mWd2uWvDtL/nvIzThLq3fr2nnGfyr/XMXlq8ZJ9WMR6PXijHlC3ksp0IpuhK6bougvQrchUAfzRLnbsen0Cqvw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@@ -397,9 +399,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@img/sharp-libvips-linux-arm64": {
|
"node_modules/@img/sharp-libvips-linux-arm64": {
|
||||||
"version": "1.1.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.0.tgz",
|
||||||
"integrity": "sha512-IVfGJa7gjChDET1dK9SekxFFdflarnUB8PwW8aGwEoF3oAsSDuNUTYS+SKDOyOJxQyDC1aPFMuRYLoDInyV9Ew==",
|
"integrity": "sha512-RXwd0CgG+uPRX5YYrkzKyalt2OJYRiJQ8ED/fi1tq9WQW2jsQIn0tqrlR5l5dr/rjqq6AHAxURhj2DVjyQWSOA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -413,9 +415,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@img/sharp-libvips-linux-ppc64": {
|
"node_modules/@img/sharp-libvips-linux-ppc64": {
|
||||||
"version": "1.1.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.0.tgz",
|
||||||
"integrity": "sha512-tiXxFZFbhnkWE2LA8oQj7KYR+bWBkiV2nilRldT7bqoEZ4HiDOcePr9wVDAZPi/Id5fT1oY9iGnDq20cwUz8lQ==",
|
"integrity": "sha512-Xod/7KaDDHkYu2phxxfeEPXfVXFKx70EAFZ0qyUdOjCcxbjqyJOEUpDe6RIyaunGxT34Anf9ue/wuWOqBW2WcQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
@@ -429,9 +431,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@img/sharp-libvips-linux-s390x": {
|
"node_modules/@img/sharp-libvips-linux-s390x": {
|
||||||
"version": "1.1.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.0.tgz",
|
||||||
"integrity": "sha512-xukSwvhguw7COyzvmjydRb3x/09+21HykyapcZchiCUkTThEQEOMtBj9UhkaBRLuBrgLFzQ2wbxdeCCJW/jgJA==",
|
"integrity": "sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"s390x"
|
"s390x"
|
||||||
],
|
],
|
||||||
@@ -445,9 +447,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@img/sharp-libvips-linux-x64": {
|
"node_modules/@img/sharp-libvips-linux-x64": {
|
||||||
"version": "1.1.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.0.tgz",
|
||||||
"integrity": "sha512-yRj2+reB8iMg9W5sULM3S74jVS7zqSzHG3Ol/twnAAkAhnGQnpjj6e4ayUz7V+FpKypwgs82xbRdYtchTTUB+Q==",
|
"integrity": "sha512-ZW3FPWIc7K1sH9E3nxIGB3y3dZkpJlMnkk7z5tu1nSkBoCgw2nSRTFHI5pB/3CQaJM0pdzMF3paf9ckKMSE9Tg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -461,9 +463,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@img/sharp-libvips-linuxmusl-arm64": {
|
"node_modules/@img/sharp-libvips-linuxmusl-arm64": {
|
||||||
"version": "1.1.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.0.tgz",
|
||||||
"integrity": "sha512-jYZdG+whg0MDK+q2COKbYidaqW/WTz0cc1E+tMAusiDygrM4ypmSCjOJPmFTvHHJ8j/6cAGyeDWZOsK06tP33w==",
|
"integrity": "sha512-UG+LqQJbf5VJ8NWJ5Z3tdIe/HXjuIdo4JeVNADXBFuG7z9zjoegpzzGIyV5zQKi4zaJjnAd2+g2nna8TZvuW9Q==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -477,9 +479,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@img/sharp-libvips-linuxmusl-x64": {
|
"node_modules/@img/sharp-libvips-linuxmusl-x64": {
|
||||||
"version": "1.1.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.0.tgz",
|
||||||
"integrity": "sha512-wK7SBdwrAiycjXdkPnGCPLjYb9lD4l6Ze2gSdAGVZrEL05AOUJESWU2lhlC+Ffn5/G+VKuSm6zzbQSzFX/P65A==",
|
"integrity": "sha512-SRYOLR7CXPgNze8akZwjoGBoN1ThNZoqpOgfnOxmWsklTGVfJiGJoC/Lod7aNMGA1jSsKWM1+HRX43OP6p9+6Q==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -493,9 +495,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@img/sharp-linux-arm": {
|
"node_modules/@img/sharp-linux-arm": {
|
||||||
"version": "0.34.2",
|
"version": "0.34.3",
|
||||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.2.tgz",
|
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.3.tgz",
|
||||||
"integrity": "sha512-0DZzkvuEOqQUP9mo2kjjKNok5AmnOr1jB2XYjkaoNRwpAYMDzRmAqUIa1nRi58S2WswqSfPOWLNOr0FDT3H5RQ==",
|
"integrity": "sha512-oBK9l+h6KBN0i3dC8rYntLiVfW8D8wH+NPNT3O/WBHeW0OQWCjfWksLUaPidsrDKpJgXp3G3/hkmhptAW0I3+A==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@@ -511,13 +513,13 @@
|
|||||||
"url": "https://opencollective.com/libvips"
|
"url": "https://opencollective.com/libvips"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@img/sharp-libvips-linux-arm": "1.1.0"
|
"@img/sharp-libvips-linux-arm": "1.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@img/sharp-linux-arm64": {
|
"node_modules/@img/sharp-linux-arm64": {
|
||||||
"version": "0.34.2",
|
"version": "0.34.3",
|
||||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.2.tgz",
|
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.3.tgz",
|
||||||
"integrity": "sha512-D8n8wgWmPDakc83LORcfJepdOSN6MvWNzzz2ux0MnIbOqdieRZwVYY32zxVx+IFUT8er5KPcyU3XXsn+GzG/0Q==",
|
"integrity": "sha512-QdrKe3EvQrqwkDrtuTIjI0bu6YEJHTgEeqdzI3uWJOH6G1O8Nl1iEeVYRGdj1h5I21CqxSvQp1Yv7xeU3ZewbA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -533,13 +535,35 @@
|
|||||||
"url": "https://opencollective.com/libvips"
|
"url": "https://opencollective.com/libvips"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@img/sharp-libvips-linux-arm64": "1.1.0"
|
"@img/sharp-libvips-linux-arm64": "1.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@img/sharp-linux-ppc64": {
|
||||||
|
"version": "0.34.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.3.tgz",
|
||||||
|
"integrity": "sha512-GLtbLQMCNC5nxuImPR2+RgrviwKwVql28FWZIW1zWruy6zLgA5/x2ZXk3mxj58X/tszVF69KK0Is83V8YgWhLA==",
|
||||||
|
"cpu": [
|
||||||
|
"ppc64"
|
||||||
|
],
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@img/sharp-libvips-linux-ppc64": "1.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@img/sharp-linux-s390x": {
|
"node_modules/@img/sharp-linux-s390x": {
|
||||||
"version": "0.34.2",
|
"version": "0.34.3",
|
||||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.2.tgz",
|
"resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.3.tgz",
|
||||||
"integrity": "sha512-EGZ1xwhBI7dNISwxjChqBGELCWMGDvmxZXKjQRuqMrakhO8QoMgqCrdjnAqJq/CScxfRn+Bb7suXBElKQpPDiw==",
|
"integrity": "sha512-3gahT+A6c4cdc2edhsLHmIOXMb17ltffJlxR0aC2VPZfwKoTGZec6u5GrFgdR7ciJSsHT27BD3TIuGcuRT0KmQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"s390x"
|
"s390x"
|
||||||
],
|
],
|
||||||
@@ -555,13 +579,13 @@
|
|||||||
"url": "https://opencollective.com/libvips"
|
"url": "https://opencollective.com/libvips"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@img/sharp-libvips-linux-s390x": "1.1.0"
|
"@img/sharp-libvips-linux-s390x": "1.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@img/sharp-linux-x64": {
|
"node_modules/@img/sharp-linux-x64": {
|
||||||
"version": "0.34.2",
|
"version": "0.34.3",
|
||||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.2.tgz",
|
"resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.3.tgz",
|
||||||
"integrity": "sha512-sD7J+h5nFLMMmOXYH4DD9UtSNBD05tWSSdWAcEyzqW8Cn5UxXvsHAxmxSesYUsTOBmUnjtxghKDl15EvfqLFbQ==",
|
"integrity": "sha512-8kYso8d806ypnSq3/Ly0QEw90V5ZoHh10yH0HnrzOCr6DKAPI6QVHvwleqMkVQ0m+fc7EH8ah0BB0QPuWY6zJQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -577,13 +601,13 @@
|
|||||||
"url": "https://opencollective.com/libvips"
|
"url": "https://opencollective.com/libvips"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@img/sharp-libvips-linux-x64": "1.1.0"
|
"@img/sharp-libvips-linux-x64": "1.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@img/sharp-linuxmusl-arm64": {
|
"node_modules/@img/sharp-linuxmusl-arm64": {
|
||||||
"version": "0.34.2",
|
"version": "0.34.3",
|
||||||
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.2.tgz",
|
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.3.tgz",
|
||||||
"integrity": "sha512-NEE2vQ6wcxYav1/A22OOxoSOGiKnNmDzCYFOZ949xFmrWZOVII1Bp3NqVVpvj+3UeHMFyN5eP/V5hzViQ5CZNA==",
|
"integrity": "sha512-vAjbHDlr4izEiXM1OTggpCcPg9tn4YriK5vAjowJsHwdBIdx0fYRsURkxLG2RLm9gyBq66gwtWI8Gx0/ov+JKQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -599,13 +623,13 @@
|
|||||||
"url": "https://opencollective.com/libvips"
|
"url": "https://opencollective.com/libvips"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@img/sharp-libvips-linuxmusl-arm64": "1.1.0"
|
"@img/sharp-libvips-linuxmusl-arm64": "1.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@img/sharp-linuxmusl-x64": {
|
"node_modules/@img/sharp-linuxmusl-x64": {
|
||||||
"version": "0.34.2",
|
"version": "0.34.3",
|
||||||
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.2.tgz",
|
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.3.tgz",
|
||||||
"integrity": "sha512-DOYMrDm5E6/8bm/yQLCWyuDJwUnlevR8xtF8bs+gjZ7cyUNYXiSf/E8Kp0Ss5xasIaXSHzb888V1BE4i1hFhAA==",
|
"integrity": "sha512-gCWUn9547K5bwvOn9l5XGAEjVTTRji4aPTqLzGXHvIr6bIDZKNTA34seMPgM0WmSf+RYBH411VavCejp3PkOeQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -621,20 +645,20 @@
|
|||||||
"url": "https://opencollective.com/libvips"
|
"url": "https://opencollective.com/libvips"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@img/sharp-libvips-linuxmusl-x64": "1.1.0"
|
"@img/sharp-libvips-linuxmusl-x64": "1.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@img/sharp-wasm32": {
|
"node_modules/@img/sharp-wasm32": {
|
||||||
"version": "0.34.2",
|
"version": "0.34.3",
|
||||||
"resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.2.tgz",
|
"resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.3.tgz",
|
||||||
"integrity": "sha512-/VI4mdlJ9zkaq53MbIG6rZY+QRN3MLbR6usYlgITEzi4Rpx5S6LFKsycOQjkOGmqTNmkIdLjEvooFKwww6OpdQ==",
|
"integrity": "sha512-+CyRcpagHMGteySaWos8IbnXcHgfDn7pO2fiC2slJxvNq9gDipYBN42/RagzctVRKgxATmfqOSulgZv5e1RdMg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"wasm32"
|
"wasm32"
|
||||||
],
|
],
|
||||||
"license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
|
"license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emnapi/runtime": "^1.4.3"
|
"@emnapi/runtime": "^1.4.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||||
@@ -644,9 +668,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@img/sharp-win32-arm64": {
|
"node_modules/@img/sharp-win32-arm64": {
|
||||||
"version": "0.34.2",
|
"version": "0.34.3",
|
||||||
"resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.2.tgz",
|
"resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.3.tgz",
|
||||||
"integrity": "sha512-cfP/r9FdS63VA5k0xiqaNaEoGxBg9k7uE+RQGzuK9fHt7jib4zAVVseR9LsE4gJcNWgT6APKMNnCcnyOtmSEUQ==",
|
"integrity": "sha512-MjnHPnbqMXNC2UgeLJtX4XqoVHHlZNd+nPt1kRPmj63wURegwBhZlApELdtxM2OIZDRv/DFtLcNhVbd1z8GYXQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -663,9 +687,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@img/sharp-win32-ia32": {
|
"node_modules/@img/sharp-win32-ia32": {
|
||||||
"version": "0.34.2",
|
"version": "0.34.3",
|
||||||
"resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.2.tgz",
|
"resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.3.tgz",
|
||||||
"integrity": "sha512-QLjGGvAbj0X/FXl8n1WbtQ6iVBpWU7JO94u/P2M4a8CFYsvQi4GW2mRy/JqkRx0qpBzaOdKJKw8uc930EX2AHw==",
|
"integrity": "sha512-xuCdhH44WxuXgOM714hn4amodJMZl3OEvf0GVTm0BEyMeA2to+8HEdRPShH0SLYptJY1uBw+SCFP9WVQi1Q/cw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ia32"
|
"ia32"
|
||||||
],
|
],
|
||||||
@@ -682,9 +706,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@img/sharp-win32-x64": {
|
"node_modules/@img/sharp-win32-x64": {
|
||||||
"version": "0.34.2",
|
"version": "0.34.3",
|
||||||
"resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.2.tgz",
|
"resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.3.tgz",
|
||||||
"integrity": "sha512-aUdT6zEYtDKCaxkofmmJDJYGCf0+pJg3eU9/oBuqvEeoB9dKI6ZLc/1iLJCTuJQDO4ptntAlkUmHgGjyuobZbw==",
|
"integrity": "sha512-OWwz05d++TxzLEv4VnsTz5CmZ6mI6S05sfQGEMrNrQcOEERbX46332IvE7pO/EUiw7jUrrS40z/M7kPyjfl04g==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -780,15 +804,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@next/env": {
|
"node_modules/@next/env": {
|
||||||
"version": "15.3.3",
|
"version": "15.4.6",
|
||||||
"resolved": "https://registry.npmjs.org/@next/env/-/env-15.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/@next/env/-/env-15.4.6.tgz",
|
||||||
"integrity": "sha512-OdiMrzCl2Xi0VTjiQQUK0Xh7bJHnOuET2s+3V+Y40WJBAXrJeGA3f+I8MZJ/YQ3mVGi5XGR1L66oFlgqXhQ4Vw==",
|
"integrity": "sha512-yHDKVTcHrZy/8TWhj0B23ylKv5ypocuCwey9ZqPyv4rPdUdRzpGCkSi03t04KBPyU96kxVtUqx6O3nE1kpxASQ==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@next/eslint-plugin-next": {
|
"node_modules/@next/eslint-plugin-next": {
|
||||||
"version": "15.3.3",
|
"version": "15.4.6",
|
||||||
"resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-15.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-15.4.6.tgz",
|
||||||
"integrity": "sha512-VKZJEiEdpKkfBmcokGjHu0vGDG+8CehGs90tBEy/IDoDDKGngeyIStt2MmE5FYNyU9BhgR7tybNWTAJY/30u+Q==",
|
"integrity": "sha512-2NOu3ln+BTcpnbIDuxx6MNq+pRrCyey4WSXGaJIyt0D2TYicHeO9QrUENNjcf673n3B1s7hsiV5xBYRCK1Q8kA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -796,9 +820,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@next/swc-darwin-arm64": {
|
"node_modules/@next/swc-darwin-arm64": {
|
||||||
"version": "15.3.3",
|
"version": "15.4.6",
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.4.6.tgz",
|
||||||
"integrity": "sha512-WRJERLuH+O3oYB4yZNVahSVFmtxRNjNF1I1c34tYMoJb0Pve+7/RaLAJJizyYiFhjYNGHRAE1Ri2Fd23zgDqhg==",
|
"integrity": "sha512-667R0RTP4DwxzmrqTs4Lr5dcEda9OxuZsVFsjVtxVMVhzSpo6nLclXejJVfQo2/g7/Z9qF3ETDmN3h65mTjpTQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -812,9 +836,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@next/swc-darwin-x64": {
|
"node_modules/@next/swc-darwin-x64": {
|
||||||
"version": "15.3.3",
|
"version": "15.4.6",
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.4.6.tgz",
|
||||||
"integrity": "sha512-XHdzH/yBc55lu78k/XwtuFR/ZXUTcflpRXcsu0nKmF45U96jt1tsOZhVrn5YH+paw66zOANpOnFQ9i6/j+UYvw==",
|
"integrity": "sha512-KMSFoistFkaiQYVQQnaU9MPWtp/3m0kn2Xed1Ces5ll+ag1+rlac20sxG+MqhH2qYWX1O2GFOATQXEyxKiIscg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -828,9 +852,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@next/swc-linux-arm64-gnu": {
|
"node_modules/@next/swc-linux-arm64-gnu": {
|
||||||
"version": "15.3.3",
|
"version": "15.4.6",
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.4.6.tgz",
|
||||||
"integrity": "sha512-VZ3sYL2LXB8znNGcjhocikEkag/8xiLgnvQts41tq6i+wql63SMS1Q6N8RVXHw5pEUjiof+II3HkDd7GFcgkzw==",
|
"integrity": "sha512-PnOx1YdO0W7m/HWFeYd2A6JtBO8O8Eb9h6nfJia2Dw1sRHoHpNf6lN1U4GKFRzRDBi9Nq2GrHk9PF3Vmwf7XVw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -844,9 +868,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@next/swc-linux-arm64-musl": {
|
"node_modules/@next/swc-linux-arm64-musl": {
|
||||||
"version": "15.3.3",
|
"version": "15.4.6",
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.4.6.tgz",
|
||||||
"integrity": "sha512-h6Y1fLU4RWAp1HPNJWDYBQ+e3G7sLckyBXhmH9ajn8l/RSMnhbuPBV/fXmy3muMcVwoJdHL+UtzRzs0nXOf9SA==",
|
"integrity": "sha512-XBbuQddtY1p5FGPc2naMO0kqs4YYtLYK/8aPausI5lyOjr4J77KTG9mtlU4P3NwkLI1+OjsPzKVvSJdMs3cFaw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -860,9 +884,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@next/swc-linux-x64-gnu": {
|
"node_modules/@next/swc-linux-x64-gnu": {
|
||||||
"version": "15.3.3",
|
"version": "15.4.6",
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.4.6.tgz",
|
||||||
"integrity": "sha512-jJ8HRiF3N8Zw6hGlytCj5BiHyG/K+fnTKVDEKvUCyiQ/0r5tgwO7OgaRiOjjRoIx2vwLR+Rz8hQoPrnmFbJdfw==",
|
"integrity": "sha512-+WTeK7Qdw82ez3U9JgD+igBAP75gqZ1vbK6R8PlEEuY0OIe5FuYXA4aTjL811kWPf7hNeslD4hHK2WoM9W0IgA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -876,9 +900,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@next/swc-linux-x64-musl": {
|
"node_modules/@next/swc-linux-x64-musl": {
|
||||||
"version": "15.3.3",
|
"version": "15.4.6",
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.4.6.tgz",
|
||||||
"integrity": "sha512-HrUcTr4N+RgiiGn3jjeT6Oo208UT/7BuTr7K0mdKRBtTbT4v9zJqCDKO97DUqqoBK1qyzP1RwvrWTvU6EPh/Cw==",
|
"integrity": "sha512-XP824mCbgQsK20jlXKrUpZoh/iO3vUWhMpxCz8oYeagoiZ4V0TQiKy0ASji1KK6IAe3DYGfj5RfKP6+L2020OQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -892,9 +916,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@next/swc-win32-arm64-msvc": {
|
"node_modules/@next/swc-win32-arm64-msvc": {
|
||||||
"version": "15.3.3",
|
"version": "15.4.6",
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.4.6.tgz",
|
||||||
"integrity": "sha512-SxorONgi6K7ZUysMtRF3mIeHC5aA3IQLmKFQzU0OuhuUYwpOBc1ypaLJLP5Bf3M9k53KUUUj4vTPwzGvl/NwlQ==",
|
"integrity": "sha512-FxrsenhUz0LbgRkNWx6FRRJIPe/MI1JRA4W4EPd5leXO00AZ6YU8v5vfx4MDXTvN77lM/EqsE3+6d2CIeF5NYg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -908,9 +932,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@next/swc-win32-x64-msvc": {
|
"node_modules/@next/swc-win32-x64-msvc": {
|
||||||
"version": "15.3.3",
|
"version": "15.4.6",
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.4.6.tgz",
|
||||||
"integrity": "sha512-4QZG6F8enl9/S2+yIiOiju0iCTFd93d8VC1q9LZS4p/Xuk81W2QDjCFeoogmrWWkAD59z8ZxepBQap2dKS5ruw==",
|
"integrity": "sha512-T4ufqnZ4u88ZheczkBTtOF+eKaM14V8kbjud/XrAakoM5DKQWjW09vD6B9fsdsWS2T7D5EY31hRHdta7QKWOng==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -985,12 +1009,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@swc/counter": {
|
|
||||||
"version": "0.1.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz",
|
|
||||||
"integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==",
|
|
||||||
"license": "Apache-2.0"
|
|
||||||
},
|
|
||||||
"node_modules/@swc/helpers": {
|
"node_modules/@swc/helpers": {
|
||||||
"version": "0.5.15",
|
"version": "0.5.15",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz",
|
||||||
@@ -1276,6 +1294,32 @@
|
|||||||
"tailwindcss": "4.1.10"
|
"tailwindcss": "4.1.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@tanstack/query-core": {
|
||||||
|
"version": "5.83.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.83.1.tgz",
|
||||||
|
"integrity": "sha512-OG69LQgT7jSp+5pPuCfzltq/+7l2xoweggjme9vlbCPa/d7D7zaqv5vN/S82SzSYZ4EDLTxNO1PWrv49RAS64Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/tannerlinsley"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tanstack/react-query": {
|
||||||
|
"version": "5.84.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.84.2.tgz",
|
||||||
|
"integrity": "sha512-cZadySzROlD2+o8zIfbD978p0IphuQzRWiiH3I2ugnTmz4jbjc0+TdibpwqxlzynEen8OulgAg+rzdNF37s7XQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@tanstack/query-core": "5.83.1"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/tannerlinsley"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^18 || ^19"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@tybys/wasm-util": {
|
"node_modules/@tybys/wasm-util": {
|
||||||
"version": "0.9.0",
|
"version": "0.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.9.0.tgz",
|
||||||
@@ -1309,15 +1353,21 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "20.19.0",
|
"version": "24.0.10",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.10.tgz",
|
||||||
"integrity": "sha512-hfrc+1tud1xcdVTABC2JiomZJEklMcXYNTVtZLAeqTVWD+qL5jkHKT+1lOtqDdGxt+mB53DTtiz673vfjU8D1Q==",
|
"integrity": "sha512-ENHwaH+JIRTDIEEbDK6QSQntAYGtbvdDXnMXnZaZ6k13Du1dPMmprkEHIL7ok2Wl2aZevetwTAb5S+7yIF+enA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"undici-types": "~6.21.0"
|
"undici-types": "~7.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/offscreencanvas": {
|
||||||
|
"version": "2019.7.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.7.3.tgz",
|
||||||
|
"integrity": "sha512-ieXiYmgSRXUDeOntE1InxjWyvEelZGP63M+cGuquuRLuIKKT1osnkXjxev9B7d1nXSug5vpunx+gNlbVxMlC9A==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@types/react": {
|
"node_modules/@types/react": {
|
||||||
"version": "19.1.8",
|
"version": "19.1.8",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.8.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.8.tgz",
|
||||||
@@ -2211,17 +2261,6 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/busboy": {
|
|
||||||
"version": "1.6.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
|
|
||||||
"integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
|
|
||||||
"dependencies": {
|
|
||||||
"streamsearch": "^1.1.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10.16.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/call-bind": {
|
"node_modules/call-bind": {
|
||||||
"version": "1.0.8",
|
"version": "1.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
|
||||||
@@ -2842,13 +2881,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint-config-next": {
|
"node_modules/eslint-config-next": {
|
||||||
"version": "15.3.3",
|
"version": "15.4.6",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-15.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-15.4.6.tgz",
|
||||||
"integrity": "sha512-QJLv/Ouk2vZnxL4b67njJwTLjTf7uZRltI0LL4GERYR4qMF5z08+gxkfODAeaK7TiC6o+cER91bDaEnwrTWV6Q==",
|
"integrity": "sha512-4uznvw5DlTTjrZgYZjMciSdDDMO2SWIuQgUNaFyC2O3Zw3Z91XeIejeVa439yRq2CnJb/KEvE4U2AeN/66FpUA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@next/eslint-plugin-next": "15.3.3",
|
"@next/eslint-plugin-next": "15.4.6",
|
||||||
"@rushstack/eslint-patch": "^1.10.3",
|
"@rushstack/eslint-patch": "^1.10.3",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0",
|
"@typescript-eslint/eslint-plugin": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0",
|
||||||
"@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0",
|
"@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0",
|
||||||
@@ -4668,15 +4707,13 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/next": {
|
"node_modules/next": {
|
||||||
"version": "15.3.3",
|
"version": "15.4.6",
|
||||||
"resolved": "https://registry.npmjs.org/next/-/next-15.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/next/-/next-15.4.6.tgz",
|
||||||
"integrity": "sha512-JqNj29hHNmCLtNvd090SyRbXJiivQ+58XjCcrC50Crb5g5u2zi7Y2YivbsEfzk6AtVI80akdOQbaMZwWB1Hthw==",
|
"integrity": "sha512-us++E/Q80/8+UekzB3SAGs71AlLDsadpFMXVNM/uQ0BMwsh9m3mr0UNQIfjKed8vpWXsASe+Qifrnu1oLIcKEQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@next/env": "15.3.3",
|
"@next/env": "15.4.6",
|
||||||
"@swc/counter": "0.1.3",
|
|
||||||
"@swc/helpers": "0.5.15",
|
"@swc/helpers": "0.5.15",
|
||||||
"busboy": "1.6.0",
|
|
||||||
"caniuse-lite": "^1.0.30001579",
|
"caniuse-lite": "^1.0.30001579",
|
||||||
"postcss": "8.4.31",
|
"postcss": "8.4.31",
|
||||||
"styled-jsx": "5.1.6"
|
"styled-jsx": "5.1.6"
|
||||||
@@ -4688,19 +4725,19 @@
|
|||||||
"node": "^18.18.0 || ^19.8.0 || >= 20.0.0"
|
"node": "^18.18.0 || ^19.8.0 || >= 20.0.0"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@next/swc-darwin-arm64": "15.3.3",
|
"@next/swc-darwin-arm64": "15.4.6",
|
||||||
"@next/swc-darwin-x64": "15.3.3",
|
"@next/swc-darwin-x64": "15.4.6",
|
||||||
"@next/swc-linux-arm64-gnu": "15.3.3",
|
"@next/swc-linux-arm64-gnu": "15.4.6",
|
||||||
"@next/swc-linux-arm64-musl": "15.3.3",
|
"@next/swc-linux-arm64-musl": "15.4.6",
|
||||||
"@next/swc-linux-x64-gnu": "15.3.3",
|
"@next/swc-linux-x64-gnu": "15.4.6",
|
||||||
"@next/swc-linux-x64-musl": "15.3.3",
|
"@next/swc-linux-x64-musl": "15.4.6",
|
||||||
"@next/swc-win32-arm64-msvc": "15.3.3",
|
"@next/swc-win32-arm64-msvc": "15.4.6",
|
||||||
"@next/swc-win32-x64-msvc": "15.3.3",
|
"@next/swc-win32-x64-msvc": "15.4.6",
|
||||||
"sharp": "^0.34.1"
|
"sharp": "^0.34.3"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@opentelemetry/api": "^1.1.0",
|
"@opentelemetry/api": "^1.1.0",
|
||||||
"@playwright/test": "^1.41.2",
|
"@playwright/test": "^1.51.1",
|
||||||
"babel-plugin-react-compiler": "*",
|
"babel-plugin-react-compiler": "*",
|
||||||
"react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0",
|
"react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0",
|
||||||
"react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0",
|
"react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0",
|
||||||
@@ -5070,6 +5107,15 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/qr-scanner": {
|
||||||
|
"version": "1.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/qr-scanner/-/qr-scanner-1.4.2.tgz",
|
||||||
|
"integrity": "sha512-kV1yQUe2FENvn59tMZW6mOVfpq9mGxGf8l6+EGaXUOd4RBOLg7tRC83OrirM5AtDvZRpdjdlXURsHreAOSPOUw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/offscreencanvas": "^2019.6.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/queue-microtask": {
|
"node_modules/queue-microtask": {
|
||||||
"version": "1.2.3",
|
"version": "1.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
||||||
@@ -5092,24 +5138,24 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/react": {
|
"node_modules/react": {
|
||||||
"version": "19.1.0",
|
"version": "19.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/react/-/react-19.1.1.tgz",
|
||||||
"integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==",
|
"integrity": "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-dom": {
|
"node_modules/react-dom": {
|
||||||
"version": "19.1.0",
|
"version": "19.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.1.tgz",
|
||||||
"integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==",
|
"integrity": "sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"scheduler": "^0.26.0"
|
"scheduler": "^0.26.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "^19.1.0"
|
"react": "^19.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-is": {
|
"node_modules/react-is": {
|
||||||
@@ -5363,9 +5409,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/sharp": {
|
"node_modules/sharp": {
|
||||||
"version": "0.34.2",
|
"version": "0.34.3",
|
||||||
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.2.tgz",
|
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.3.tgz",
|
||||||
"integrity": "sha512-lszvBmB9QURERtyKT2bNmsgxXK0ShJrL/fvqlonCo7e6xBF8nT8xU6pW+PMIbLsz0RxQk3rgH9kd8UmvOzlMJg==",
|
"integrity": "sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg==",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
@@ -5381,27 +5427,28 @@
|
|||||||
"url": "https://opencollective.com/libvips"
|
"url": "https://opencollective.com/libvips"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@img/sharp-darwin-arm64": "0.34.2",
|
"@img/sharp-darwin-arm64": "0.34.3",
|
||||||
"@img/sharp-darwin-x64": "0.34.2",
|
"@img/sharp-darwin-x64": "0.34.3",
|
||||||
"@img/sharp-libvips-darwin-arm64": "1.1.0",
|
"@img/sharp-libvips-darwin-arm64": "1.2.0",
|
||||||
"@img/sharp-libvips-darwin-x64": "1.1.0",
|
"@img/sharp-libvips-darwin-x64": "1.2.0",
|
||||||
"@img/sharp-libvips-linux-arm": "1.1.0",
|
"@img/sharp-libvips-linux-arm": "1.2.0",
|
||||||
"@img/sharp-libvips-linux-arm64": "1.1.0",
|
"@img/sharp-libvips-linux-arm64": "1.2.0",
|
||||||
"@img/sharp-libvips-linux-ppc64": "1.1.0",
|
"@img/sharp-libvips-linux-ppc64": "1.2.0",
|
||||||
"@img/sharp-libvips-linux-s390x": "1.1.0",
|
"@img/sharp-libvips-linux-s390x": "1.2.0",
|
||||||
"@img/sharp-libvips-linux-x64": "1.1.0",
|
"@img/sharp-libvips-linux-x64": "1.2.0",
|
||||||
"@img/sharp-libvips-linuxmusl-arm64": "1.1.0",
|
"@img/sharp-libvips-linuxmusl-arm64": "1.2.0",
|
||||||
"@img/sharp-libvips-linuxmusl-x64": "1.1.0",
|
"@img/sharp-libvips-linuxmusl-x64": "1.2.0",
|
||||||
"@img/sharp-linux-arm": "0.34.2",
|
"@img/sharp-linux-arm": "0.34.3",
|
||||||
"@img/sharp-linux-arm64": "0.34.2",
|
"@img/sharp-linux-arm64": "0.34.3",
|
||||||
"@img/sharp-linux-s390x": "0.34.2",
|
"@img/sharp-linux-ppc64": "0.34.3",
|
||||||
"@img/sharp-linux-x64": "0.34.2",
|
"@img/sharp-linux-s390x": "0.34.3",
|
||||||
"@img/sharp-linuxmusl-arm64": "0.34.2",
|
"@img/sharp-linux-x64": "0.34.3",
|
||||||
"@img/sharp-linuxmusl-x64": "0.34.2",
|
"@img/sharp-linuxmusl-arm64": "0.34.3",
|
||||||
"@img/sharp-wasm32": "0.34.2",
|
"@img/sharp-linuxmusl-x64": "0.34.3",
|
||||||
"@img/sharp-win32-arm64": "0.34.2",
|
"@img/sharp-wasm32": "0.34.3",
|
||||||
"@img/sharp-win32-ia32": "0.34.2",
|
"@img/sharp-win32-arm64": "0.34.3",
|
||||||
"@img/sharp-win32-x64": "0.34.2"
|
"@img/sharp-win32-ia32": "0.34.3",
|
||||||
|
"@img/sharp-win32-x64": "0.34.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/shebang-command": {
|
"node_modules/shebang-command": {
|
||||||
@@ -5543,14 +5590,6 @@
|
|||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/streamsearch": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
|
|
||||||
"integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/string.prototype.includes": {
|
"node_modules/string.prototype.includes": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz",
|
||||||
@@ -5986,9 +6025,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/undici-types": {
|
"node_modules/undici-types": {
|
||||||
"version": "6.21.0",
|
"version": "7.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz",
|
||||||
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
"integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
23
package.json
23
package.json
@@ -6,22 +6,25 @@
|
|||||||
"dev": "next dev --turbopack",
|
"dev": "next dev --turbopack",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "next lint"
|
"lint": "next lint",
|
||||||
|
"tunnel": "npx cloudflared tunnel --url http://localhost:3000"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"react": "^19.0.0",
|
"@tanstack/react-query": "^5.84.2",
|
||||||
"react-dom": "^19.0.0",
|
"next": "15.4.6",
|
||||||
"next": "15.3.3"
|
"qr-scanner": "^1.4.2",
|
||||||
|
"react": "^19.1.1",
|
||||||
|
"react-dom": "^19.1.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"typescript": "^5",
|
"@eslint/eslintrc": "^3",
|
||||||
"@types/node": "^20",
|
"@tailwindcss/postcss": "^4",
|
||||||
|
"@types/node": "^24",
|
||||||
"@types/react": "^19",
|
"@types/react": "^19",
|
||||||
"@types/react-dom": "^19",
|
"@types/react-dom": "^19",
|
||||||
"@tailwindcss/postcss": "^4",
|
|
||||||
"tailwindcss": "^4",
|
|
||||||
"eslint": "^9",
|
"eslint": "^9",
|
||||||
"eslint-config-next": "15.3.3",
|
"eslint-config-next": "15.4.6",
|
||||||
"@eslint/eslintrc": "^3"
|
"tailwindcss": "^4",
|
||||||
|
"typescript": "^5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
115
src/app/api/lookup/route.ts
Normal file
115
src/app/api/lookup/route.ts
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
|
|
||||||
|
type MerchantUserResponse = {
|
||||||
|
firstName: string;
|
||||||
|
lastName: string;
|
||||||
|
documentPhoto: string;
|
||||||
|
age: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ProblemDetails = {
|
||||||
|
type: string;
|
||||||
|
title: string;
|
||||||
|
detail: string;
|
||||||
|
status: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
async function fetchOidcToken(): Promise<string> {
|
||||||
|
const configUrl = process.env.BANKID_OIDC_CONFIG;
|
||||||
|
if (!configUrl) throw new Error("BANKID_OIDC_CONFIG not set");
|
||||||
|
|
||||||
|
// fetch OIDC config
|
||||||
|
const configRes = await fetch(configUrl);
|
||||||
|
if (!configRes.ok) throw new Error("Failed to fetch OIDC config");
|
||||||
|
const config = await configRes.json();
|
||||||
|
const tokenEndpoint = config.token_endpoint;
|
||||||
|
if (!tokenEndpoint) throw new Error("No token_endpoint in OIDC config");
|
||||||
|
|
||||||
|
// prepare client credentials
|
||||||
|
const clientId = process.env.BANKID_CLIENT_ID;
|
||||||
|
const clientSecret = process.env.BANKID_CLIENT_SECRET;
|
||||||
|
if (!clientId || !clientSecret) throw new Error("Client credentials not set");
|
||||||
|
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
params.append("grant_type", "client_credentials");
|
||||||
|
// there are other scopes, but we don't seem to have access to more than the age + picture
|
||||||
|
params.append("scope", "vis-leg/identity_picture_age");
|
||||||
|
params.append("client_id", clientId);
|
||||||
|
params.append("client_secret", clientSecret);
|
||||||
|
|
||||||
|
// request token
|
||||||
|
const tokenRes = await fetch(tokenEndpoint, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
|
},
|
||||||
|
body: params.toString(),
|
||||||
|
});
|
||||||
|
if (!tokenRes.ok) {
|
||||||
|
const errorText = await tokenRes.text();
|
||||||
|
console.error("Failed to fetch OIDC token. Response:", errorText);
|
||||||
|
throw new Error("Failed to fetch OIDC token");
|
||||||
|
}
|
||||||
|
const tokenData = await tokenRes.json();
|
||||||
|
if (!tokenData.access_token)
|
||||||
|
throw new Error("No access_token in token response");
|
||||||
|
console.log("Got a token from", tokenEndpoint);
|
||||||
|
return tokenData.access_token;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function POST(req: NextRequest) {
|
||||||
|
try {
|
||||||
|
const token = await fetchOidcToken();
|
||||||
|
|
||||||
|
const apiBase = process.env.ID_CARD_API_BASE_URL;
|
||||||
|
if (!apiBase) throw new Error("ID_CARD_API_BASE_URL not set");
|
||||||
|
const sessionUrl = apiBase + "/api/merchant/session";
|
||||||
|
|
||||||
|
// parse sessionId from request body, handle missing/invalid JSON
|
||||||
|
let body: any;
|
||||||
|
try {
|
||||||
|
body = await req.json();
|
||||||
|
} catch {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: "missing or invalid JSON body" },
|
||||||
|
{ status: 400 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { sessionId } = body || {};
|
||||||
|
if (!sessionId || typeof sessionId !== "string") {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: "missing or invalid sessionId" },
|
||||||
|
{ status: 400 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const payload = { sessionId };
|
||||||
|
console.log("sending payload:", payload);
|
||||||
|
const sessionRes = await fetch(sessionUrl, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${token}`,
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify(payload),
|
||||||
|
});
|
||||||
|
|
||||||
|
const status = sessionRes.status;
|
||||||
|
const sessionData = await sessionRes.json();
|
||||||
|
console.log("session response:", sessionData);
|
||||||
|
|
||||||
|
if (status === 200) {
|
||||||
|
return NextResponse.json(sessionData as MerchantUserResponse, { status });
|
||||||
|
} else if ([400, 401, 410, 500].includes(status)) {
|
||||||
|
return NextResponse.json(sessionData as ProblemDetails, { status });
|
||||||
|
} else {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: "Unexpected response from merchant session endpoint" },
|
||||||
|
{ status: 500 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (err: any) {
|
||||||
|
return NextResponse.json({ error: err.message }, { status: 500 });
|
||||||
|
}
|
||||||
|
}
|
@@ -13,8 +13,8 @@ const geistMono = Geist_Mono({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Create Next App",
|
title: "Oldeneuf",
|
||||||
description: "Generated by create next app",
|
description: "ID-sjekk via BankID",
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function RootLayout({
|
export default function RootLayout({
|
||||||
|
198
src/app/page.tsx
198
src/app/page.tsx
@@ -1,103 +1,111 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useState, useRef } from "react";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
import Scanner from "@/components/Scanner";
|
||||||
|
import {
|
||||||
|
useMutation,
|
||||||
|
QueryClient,
|
||||||
|
QueryClientProvider,
|
||||||
|
} from "@tanstack/react-query";
|
||||||
|
|
||||||
export default function Home() {
|
type SessionResponse = {
|
||||||
|
firstName: string;
|
||||||
|
lastName: string;
|
||||||
|
nnin: string;
|
||||||
|
documentPhoto: string;
|
||||||
|
age: number;
|
||||||
|
phoneNumber: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
function SessionInfo({ session }: { session: SessionResponse }) {
|
||||||
return (
|
return (
|
||||||
<div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
|
<div className="max-w-md mx-auto mt-6 p-6 rounded-xl shadow-lg border border-gray-200 bg-gradient-to-br from-gray-50 to-gray-200 space-y-4">
|
||||||
<main className="flex flex-col gap-[32px] row-start-2 items-center sm:items-start">
|
<div className="flex flex-col items-center">
|
||||||
<Image
|
<Image
|
||||||
className="dark:invert"
|
src={`data:image/jpeg;base64,${session.documentPhoto}`}
|
||||||
src="/next.svg"
|
alt="Document"
|
||||||
alt="Next.js logo"
|
width={200}
|
||||||
width={180}
|
height={261}
|
||||||
height={38}
|
className="w-40 h-50 object-cover rounded-lg mb-4 border border-gray-300 shadow"
|
||||||
priority
|
unoptimized
|
||||||
/>
|
/>
|
||||||
<ol className="list-inside list-decimal text-sm/6 text-center sm:text-left font-[family-name:var(--font-geist-mono)]">
|
{(session.firstName || session.lastName) && (
|
||||||
<li className="mb-2 tracking-[-.01em]">
|
<div className="text-xl font-bold text-gray-800">
|
||||||
Get started by editing{" "}
|
{[session.firstName, session.lastName].filter(Boolean).join(" ")}
|
||||||
<code className="bg-black/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded font-[family-name:var(--font-geist-mono)] font-semibold">
|
</div>
|
||||||
src/app/page.tsx
|
)}
|
||||||
</code>
|
{typeof session.age === "number" && (
|
||||||
.
|
<div className="text-3xl font-extrabold text-blue-700 mt-2">
|
||||||
</li>
|
{session.age} år
|
||||||
<li className="tracking-[-.01em]">
|
</div>
|
||||||
Save and see your changes instantly.
|
)}
|
||||||
</li>
|
</div>
|
||||||
</ol>
|
|
||||||
|
|
||||||
<div className="flex gap-4 items-center flex-col sm:flex-row">
|
|
||||||
<a
|
|
||||||
className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:w-auto"
|
|
||||||
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
<Image
|
|
||||||
className="dark:invert"
|
|
||||||
src="/vercel.svg"
|
|
||||||
alt="Vercel logomark"
|
|
||||||
width={20}
|
|
||||||
height={20}
|
|
||||||
/>
|
|
||||||
Deploy now
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 w-full sm:w-auto md:w-[158px]"
|
|
||||||
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
Read our docs
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
<footer className="row-start-3 flex gap-[24px] flex-wrap items-center justify-center">
|
|
||||||
<a
|
|
||||||
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
|
||||||
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
<Image
|
|
||||||
aria-hidden
|
|
||||||
src="/file.svg"
|
|
||||||
alt="File icon"
|
|
||||||
width={16}
|
|
||||||
height={16}
|
|
||||||
/>
|
|
||||||
Learn
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
|
||||||
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
<Image
|
|
||||||
aria-hidden
|
|
||||||
src="/window.svg"
|
|
||||||
alt="Window icon"
|
|
||||||
width={16}
|
|
||||||
height={16}
|
|
||||||
/>
|
|
||||||
Examples
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
|
||||||
href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
<Image
|
|
||||||
aria-hidden
|
|
||||||
src="/globe.svg"
|
|
||||||
alt="Globe icon"
|
|
||||||
width={16}
|
|
||||||
height={16}
|
|
||||||
/>
|
|
||||||
Go to nextjs.org →
|
|
||||||
</a>
|
|
||||||
</footer>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function lookupSession(sessionId: string) {
|
||||||
|
const res = await fetch("/api/lookup", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ sessionId }),
|
||||||
|
});
|
||||||
|
return res.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
function HomeInner() {
|
||||||
|
const [session, setSession] = useState<SessionResponse | null>(null);
|
||||||
|
const [scannedCodes, setScannedCodes] = useState<Set<string>>(new Set());
|
||||||
|
const pendingCodeRef = useRef<string | null>(null);
|
||||||
|
|
||||||
|
const mutation = useMutation({
|
||||||
|
mutationFn: lookupSession,
|
||||||
|
onSuccess: (data, variables) => {
|
||||||
|
console.log("Got lookup result:", data);
|
||||||
|
if (data && typeof data.age === "number") {
|
||||||
|
setSession(data);
|
||||||
|
setScannedCodes((prev) => new Set(prev).add(variables));
|
||||||
|
}
|
||||||
|
pendingCodeRef.current = null;
|
||||||
|
},
|
||||||
|
onError: () => {
|
||||||
|
pendingCodeRef.current = null;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleScan = (decodedText: string) => {
|
||||||
|
if (!decodedText.startsWith("VisLeg-")) {
|
||||||
|
window.alert("Ugyldig QR-kode");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
scannedCodes.has(decodedText) ||
|
||||||
|
mutation.isPending ||
|
||||||
|
pendingCodeRef.current === decodedText
|
||||||
|
) {
|
||||||
|
// Already scanned or lookup in progress for this code
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pendingCodeRef.current = decodedText;
|
||||||
|
mutation.mutate(decodedText);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Scanner onScan={handleScan} />
|
||||||
|
{session && <SessionInfo session={session} />}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Provide react-query context
|
||||||
|
const queryClient = new QueryClient();
|
||||||
|
|
||||||
|
export default function Home() {
|
||||||
|
return (
|
||||||
|
<QueryClientProvider client={queryClient}>
|
||||||
|
<HomeInner />
|
||||||
|
</QueryClientProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
308
src/components/Scanner.tsx
Normal file
308
src/components/Scanner.tsx
Normal file
@@ -0,0 +1,308 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useEffect, useRef, useState } from "react";
|
||||||
|
import QrScanner from "qr-scanner";
|
||||||
|
|
||||||
|
interface ScannerProps {
|
||||||
|
onScan: (decodedText: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Camera {
|
||||||
|
id: string;
|
||||||
|
label: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Scanner({ onScan }: ScannerProps) {
|
||||||
|
const videoRef = useRef<HTMLVideoElement>(null);
|
||||||
|
const scannerRef = useRef<QrScanner | null>(null);
|
||||||
|
const [isScanning, setIsScanning] = useState(false);
|
||||||
|
const [hasCamera, setHasCamera] = useState<boolean | null>(null);
|
||||||
|
const [cameras, setCameras] = useState<Camera[]>([]);
|
||||||
|
const [selectedCamera, setSelectedCamera] = useState("environment");
|
||||||
|
const [hasFlash, setHasFlash] = useState(false);
|
||||||
|
const [isFlashOn, setIsFlashOn] = useState(false);
|
||||||
|
const [error, setError] = useState<string>("");
|
||||||
|
const [highlightStyle, setHighlightStyle] = useState("default-style");
|
||||||
|
const [showScanRegion, setShowScanRegion] = useState(false);
|
||||||
|
|
||||||
|
const handleScanResult = (result: QrScanner.ScanResult) => {
|
||||||
|
onScan(result.data);
|
||||||
|
setError("");
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleScanError = (error: string | Error) => {
|
||||||
|
setError(error.toString());
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateFlashAvailability = async () => {
|
||||||
|
if (scannerRef.current) {
|
||||||
|
const flashAvailable = await scannerRef.current.hasFlash();
|
||||||
|
setHasFlash(flashAvailable);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const initializeScanner = async () => {
|
||||||
|
if (!videoRef.current) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
scannerRef.current = new QrScanner(videoRef.current, handleScanResult, {
|
||||||
|
onDecodeError: handleScanError,
|
||||||
|
highlightScanRegion: true,
|
||||||
|
highlightCodeOutline: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
await scannerRef.current.start();
|
||||||
|
setIsScanning(true);
|
||||||
|
await updateFlashAvailability();
|
||||||
|
|
||||||
|
// expects both light and dark QR codes
|
||||||
|
scannerRef.current.setInversionMode("both");
|
||||||
|
|
||||||
|
const availableCameras = await QrScanner.listCameras(true);
|
||||||
|
setCameras(availableCameras);
|
||||||
|
} catch (err) {
|
||||||
|
setError(`Failed to initialize scanner: ${err}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const startScanner = async () => {
|
||||||
|
if (scannerRef.current) {
|
||||||
|
try {
|
||||||
|
await scannerRef.current.start();
|
||||||
|
setIsScanning(true);
|
||||||
|
} catch (err) {
|
||||||
|
setError(`Failed to start scanner: ${err}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await initializeScanner();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const stopScanner = () => {
|
||||||
|
if (scannerRef.current) {
|
||||||
|
scannerRef.current.stop();
|
||||||
|
setIsScanning(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleFlash = async () => {
|
||||||
|
if (scannerRef.current && hasFlash) {
|
||||||
|
try {
|
||||||
|
await scannerRef.current.toggleFlash();
|
||||||
|
setIsFlashOn(scannerRef.current.isFlashOn());
|
||||||
|
} catch (err) {
|
||||||
|
setError(`Failed to toggle flash: ${err}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCameraChange = async (cameraId: string) => {
|
||||||
|
if (scannerRef.current) {
|
||||||
|
try {
|
||||||
|
await scannerRef.current.setCamera(cameraId);
|
||||||
|
setSelectedCamera(cameraId);
|
||||||
|
await updateFlashAvailability();
|
||||||
|
} catch (err) {
|
||||||
|
setError(`Failed to change camera: ${err}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
QrScanner.hasCamera().then(setHasCamera);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (scannerRef.current) {
|
||||||
|
scannerRef.current.destroy();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (scannerRef.current && scannerRef.current.$canvas) {
|
||||||
|
scannerRef.current.$canvas.style.display = showScanRegion
|
||||||
|
? "block"
|
||||||
|
: "none";
|
||||||
|
}
|
||||||
|
}, [showScanRegion]);
|
||||||
|
|
||||||
|
const getVideoContainerClass = () => {
|
||||||
|
// on mobile portrait, fill top 50% of screen, no margin top/left/right
|
||||||
|
const baseClass = "relative w-full max-w-md mx-auto";
|
||||||
|
return `${baseClass} video-container-responsive`;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-full max-w-2xl mx-auto">
|
||||||
|
<div className={getVideoContainerClass()}>
|
||||||
|
<video
|
||||||
|
ref={videoRef}
|
||||||
|
className="w-full h-auto rounded-lg border-2 border-gray-300"
|
||||||
|
playsInline
|
||||||
|
muted
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="p-4 space-y-4">
|
||||||
|
<div className="text-center">
|
||||||
|
{/* Video Container */}
|
||||||
|
|
||||||
|
{/* Controls */}
|
||||||
|
<div className="mt-4 space-y-4">
|
||||||
|
{/* Start/Stop Toggle Button */}
|
||||||
|
<div className="flex gap-2 justify-center">
|
||||||
|
<button
|
||||||
|
onClick={isScanning ? stopScanner : startScanner}
|
||||||
|
className={`px-4 py-2 text-white rounded-lg transition-colors ${
|
||||||
|
isScanning
|
||||||
|
? "bg-red-500 hover:bg-red-600"
|
||||||
|
: "bg-green-500 hover:bg-green-600"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{isScanning ? "Stop Scanner" : "Start Scanner"}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{/* Flash Toggle */}
|
||||||
|
{/* {hasFlash && (
|
||||||
|
<button
|
||||||
|
onClick={toggleFlash}
|
||||||
|
className="px-4 py-2 bg-yellow-500 text-white rounded-lg hover:bg-yellow-600 transition-colors"
|
||||||
|
>
|
||||||
|
📸 Flash: {isFlashOn ? "on" : "off"}
|
||||||
|
</button>
|
||||||
|
)} */}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{ display: "none" }}>
|
||||||
|
{/* Settings */}
|
||||||
|
<div className="mt-6 space-y-4 text-left">
|
||||||
|
{/* Camera Selection */}
|
||||||
|
{cameras.length > 0 && (
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||||
|
Camera:
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
value={selectedCamera}
|
||||||
|
onChange={(e) => handleCameraChange(e.target.value)}
|
||||||
|
className="w-full p-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||||
|
>
|
||||||
|
<option value="environment">
|
||||||
|
Environment Facing (Back Camera)
|
||||||
|
</option>
|
||||||
|
<option value="user">User Facing (Front Camera)</option>
|
||||||
|
{cameras.map((camera) => (
|
||||||
|
<option key={camera.id} value={camera.id}>
|
||||||
|
{camera.label}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Highlight Style */}
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||||
|
Highlight Style:
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
value={highlightStyle}
|
||||||
|
onChange={(e) => setHighlightStyle(e.target.value)}
|
||||||
|
className="w-full p-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||||
|
>
|
||||||
|
<option value="default-style">Default style</option>
|
||||||
|
<option value="example-style-1">Custom style 1</option>
|
||||||
|
<option value="example-style-2">Custom style 2</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Show Scan Region */}
|
||||||
|
<div className="flex items-center">
|
||||||
|
<input
|
||||||
|
id="show-scan-region"
|
||||||
|
type="checkbox"
|
||||||
|
checked={showScanRegion}
|
||||||
|
onChange={(e) => setShowScanRegion(e.target.checked)}
|
||||||
|
className="mr-2 h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded"
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
htmlFor="show-scan-region"
|
||||||
|
className="text-sm font-medium text-gray-700"
|
||||||
|
>
|
||||||
|
Show scan region canvas
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Status Information */}
|
||||||
|
<div className="mt-6 space-y-2 text-sm text-gray-600">
|
||||||
|
<div>
|
||||||
|
<span className="font-medium">Device has camera:</span>{" "}
|
||||||
|
{hasCamera !== null
|
||||||
|
? hasCamera
|
||||||
|
? "Yes"
|
||||||
|
: "No"
|
||||||
|
: "Checking..."}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span className="font-medium">Camera has flash:</span>{" "}
|
||||||
|
{hasFlash ? "Yes" : "No"}
|
||||||
|
</div>
|
||||||
|
{error && (
|
||||||
|
<div className="text-red-600 font-medium">
|
||||||
|
<span className="font-medium">Error:</span> {error}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<style jsx>{`
|
||||||
|
/* Responsive video container for mobile portrait */
|
||||||
|
@media (max-width: 768px) and (orientation: portrait) {
|
||||||
|
.video-container-responsive {
|
||||||
|
width: 100vw !important;
|
||||||
|
max-width: 100vw !important;
|
||||||
|
margin-left: 0 !important;
|
||||||
|
margin-right: 0 !important;
|
||||||
|
margin-top: 0 !important;
|
||||||
|
height: 50vh !important;
|
||||||
|
background: #000;
|
||||||
|
border-radius: 0 !important;
|
||||||
|
display: flex;
|
||||||
|
align-items: stretch;
|
||||||
|
justify-content: stretch;
|
||||||
|
}
|
||||||
|
.video-container-responsive video {
|
||||||
|
width: 100vw !important;
|
||||||
|
height: 50vh !important;
|
||||||
|
object-fit: cover;
|
||||||
|
border-radius: 0 !important;
|
||||||
|
border: none !important;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.scanner-style-1 .scan-region-highlight-svg),
|
||||||
|
:global(.scanner-style-1 .code-outline-highlight) {
|
||||||
|
stroke: #64a2f3 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.scanner-style-2 .scan-region-highlight) {
|
||||||
|
border-radius: 30px;
|
||||||
|
outline: rgba(0, 0, 0, 0.25) solid 50vmax;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.scanner-style-2 .scan-region-highlight-svg) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.scanner-style-2 .code-outline-highlight) {
|
||||||
|
stroke: rgba(255, 255, 255, 0.5) !important;
|
||||||
|
stroke-width: 15 !important;
|
||||||
|
stroke-dasharray: none !important;
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
Reference in New Issue
Block a user