feat: integrate thermal printer functionality with receipt encoding
- Added @point-of-sale/receipt-printer-encoder dependency to package.json. - Implemented useThermalPrinter hook for managing Bluetooth and USB printer connections. - Created encodeOrderReceipt function to format receipt data for printing. - Enhanced TransactionIndex component to include printer connection options and receipt printing functionality. - Added dropdown menu for selecting printer connection type (Bluetooth/USB) and printing receipt options (58mm/80mm). - Updated TransactionCardRow to include print receipt actions. - Defined TypeScript types for receipt printer encoder and web Bluetooth/USB APIs. - Configured Vite to optimize dependencies for receipt printer encoder.
This commit is contained in:
parent
add157183b
commit
f7a91739f9
@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use App\Settings\SystemSettings;
|
||||
use Illuminate\Http\Request;
|
||||
use Inertia\Middleware;
|
||||
|
||||
@ -38,6 +39,7 @@ public function share(Request $request): array
|
||||
return [
|
||||
...parent::share($request),
|
||||
'name' => config('app.name'),
|
||||
'address' => app(SystemSettings::class)->address ?? '',
|
||||
'auth' => [
|
||||
'user' => $request->user()
|
||||
? tap($request->user()->load('userProfile', 'roles'), function ($user) {
|
||||
|
||||
829
package-lock.json
generated
829
package-lock.json
generated
@ -13,6 +13,7 @@
|
||||
"@inertiajs/react": "^3.0.0",
|
||||
"@inertiajs/vite": "^3.0.0",
|
||||
"@laravel/passkeys": "^0.2.0",
|
||||
"@point-of-sale/receipt-printer-encoder": "^3.0.3",
|
||||
"@radix-ui/react-avatar": "^1.1.3",
|
||||
"@radix-ui/react-checkbox": "^1.1.4",
|
||||
"@radix-ui/react-collapsible": "^1.1.3",
|
||||
@ -1779,6 +1780,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@canvas/image-data": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@canvas/image-data/-/image-data-1.1.0.tgz",
|
||||
"integrity": "sha512-QdObRRjRbcXGmM1tmJ+MrHcaz1MftF2+W7YI+MsphnsCrmtyfS0d5qJbk0MeSbUeyM/jCb0hmnkXPsy026L7dA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@date-fns/tz": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@date-fns/tz/-/tz-1.5.0.tgz",
|
||||
@ -2043,37 +2050,6 @@
|
||||
"integrity": "sha512-VYJy0uhFm9zTJ1TxBaW/pA8bjbOM/OttaNMwZ1RHG4JKyRG7DhSdiqD1ipQoAyoD22olUtxbP78W9xY3Wd11bg==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@emnapi/core": {
|
||||
"version": "2.0.0-alpha.3",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-2.0.0-alpha.3.tgz",
|
||||
"integrity": "sha512-AZypUeJ/yByuxyS7BlSNRDOMLMlROYtjYdIAuBmJssVz1UJDSeYxLrdizhXCFYhedC5bqd/ASy8EuNXbVVXp9g==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@emnapi/wasi-threads": "2.0.1",
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emnapi/runtime": {
|
||||
"version": "2.0.0-alpha.3",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-2.0.0-alpha.3.tgz",
|
||||
"integrity": "sha512-hFPAhMUjJD9BSyCANEISPOogeXC9Zo9ZQl7L6vKnaVsMkCtzznaW/naYypeyl0Gv5rYfWYsZbpixTMpjDJzQeA==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emnapi/wasi-threads": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-2.0.1.tgz",
|
||||
"integrity": "sha512-9DsSk+o5NBX0CCJT8s0EROGSGxjR/tKu6aBTaVyq+SjAEQH4XcdcRxPBRzsBLizTTJ49MJjF+jgu3qnO9GLQcQ==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint-community/eslint-utils": {
|
||||
"version": "4.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz",
|
||||
@ -2583,27 +2559,6 @@
|
||||
"node": "^22.20 || ^24.12 || >=25"
|
||||
}
|
||||
},
|
||||
"node_modules/@napi-rs/wasm-runtime": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.0.tgz",
|
||||
"integrity": "sha512-kDoONqMa+VnZ4vvvu/ZUurpJ4gkZU57e7g69qpNgWhYcZFPUHZM2CEMKm+cG6ufDVALbjMvfmMjFVqaK7uEMnA==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@tybys/wasm-util": "^0.10.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^20.19.0 || ^22.13.0 || >=23.5.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/Brooooooklyn"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@emnapi/core": "^2.0.0-alpha.3",
|
||||
"@emnapi/runtime": "^2.0.0-alpha.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@nodelib/fs.scandir": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||
@ -2648,6 +2603,25 @@
|
||||
"url": "https://github.com/sponsors/Boshen"
|
||||
}
|
||||
},
|
||||
"node_modules/@point-of-sale/codepage-encoder": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@point-of-sale/codepage-encoder/-/codepage-encoder-3.0.2.tgz",
|
||||
"integrity": "sha512-Sx+0sE/XgPtMUNKScHpwTIZiHNd/+Mg0udACQJVwPsxH9kUAgIkRi9kRlmWBMCruwOJadjKLyKs8MO8Bh+VS/A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@point-of-sale/receipt-printer-encoder": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@point-of-sale/receipt-printer-encoder/-/receipt-printer-encoder-3.0.3.tgz",
|
||||
"integrity": "sha512-2+xgs6rwNfrkEw9g4LkgQS6k29qnmo3+hOV7Z2xHNBFn8h4FrwquZwL/dBOTwgmQ9sWEYBkj/bQkg9Rz6lL99w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@canvas/image-data": "^1.0.0",
|
||||
"@point-of-sale/codepage-encoder": "^3.0.0",
|
||||
"canvas-dither": "^1.0.1",
|
||||
"canvas-flatten": "^1.0.1",
|
||||
"resize-image-data": "^0.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/number": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.3.tgz",
|
||||
@ -4170,150 +4144,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-android-arm64": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.5.tgz",
|
||||
"integrity": "sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-darwin-arm64": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.5.tgz",
|
||||
"integrity": "sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-darwin-x64": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.5.tgz",
|
||||
"integrity": "sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-freebsd-x64": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.5.tgz",
|
||||
"integrity": "sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-arm-gnueabihf": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.5.tgz",
|
||||
"integrity": "sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-arm64-gnu": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.5.tgz",
|
||||
"integrity": "sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-arm64-musl": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.5.tgz",
|
||||
"integrity": "sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-ppc64-gnu": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.5.tgz",
|
||||
"integrity": "sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-s390x-gnu": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.5.tgz",
|
||||
"integrity": "sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-x64-gnu": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.5.tgz",
|
||||
@ -4346,103 +4176,6 @@
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-openharmony-arm64": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.5.tgz",
|
||||
"integrity": "sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openharmony"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-wasm32-wasi": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.5.tgz",
|
||||
"integrity": "sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA==",
|
||||
"cpu": [
|
||||
"wasm32"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@emnapi/core": "1.11.1",
|
||||
"@emnapi/runtime": "1.11.1",
|
||||
"@napi-rs/wasm-runtime": "^1.1.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-wasm32-wasi/node_modules/@emnapi/core": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz",
|
||||
"integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@emnapi/wasi-threads": "1.2.2",
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-wasm32-wasi/node_modules/@emnapi/runtime": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz",
|
||||
"integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-wasm32-wasi/node_modules/@emnapi/wasi-threads": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz",
|
||||
"integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-win32-arm64-msvc": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.5.tgz",
|
||||
"integrity": "sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-win32-x64-msvc": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.5.tgz",
|
||||
"integrity": "sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/pluginutils": {
|
||||
"version": "1.0.0-rc.3",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.3.tgz",
|
||||
@ -5041,118 +4774,6 @@
|
||||
"@tailwindcss/oxide-win32-x64-msvc": "4.3.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-android-arm64": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.3.tgz",
|
||||
"integrity": "sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-darwin-arm64": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.3.tgz",
|
||||
"integrity": "sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-darwin-x64": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.3.tgz",
|
||||
"integrity": "sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-freebsd-x64": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.3.tgz",
|
||||
"integrity": "sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.3.tgz",
|
||||
"integrity": "sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-linux-arm64-gnu": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.3.tgz",
|
||||
"integrity": "sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-linux-arm64-musl": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.3.tgz",
|
||||
"integrity": "sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-linux-x64-gnu": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.3.tgz",
|
||||
@ -5185,51 +4806,6 @@
|
||||
"node": ">= 20"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-wasm32-wasi": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.3.tgz",
|
||||
"integrity": "sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ==",
|
||||
"bundleDependencies": [
|
||||
"@napi-rs/wasm-runtime",
|
||||
"@emnapi/core",
|
||||
"@emnapi/runtime",
|
||||
"@tybys/wasm-util",
|
||||
"@emnapi/wasi-threads",
|
||||
"tslib"
|
||||
],
|
||||
"cpu": [
|
||||
"wasm32"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@emnapi/core": "^1.11.1",
|
||||
"@emnapi/runtime": "^1.11.1",
|
||||
"@emnapi/wasi-threads": "^1.2.2",
|
||||
"@napi-rs/wasm-runtime": "^1.1.4",
|
||||
"@tybys/wasm-util": "^0.10.2",
|
||||
"tslib": "^2.8.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.3.tgz",
|
||||
"integrity": "sha512-3rc292Ca2ceK6Ulcc/bAVnTs/3nDtoPhyEKlgPv+yQJQi/JS/AMJlqzxvlDacL1nekbrcf6bTqp/jV4qgnPxNQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-win32-x64-msvc": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.3.tgz",
|
||||
@ -5356,16 +4932,6 @@
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/@tybys/wasm-util": {
|
||||
"version": "0.10.3",
|
||||
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz",
|
||||
"integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/babel__core": {
|
||||
"version": "7.20.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
|
||||
@ -5854,216 +5420,6 @@
|
||||
"url": "https://opencollective.com/eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/@unrs/resolver-binding-android-arm-eabi": {
|
||||
"version": "1.12.2",
|
||||
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.12.2.tgz",
|
||||
"integrity": "sha512-g5T90pqg1bo/7mytQx6F4iBNC0Wsh9cu+z9veDbFjc7HjpesJFWD7QMS0NGStXM075+7dJPPVvBbpZlnrdpi/w==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
]
|
||||
},
|
||||
"node_modules/@unrs/resolver-binding-android-arm64": {
|
||||
"version": "1.12.2",
|
||||
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.12.2.tgz",
|
||||
"integrity": "sha512-YGCRZv/9GLhwmz6mYDeTsm/92BAyR28l6c2ReweVW5pWgfsitWLY8upvfRlGdoyD8HjeTHSYJWyZGD4KJA/nFQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
]
|
||||
},
|
||||
"node_modules/@unrs/resolver-binding-darwin-arm64": {
|
||||
"version": "1.12.2",
|
||||
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.12.2.tgz",
|
||||
"integrity": "sha512-u9DiNT1auQMO20A9SyTuG3wUgQWB9Z7KjAg0uFuCDR1FsAY8A0CG2S6JpHS1xwm/w1G08bjXZDcyOCjv1WAm2w==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
]
|
||||
},
|
||||
"node_modules/@unrs/resolver-binding-darwin-x64": {
|
||||
"version": "1.12.2",
|
||||
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.12.2.tgz",
|
||||
"integrity": "sha512-f7rPLi/T1HVKZu/u6t87lroib16n8vrSzcyxI7lg4BGO9UF26KhQL44sd9eOUgrTYhvRXtWOIZT5PejdPyJfUA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
]
|
||||
},
|
||||
"node_modules/@unrs/resolver-binding-freebsd-x64": {
|
||||
"version": "1.12.2",
|
||||
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.12.2.tgz",
|
||||
"integrity": "sha512-BpcOjWCJub6nRZUS2zA20pmLvjtqAtGejETaIyRLiZiQf++cbrjltLA5NN/xaXfqeOBOSlMFbemIl5/S5tljmg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
]
|
||||
},
|
||||
"node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": {
|
||||
"version": "1.12.2",
|
||||
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.12.2.tgz",
|
||||
"integrity": "sha512-vZTDvdSISZjJx66OzJqtsOhzifbqRjbmI1Mnu49fQDwog5GtDI4QidRiEAYbZCRj9C8YZEW+3ZjqsyS9GR4k2A==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@unrs/resolver-binding-linux-arm-musleabihf": {
|
||||
"version": "1.12.2",
|
||||
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.12.2.tgz",
|
||||
"integrity": "sha512-BiPI+IrIlwcW4nLLMM21+B1dFPzd55yAVgVGrdgDjNef+ch03GdxrcyaIz8X9SsQirh/kCQ7mviyWlMxdh2D7g==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@unrs/resolver-binding-linux-arm64-gnu": {
|
||||
"version": "1.12.2",
|
||||
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.12.2.tgz",
|
||||
"integrity": "sha512-zJc0H99FEPoFfSrNpa91HYfxzfAJCr502oxNK1cfdC9hlaFI43RT+JFCann9JUgZmLzzntChHyn13Sgn9ljHNg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@unrs/resolver-binding-linux-arm64-musl": {
|
||||
"version": "1.12.2",
|
||||
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.12.2.tgz",
|
||||
"integrity": "sha512-KQ3Lki6l+Pz1k/eBipN41ES+YUK30beLGb9YqcB1O542cyLCNE6GaxrfcY3T6EezmGGk84wb5XyO9loTM9tkcA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@unrs/resolver-binding-linux-loong64-gnu": {
|
||||
"version": "1.12.2",
|
||||
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-loong64-gnu/-/resolver-binding-linux-loong64-gnu-1.12.2.tgz",
|
||||
"integrity": "sha512-3SJGEh1DborhG6pyxvhPzCT4bbSIVihsvgJc13P1bHG7KLdNDaF9T3gsTwFc7Jw/5Y5/iWOjkEx7Zy0NvCGX3Q==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@unrs/resolver-binding-linux-loong64-musl": {
|
||||
"version": "1.12.2",
|
||||
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-loong64-musl/-/resolver-binding-linux-loong64-musl-1.12.2.tgz",
|
||||
"integrity": "sha512-jiuG/Obbel7uw1PwHNFfrkiKhLAF6mnyZ6aWlOAVN9WqKm8v0OFGnciJIHu8+CMvXLQ8AD51LPzAoUfT21D5Ew==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@unrs/resolver-binding-linux-ppc64-gnu": {
|
||||
"version": "1.12.2",
|
||||
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.12.2.tgz",
|
||||
"integrity": "sha512-q7xRvVpmcfeL+LlZg8Pbbo6QaTZwDU5BaGZbwfhkEsXJn3Was8xYfE0RBH266xZt0rM6B7i8xAYIvjthuUIWHg==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@unrs/resolver-binding-linux-riscv64-gnu": {
|
||||
"version": "1.12.2",
|
||||
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.12.2.tgz",
|
||||
"integrity": "sha512-0CVdx6lcnT3Q9inOH8tsMIOJ6ImndllMjqJHg8RLVdB7Vq4SfkEXl9mCSsVNuNA4MCYycRicCUxPCabVHJRr6A==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@unrs/resolver-binding-linux-riscv64-musl": {
|
||||
"version": "1.12.2",
|
||||
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.12.2.tgz",
|
||||
"integrity": "sha512-iOwlRo9vnp6R6ohHQS11n0NnfdXx/omhkocmIfaPRpQhKZ+3BDMkkdRVh53qjkFkpPddf+FETA28NwGN7l5l+w==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@unrs/resolver-binding-linux-s390x-gnu": {
|
||||
"version": "1.12.2",
|
||||
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.12.2.tgz",
|
||||
"integrity": "sha512-HYJtLfXq94q8iZNFT1lknx258wlkkWhZeUXJRqzKBBUJ00CvZ+N33zgbCqimLjsyw5Va6uUxhVa12mI+kaveEw==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@unrs/resolver-binding-linux-x64-gnu": {
|
||||
"version": "1.12.2",
|
||||
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.12.2.tgz",
|
||||
@ -6092,115 +5448,6 @@
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@unrs/resolver-binding-openharmony-arm64": {
|
||||
"version": "1.12.2",
|
||||
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-openharmony-arm64/-/resolver-binding-openharmony-arm64-1.12.2.tgz",
|
||||
"integrity": "sha512-YZ9hP4O0X9PQb8eO980qmLNGH4zT3I9+SZTdt0Pr0YyuGQhYKoOZkV02VzrzyOZJ5xIJ3UFIenKkUkGg8GjgWQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openharmony"
|
||||
]
|
||||
},
|
||||
"node_modules/@unrs/resolver-binding-wasm32-wasi": {
|
||||
"version": "1.12.2",
|
||||
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.12.2.tgz",
|
||||
"integrity": "sha512-tYFDIkMxSflfEc/h92ZWNsZlHSwgimbNHSO3PL2JWQHfCuC2q316jMyYU9TIWZsFK2bQwyK5VAdYgn8ygPj69A==",
|
||||
"cpu": [
|
||||
"wasm32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@emnapi/core": "1.10.0",
|
||||
"@emnapi/runtime": "1.10.0",
|
||||
"@napi-rs/wasm-runtime": "^1.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@unrs/resolver-binding-wasm32-wasi/node_modules/@emnapi/core": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz",
|
||||
"integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@emnapi/wasi-threads": "1.2.1",
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@unrs/resolver-binding-wasm32-wasi/node_modules/@emnapi/runtime": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz",
|
||||
"integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@unrs/resolver-binding-wasm32-wasi/node_modules/@emnapi/wasi-threads": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz",
|
||||
"integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@unrs/resolver-binding-win32-arm64-msvc": {
|
||||
"version": "1.12.2",
|
||||
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.12.2.tgz",
|
||||
"integrity": "sha512-qzNyg3xL0VPQmCaUh+N5jSitce6k+uCBfMDesWRnlULOZaqUkaJ0ybdT+UqlAWJoQjuqfIU/0Ptx9bteN4D82g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
]
|
||||
},
|
||||
"node_modules/@unrs/resolver-binding-win32-ia32-msvc": {
|
||||
"version": "1.12.2",
|
||||
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.12.2.tgz",
|
||||
"integrity": "sha512-WD9sY00OfpHVGfsnHZoA8jVT+esS/Bg8z8jzxp5BnDCjjwsuKsPQrzswwpFy4J1AUJbXPRfkpcX0mXrzeXW79g==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
]
|
||||
},
|
||||
"node_modules/@unrs/resolver-binding-win32-x64-msvc": {
|
||||
"version": "1.12.2",
|
||||
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.12.2.tgz",
|
||||
"integrity": "sha512-nAB74NfSNKknqQ1RrYj6uz8FcXEomu/MATJZxh/x+BArzN2U3JbOYC0APYzUIGhVY3m5hRxA8VPNdPBoG8txlA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
]
|
||||
},
|
||||
"node_modules/@vitejs/plugin-react": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.2.0.tgz",
|
||||
@ -6860,6 +6107,18 @@
|
||||
],
|
||||
"license": "CC-BY-4.0"
|
||||
},
|
||||
"node_modules/canvas-dither": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/canvas-dither/-/canvas-dither-1.0.1.tgz",
|
||||
"integrity": "sha512-wT1RnV2y9SauibcsfMMCagQTnXmyQq2nJXuX5tTEvo0sXm1vVbJ2r8QxSC3fRQMY3ZgZVq1j54cOvmO4u+Lu/A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/canvas-flatten": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/canvas-flatten/-/canvas-flatten-1.0.1.tgz",
|
||||
"integrity": "sha512-UhHeKeWGS23Bm5teZiHk6NEOme1OKCpv6arFovV89dydt0Xz+U4HkPXAKBZS/hmpR15x5U9tDj1ieMQMTwvCAA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/chalk": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||
@ -12313,6 +11572,18 @@
|
||||
"integrity": "sha512-AgZ3UOZm3YndfrJ4OYjgrT7bmCm/1iqkjvEfH/oYjzh6PD2qw4QuT3jjnXIrpdt4MTpMXclMT3lXbmRY+XRakw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/resize-image-data": {
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/resize-image-data/-/resize-image-data-0.3.1.tgz",
|
||||
"integrity": "sha512-6hVRn2S6W1cdycreA6Vth5XRN2NnGs7/RnVpxNw/1OCK8aCoevRFH2WprmQRZDnnH3e6awLv2tTIPuv7/7xeGg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@canvas/image-data": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/resolve": {
|
||||
"version": "2.0.0-next.7",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.7.tgz",
|
||||
|
||||
@ -38,6 +38,7 @@
|
||||
"@inertiajs/react": "^3.0.0",
|
||||
"@inertiajs/vite": "^3.0.0",
|
||||
"@laravel/passkeys": "^0.2.0",
|
||||
"@point-of-sale/receipt-printer-encoder": "^3.0.3",
|
||||
"@radix-ui/react-avatar": "^1.1.3",
|
||||
"@radix-ui/react-checkbox": "^1.1.4",
|
||||
"@radix-ui/react-collapsible": "^1.1.3",
|
||||
|
||||
368
resources/js/hooks/use-thermal-printer.ts
Normal file
368
resources/js/hooks/use-thermal-printer.ts
Normal file
@ -0,0 +1,368 @@
|
||||
import ReceiptPrinterEncoder from '@point-of-sale/receipt-printer-encoder';
|
||||
import { useCallback, useRef, useState } from 'react';
|
||||
|
||||
export type PrinterConnectionType = 'bluetooth' | 'usb';
|
||||
export type PaperWidth = 58 | 80;
|
||||
|
||||
interface PrinterState {
|
||||
connected: boolean;
|
||||
connectionType: PrinterConnectionType | null;
|
||||
printerName: string | null;
|
||||
}
|
||||
|
||||
/* ── Receipt Builder (persis seperti contoh Vue) ── */
|
||||
|
||||
export function getPaperColumns(paperWidth: PaperWidth): number {
|
||||
return paperWidth === 58 ? 32 : 48;
|
||||
}
|
||||
|
||||
function dashedLine(columns: number): string {
|
||||
return '-'.repeat(columns);
|
||||
}
|
||||
|
||||
export type OrderReceiptItem = {
|
||||
product_name: string;
|
||||
variant_name: string;
|
||||
quantity: string;
|
||||
unit_price: string;
|
||||
subtotal: string;
|
||||
};
|
||||
|
||||
export type OrderReceiptData = {
|
||||
order_number: string;
|
||||
created_at: string;
|
||||
customer_name: string | null;
|
||||
cashier_name: string;
|
||||
items: OrderReceiptItem[];
|
||||
subtotal: string;
|
||||
discount: string;
|
||||
nego_price: string | null;
|
||||
total_amount: string;
|
||||
notes: string | null;
|
||||
};
|
||||
|
||||
export type EncodeOrderReceiptOptions = {
|
||||
storeName: string;
|
||||
storeAddress: string;
|
||||
paperWidth: PaperWidth;
|
||||
};
|
||||
|
||||
export function encodeOrderReceipt(
|
||||
order: OrderReceiptData,
|
||||
options: EncodeOrderReceiptOptions,
|
||||
): Uint8Array {
|
||||
const columns = getPaperColumns(options.paperWidth);
|
||||
const amountColumnWidth = 14;
|
||||
const labelColumnWidth = columns - amountColumnWidth;
|
||||
|
||||
const encoder = new ReceiptPrinterEncoder({ columns });
|
||||
|
||||
encoder
|
||||
.initialize()
|
||||
.align('center')
|
||||
.bold(true)
|
||||
.text('')
|
||||
.newline()
|
||||
.text(options.storeName)
|
||||
.newline()
|
||||
.bold(false)
|
||||
.newline();
|
||||
|
||||
for (const addressLine of options.storeAddress.split('\n')) {
|
||||
const trimmed = addressLine.trim();
|
||||
if (trimmed) {
|
||||
encoder.align('center').text(trimmed).newline();
|
||||
}
|
||||
}
|
||||
|
||||
encoder.align('center').line(dashedLine(columns));
|
||||
|
||||
encoder
|
||||
.align('left')
|
||||
.line(`No : ${order.order_number}`)
|
||||
.line(`Tgl : ${order.created_at}`);
|
||||
|
||||
if (order.customer_name) {
|
||||
encoder.line(`Cust : ${order.customer_name}`);
|
||||
}
|
||||
|
||||
encoder
|
||||
.line(`Kasir: ${order.cashier_name}`)
|
||||
.line(dashedLine(columns));
|
||||
|
||||
for (const item of order.items) {
|
||||
encoder
|
||||
.line(item.product_name)
|
||||
.line(` ${item.variant_name}`)
|
||||
.table(
|
||||
[
|
||||
{ width: labelColumnWidth, align: 'left' },
|
||||
{ width: amountColumnWidth, align: 'right' },
|
||||
],
|
||||
[
|
||||
[
|
||||
`${item.quantity} x ${item.unit_price}`,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(rowEncoder: any) =>
|
||||
rowEncoder
|
||||
.bold()
|
||||
.text(item.subtotal)
|
||||
.bold(false),
|
||||
],
|
||||
],
|
||||
)
|
||||
.newline();
|
||||
}
|
||||
|
||||
encoder.line(dashedLine(columns));
|
||||
|
||||
const summaryRows = [
|
||||
['Subtotal', order.subtotal],
|
||||
['Diskon', order.discount],
|
||||
];
|
||||
|
||||
if (order.nego_price) {
|
||||
summaryRows.push(['Harga Nego', order.nego_price]);
|
||||
}
|
||||
|
||||
summaryRows.push(['Total', order.total_amount]);
|
||||
|
||||
encoder.table(
|
||||
[
|
||||
{ width: labelColumnWidth, align: 'left' },
|
||||
{ width: amountColumnWidth, align: 'right' },
|
||||
],
|
||||
summaryRows,
|
||||
);
|
||||
|
||||
encoder
|
||||
.newline()
|
||||
.line(dashedLine(columns))
|
||||
.align('center')
|
||||
.line('Terima kasih atas kunjungannya')
|
||||
.line('Silakan berbelanja kembali')
|
||||
.newline(3)
|
||||
.cut();
|
||||
|
||||
return encoder.encode();
|
||||
}
|
||||
|
||||
/* ── Printer Connection Hook ── */
|
||||
|
||||
export function useThermalPrinter() {
|
||||
const [state, setState] = useState<PrinterState>({
|
||||
connected: false,
|
||||
connectionType: null,
|
||||
printerName: null,
|
||||
});
|
||||
|
||||
const bluetoothDeviceRef = useRef<BluetoothDevice | null>(null);
|
||||
const bluetoothCharacteristicRef =
|
||||
useRef<BluetoothCharacteristic | null>(null);
|
||||
const usbDeviceRef = useRef<USBDevice | null>(null);
|
||||
|
||||
const disconnect = useCallback(async () => {
|
||||
try {
|
||||
if (
|
||||
state.connectionType === 'bluetooth' &&
|
||||
bluetoothDeviceRef.current
|
||||
) {
|
||||
bluetoothDeviceRef.current.gatt?.disconnect();
|
||||
bluetoothDeviceRef.current = null;
|
||||
bluetoothCharacteristicRef.current = null;
|
||||
}
|
||||
|
||||
if (state.connectionType === 'usb' && usbDeviceRef.current) {
|
||||
usbDeviceRef.current.close();
|
||||
usbDeviceRef.current = null;
|
||||
}
|
||||
} catch {
|
||||
// silent
|
||||
}
|
||||
|
||||
setState({
|
||||
connected: false,
|
||||
connectionType: null,
|
||||
printerName: null,
|
||||
});
|
||||
}, [state.connectionType]);
|
||||
|
||||
const connectBluetooth = useCallback(async () => {
|
||||
if (!navigator.bluetooth) {
|
||||
throw new Error(
|
||||
'Web Bluetooth tidak didukung di browser ini. Gunakan Chrome atau Edge.',
|
||||
);
|
||||
}
|
||||
|
||||
const device = await navigator.bluetooth.requestDevice({
|
||||
acceptAllDevices: true,
|
||||
optionalServices: [
|
||||
'000018f0-0000-1000-8000-00805f9b34fb',
|
||||
'00001800-0000-1000-8000-00805f9b34fb',
|
||||
'00001801-0000-1000-8000-00805f9b34fb',
|
||||
'00001101-0000-1000-8000-00805f9b34fb',
|
||||
'0000ffe0-0000-1000-8000-00805f9b34fb',
|
||||
'0000fee7-0000-1000-8000-00805f9b34fb',
|
||||
],
|
||||
});
|
||||
|
||||
const server = await device.gatt?.connect();
|
||||
|
||||
if (!server) {
|
||||
throw new Error('Gagal terhubung ke perangkat Bluetooth.');
|
||||
}
|
||||
|
||||
let characteristic: BluetoothCharacteristic | null = null;
|
||||
|
||||
const services = await server.getPrimaryServices();
|
||||
for (const svc of services) {
|
||||
try {
|
||||
const chars = await svc.getCharacteristics();
|
||||
const writable = chars.find(
|
||||
(c) =>
|
||||
c.properties.writeWithoutResponse ||
|
||||
c.properties.write,
|
||||
);
|
||||
if (writable) {
|
||||
characteristic = writable;
|
||||
break;
|
||||
}
|
||||
} catch {
|
||||
// skip
|
||||
}
|
||||
}
|
||||
|
||||
if (!characteristic) {
|
||||
throw new Error(
|
||||
'Tidak ditemukan characteristic yang bisa ditulis di printer ini.',
|
||||
);
|
||||
}
|
||||
|
||||
bluetoothDeviceRef.current = device;
|
||||
bluetoothCharacteristicRef.current = characteristic;
|
||||
|
||||
setState({
|
||||
connected: true,
|
||||
connectionType: 'bluetooth',
|
||||
printerName: device.name ?? 'Printer Bluetooth',
|
||||
});
|
||||
|
||||
return device.name ?? 'Printer Bluetooth';
|
||||
}, []);
|
||||
|
||||
const connectUSB = useCallback(async () => {
|
||||
if (!navigator.usb) {
|
||||
throw new Error(
|
||||
'Web USB tidak didukung di browser ini. Gunakan Chrome atau Edge.',
|
||||
);
|
||||
}
|
||||
|
||||
const device = await navigator.usb.requestDevice({ filters: [] });
|
||||
await device.open();
|
||||
|
||||
if (device.configuration === null) {
|
||||
await device.selectConfiguration(1);
|
||||
}
|
||||
|
||||
const iface =
|
||||
device.configuration?.interfaces.find((i) =>
|
||||
i.alternate.endpoints.some((e) => e.direction === 'out'),
|
||||
) ?? device.configuration?.interfaces[0];
|
||||
|
||||
if (iface) {
|
||||
await device.claimInterface(iface.interfaceNumber);
|
||||
}
|
||||
|
||||
usbDeviceRef.current = device;
|
||||
|
||||
setState({
|
||||
connected: true,
|
||||
connectionType: 'usb',
|
||||
printerName: device.productName ?? 'Printer USB',
|
||||
});
|
||||
|
||||
return device.productName ?? 'Printer USB';
|
||||
}, []);
|
||||
|
||||
const sendToBluetooth = useCallback(async (data: Uint8Array) => {
|
||||
const characteristic = bluetoothCharacteristicRef.current;
|
||||
if (!characteristic) {
|
||||
throw new Error(
|
||||
'Printer Bluetooth tidak terhubung.',
|
||||
);
|
||||
}
|
||||
|
||||
const useWriteWithoutResponse =
|
||||
characteristic.properties.writeWithoutResponse;
|
||||
const chunkSize = 128;
|
||||
|
||||
for (let i = 0; i < data.length; i += chunkSize) {
|
||||
const chunk = data.slice(i, i + chunkSize);
|
||||
if (useWriteWithoutResponse) {
|
||||
await characteristic.writeValueWithoutResponse(chunk);
|
||||
} else {
|
||||
await characteristic.writeValueWithResponse(chunk);
|
||||
}
|
||||
await new Promise((r) => setTimeout(r, 10));
|
||||
}
|
||||
}, []);
|
||||
|
||||
const sendToUSB = useCallback(async (data: Uint8Array) => {
|
||||
const device = usbDeviceRef.current;
|
||||
if (!device) {
|
||||
throw new Error('Printer USB tidak terhubung.');
|
||||
}
|
||||
|
||||
const iface = device.configuration?.interfaces.find((i) =>
|
||||
i.alternate.endpoints.some((e) => e.direction === 'out'),
|
||||
);
|
||||
const endpoint =
|
||||
iface?.alternate.endpoints.find(
|
||||
(e) => e.direction === 'out',
|
||||
)?.endpointNumber ?? 1;
|
||||
|
||||
const chunkSize = 512;
|
||||
for (let i = 0; i < data.length; i += chunkSize) {
|
||||
const chunk = data.slice(i, i + chunkSize);
|
||||
await device.transferOut(endpoint, chunk);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const print = useCallback(
|
||||
async (data: Uint8Array) => {
|
||||
if (!state.connected) {
|
||||
throw new Error('Tidak ada printer yang terhubung.');
|
||||
}
|
||||
|
||||
if (state.connectionType === 'bluetooth') {
|
||||
await sendToBluetooth(data);
|
||||
} else if (state.connectionType === 'usb') {
|
||||
await sendToUSB(data);
|
||||
}
|
||||
},
|
||||
[state.connected, state.connectionType, sendToBluetooth, sendToUSB],
|
||||
);
|
||||
|
||||
const connect = useCallback(
|
||||
async (type: PrinterConnectionType) => {
|
||||
if (state.connected) {
|
||||
await disconnect();
|
||||
}
|
||||
|
||||
if (type === 'bluetooth') {
|
||||
return await connectBluetooth();
|
||||
}
|
||||
return await connectUSB();
|
||||
},
|
||||
[state.connected, disconnect, connectBluetooth, connectUSB],
|
||||
);
|
||||
|
||||
return {
|
||||
connected: state.connected,
|
||||
connectionType: state.connectionType,
|
||||
printerName: state.printerName,
|
||||
connect,
|
||||
disconnect,
|
||||
print,
|
||||
};
|
||||
}
|
||||
@ -1,7 +1,8 @@
|
||||
import { Head, Link, router } from '@inertiajs/react';
|
||||
import { Head, Link, router, usePage } from '@inertiajs/react';
|
||||
import { format } from 'date-fns';
|
||||
import { Plus } from 'lucide-react';
|
||||
import { Bluetooth, Cable, Plus, Printer, Unplug } from 'lucide-react';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { toast } from 'sonner';
|
||||
import { CardTable } from '@/components/card-table';
|
||||
import { DatePicker } from '@/components/date-picker';
|
||||
import { DeleteConfirmDialog } from '@/components/delete-confirm-dialog';
|
||||
@ -17,6 +18,13 @@ import {
|
||||
ComboboxItem,
|
||||
ComboboxList,
|
||||
} from '@/components/ui/combobox';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu';
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
@ -26,6 +34,7 @@ import {
|
||||
} from '@/components/ui/select';
|
||||
import { useCan } from '@/hooks/use-can';
|
||||
import { useServerTable } from '@/hooks/use-server-table';
|
||||
import { useThermalPrinter, encodeOrderReceipt } from '@/hooks/use-thermal-printer';
|
||||
import {
|
||||
destroy,
|
||||
create as transactionCreate,
|
||||
@ -89,8 +98,10 @@ export default function TransactionIndex({
|
||||
filterOptions,
|
||||
}: Props) {
|
||||
const { can } = useCan();
|
||||
const { name: appName, address: appAddress } = usePage().props as unknown as { name: string; address: string };
|
||||
const [deleting, setDeleting] = useState<Transaction | null>(null);
|
||||
const expand = useCardTableExpand(true);
|
||||
const printer = useThermalPrinter();
|
||||
|
||||
const pagination = {
|
||||
current_page: transactions.current_page,
|
||||
@ -152,6 +163,52 @@ export default function TransactionIndex({
|
||||
router.patch(transactionUpdateStatus.url(transaction.id), { status });
|
||||
}
|
||||
|
||||
function handlePrintReceipt(transaction: Transaction, paperWidth: 58 | 80) {
|
||||
if (!printer.connected) {
|
||||
alert('Hubungkan printer terlebih dahulu.');
|
||||
return;
|
||||
}
|
||||
|
||||
const rupiah = (n: number) => `Rp ${n.toLocaleString('id-ID')}`;
|
||||
|
||||
const items = (transaction.order_items ?? []).map((item) => ({
|
||||
product_name: item.product_variant?.product?.name ?? '-',
|
||||
variant_name: item.product_variant?.name ?? '-',
|
||||
quantity: `${Number(item.quantity)}`,
|
||||
unit_price: rupiah(Number(item.unit_price)),
|
||||
subtotal: rupiah(Number(item.subtotal)),
|
||||
}));
|
||||
|
||||
const cashierName =
|
||||
transaction.created_by?.user_profile?.full_name ?? '-';
|
||||
|
||||
const data = encodeOrderReceipt(
|
||||
{
|
||||
order_number: transaction.order_number,
|
||||
created_at: format(new Date(transaction.created_at), 'dd/MM/yyyy HH:mm'),
|
||||
customer_name: transaction.customer?.name ?? null,
|
||||
cashier_name: cashierName,
|
||||
items,
|
||||
subtotal: rupiah(Number(transaction.subtotal)),
|
||||
discount: rupiah(Number(transaction.discount)),
|
||||
nego_price: transaction.nego_price != null && Number(transaction.nego_price) > 0
|
||||
? rupiah(Number(transaction.nego_price))
|
||||
: null,
|
||||
total_amount: rupiah(Number(transaction.total_amount)),
|
||||
notes: transaction.notes ?? null,
|
||||
},
|
||||
{
|
||||
storeName: appName,
|
||||
storeAddress: appAddress,
|
||||
paperWidth,
|
||||
},
|
||||
);
|
||||
|
||||
printer.print(data).catch((err: Error) => {
|
||||
alert(`Gagal mencetak: ${err.message}`);
|
||||
});
|
||||
}
|
||||
|
||||
const filterToolbar = (
|
||||
<FilterPopover
|
||||
open={filterOpen}
|
||||
@ -358,14 +415,76 @@ export default function TransactionIndex({
|
||||
<PageHeader
|
||||
title="Transaksi"
|
||||
actions={
|
||||
can('orders.create') ? (
|
||||
<Button asChild>
|
||||
<Link href={transactionCreate.url()}>
|
||||
<Plus className="h-4 w-4" />
|
||||
Tambah
|
||||
</Link>
|
||||
</Button>
|
||||
) : undefined
|
||||
<div className="flex items-center gap-2">
|
||||
{printer.connected ? (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="outline" size="sm">
|
||||
<Printer className="h-4 w-4" />
|
||||
{printer.printerName}
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem
|
||||
onClick={async () => {
|
||||
const name = await printer.disconnect();
|
||||
toast.success('Printer diputuskan.');
|
||||
}}
|
||||
>
|
||||
<Unplug className="h-4 w-4" />
|
||||
Putuskan
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
) : (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="outline" size="sm">
|
||||
<Printer className="h-4 w-4" />
|
||||
Hubungkan Printer
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem
|
||||
onClick={async () => {
|
||||
try {
|
||||
const name = await printer.connect('bluetooth');
|
||||
toast.success(`Terhubung ke ${name}`);
|
||||
} catch (err) {
|
||||
toast.error(err instanceof Error ? err.message : 'Gagal menghubungkan Bluetooth.');
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Bluetooth className="h-4 w-4" />
|
||||
Hubungkan Bluetooth
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem
|
||||
onClick={async () => {
|
||||
try {
|
||||
const name = await printer.connect('usb');
|
||||
toast.success(`Terhubung ke ${name}`);
|
||||
} catch (err) {
|
||||
toast.error(err instanceof Error ? err.message : 'Gagal menghubungkan USB.');
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Cable className="h-4 w-4" />
|
||||
Hubungkan USB
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)}
|
||||
|
||||
{can('orders.create') ? (
|
||||
<Button asChild>
|
||||
<Link href={transactionCreate.url()}>
|
||||
<Plus className="h-4 w-4" />
|
||||
Tambah
|
||||
</Link>
|
||||
</Button>
|
||||
) : undefined}
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
|
||||
@ -404,6 +523,7 @@ export default function TransactionIndex({
|
||||
}}
|
||||
onDelete={(t) => setDeleting(t)}
|
||||
onUpdateStatus={(t, status) => handleUpdateStatus(t, status)}
|
||||
onPrint={(t, pw) => handlePrintReceipt(t, pw)}
|
||||
/>
|
||||
)}
|
||||
renderSubContent={(transaction) => (
|
||||
|
||||
@ -2,10 +2,17 @@ import { RowActions } from '@/components/row-actions';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu';
|
||||
import { useCan } from '@/hooks/use-can';
|
||||
import type { PaperWidth } from '@/hooks/use-thermal-printer';
|
||||
import { formatDateTime, formatNumber } from '@/lib/format';
|
||||
import { formatCurrency } from '@/lib/utils';
|
||||
import { CheckCircle, ChevronDown, Pencil, Send, Trash2, XCircle } from 'lucide-react';
|
||||
import { CheckCircle, ChevronDown, Pencil, Printer, Send, Trash2, XCircle } from 'lucide-react';
|
||||
import type { Transaction } from './columns';
|
||||
|
||||
const STATUS_BADGE_CLASSES: Record<string, string> = {
|
||||
@ -24,6 +31,7 @@ export type TransactionCardRowParams = {
|
||||
onEdit: (transaction: Transaction) => void;
|
||||
onDelete: (transaction: Transaction) => void;
|
||||
onUpdateStatus: (transaction: Transaction, status: string) => void;
|
||||
onPrint: (transaction: Transaction, paperWidth: PaperWidth) => void;
|
||||
};
|
||||
|
||||
export function TransactionCardRow({
|
||||
@ -34,6 +42,7 @@ export function TransactionCardRow({
|
||||
onEdit,
|
||||
onDelete,
|
||||
onUpdateStatus,
|
||||
onPrint,
|
||||
}: TransactionCardRowParams) {
|
||||
const { can } = useCan();
|
||||
const items = transaction.order_items ?? [];
|
||||
@ -51,7 +60,6 @@ export function TransactionCardRow({
|
||||
);
|
||||
const statusBadgeClass =
|
||||
STATUS_BADGE_CLASSES[transaction.status] ?? STATUS_BADGE_CLASSES.pending;
|
||||
console.log('transaction', transaction);
|
||||
return (
|
||||
<Card className="overflow-hidden">
|
||||
<CardContent className="p-0">
|
||||
@ -188,7 +196,26 @@ console.log('transaction', transaction);
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{(can('orders.update') || can('orders.delete')) && (
|
||||
<div className="flex shrink-0 items-center gap-1">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="ghost" size="icon" title="Cetak Struk">
|
||||
<Printer className="h-4 w-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem
|
||||
onClick={() => onPrint(transaction, 58)}
|
||||
>
|
||||
Struk 58mm
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
onClick={() => onPrint(transaction, 80)}
|
||||
>
|
||||
Struk 80mm
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
<RowActions
|
||||
actions={[
|
||||
...(transaction.status === 'pending' && can('orders.update')
|
||||
@ -240,7 +267,7 @@ console.log('transaction', transaction);
|
||||
]}
|
||||
wrapperClassName="flex shrink-0 items-center gap-1"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
@ -34,7 +34,7 @@ import {
|
||||
edit as variantEdit,
|
||||
} from '@/routes/admin/master/products/variants';
|
||||
import { Head } from '@inertiajs/react';
|
||||
import { Plus } from 'lucide-react';
|
||||
import { Plus } from 'lucide-react';
|
||||
import { useMemo, useState } from 'react';
|
||||
import type { Product, ProductVariant } from './columns';
|
||||
import { ProductCardRow } from './product-card';
|
||||
|
||||
35
resources/js/types/receipt-printer-encoder.d.ts
vendored
Normal file
35
resources/js/types/receipt-printer-encoder.d.ts
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
declare module '@point-of-sale/receipt-printer-encoder' {
|
||||
interface ReceiptPrinterEncoderOptions {
|
||||
language?: string;
|
||||
codepageMapping?: Record<string, string>;
|
||||
columns?: number;
|
||||
}
|
||||
|
||||
interface TableOptions {
|
||||
width: number;
|
||||
align: 'left' | 'center' | 'right';
|
||||
}
|
||||
|
||||
type TableCell = string | ((encoder: ReceiptPrinterEncoder) => void);
|
||||
|
||||
class ReceiptPrinterEncoder {
|
||||
constructor(options?: ReceiptPrinterEncoderOptions);
|
||||
initialize(): this;
|
||||
reset(): this;
|
||||
align(align: 'left' | 'center' | 'right'): this;
|
||||
bold(bold?: boolean): this;
|
||||
underline(underline?: boolean): this;
|
||||
invert(invert?: boolean): this;
|
||||
width(width: number): this;
|
||||
height(height: number): this;
|
||||
text(text: string): this;
|
||||
newline(count?: number): this;
|
||||
line(text: string): this;
|
||||
table(options: TableOptions[], rows: TableCell[][]): this;
|
||||
cut(): this;
|
||||
feed(lines?: number): this;
|
||||
encode(): Uint8Array;
|
||||
}
|
||||
|
||||
export default ReceiptPrinterEncoder;
|
||||
}
|
||||
174
resources/js/types/web-bluetooth-usb.d.ts
vendored
Normal file
174
resources/js/types/web-bluetooth-usb.d.ts
vendored
Normal file
@ -0,0 +1,174 @@
|
||||
interface BluetoothDevice {
|
||||
readonly id: string;
|
||||
readonly name: string | null;
|
||||
readonly gatt?: BluetoothRemoteGATTServer;
|
||||
addEventListener(
|
||||
type: 'gattserverdisconnected',
|
||||
listener: EventListener,
|
||||
): void;
|
||||
removeEventListener(
|
||||
type: 'gattserverdisconnected',
|
||||
listener: EventListener,
|
||||
): void;
|
||||
}
|
||||
|
||||
interface BluetoothRemoteGATTServer {
|
||||
readonly device: BluetoothDevice;
|
||||
readonly connected: boolean;
|
||||
connect(): Promise<BluetoothRemoteGATTServer>;
|
||||
disconnect(): void;
|
||||
getPrimaryService(
|
||||
service: string,
|
||||
): Promise<BluetoothRemoteGATTService>;
|
||||
getPrimaryServices(): Promise<BluetoothRemoteGATTService[]>;
|
||||
}
|
||||
|
||||
interface BluetoothRemoteGATTService {
|
||||
readonly device: BluetoothDevice;
|
||||
readonly uuid: string;
|
||||
getCharacteristic(
|
||||
characteristic: string,
|
||||
): Promise<BluetoothCharacteristic>;
|
||||
getCharacteristics(): Promise<BluetoothCharacteristic[]>;
|
||||
}
|
||||
|
||||
interface BluetoothCharacteristic {
|
||||
readonly uuid: string;
|
||||
readonly service: BluetoothRemoteGATTService;
|
||||
readonly properties: BluetoothCharacteristicProperties;
|
||||
readValue(): Promise<DataView>;
|
||||
writeValueWithResponse(value: BufferSource): Promise<void>;
|
||||
writeValueWithoutResponse(value: BufferSource): Promise<void>;
|
||||
startNotifications(): Promise<BluetoothCharacteristic>;
|
||||
stopNotifications(): Promise<BluetoothCharacteristic>;
|
||||
}
|
||||
|
||||
interface BluetoothCharacteristicProperties {
|
||||
readonly broadcast: boolean;
|
||||
readonly read: boolean;
|
||||
readonly writeWithoutResponse: boolean;
|
||||
readonly write: boolean;
|
||||
readonly notify: boolean;
|
||||
readonly indicate: boolean;
|
||||
readonly authenticatedSignedWrites: boolean;
|
||||
readonly reliableWrite: boolean;
|
||||
readonly writableAuxiliaries: boolean;
|
||||
}
|
||||
|
||||
interface BluetoothRequestDeviceOptions {
|
||||
filters?: BluetoothLEScanFilter[];
|
||||
optionalServices?: string[];
|
||||
acceptAllDevices?: boolean;
|
||||
}
|
||||
|
||||
interface BluetoothLEScanFilter {
|
||||
services?: string[];
|
||||
name?: string;
|
||||
namePrefix?: string;
|
||||
manufacturerData?: BluetoothManufacturerDataFilter[];
|
||||
serviceData?: BluetoothServiceDataFilter[];
|
||||
}
|
||||
|
||||
interface BluetoothManufacturerDataFilter {
|
||||
companyIdentifier: number;
|
||||
dataPrefix?: BufferSource;
|
||||
mask?: BufferSource;
|
||||
}
|
||||
|
||||
interface BluetoothServiceDataFilter {
|
||||
service: string;
|
||||
dataPrefix?: BufferSource;
|
||||
mask?: BufferSource;
|
||||
}
|
||||
|
||||
interface Navigator {
|
||||
bluetooth?: {
|
||||
requestDevice(
|
||||
options: BluetoothRequestDeviceOptions,
|
||||
): Promise<BluetoothDevice>;
|
||||
};
|
||||
}
|
||||
|
||||
interface USBDevice {
|
||||
readonly vendorId: number;
|
||||
readonly productId: number;
|
||||
readonly deviceVersionMajor: number;
|
||||
readonly deviceVersionMinor: number;
|
||||
readonly deviceVersionSubminor: number;
|
||||
readonly manufacturerName: string | null;
|
||||
readonly productName: string | null;
|
||||
readonly serialNumber: string | null;
|
||||
readonly configuration: USBConfiguration | null;
|
||||
open(): Promise<void>;
|
||||
close(): Promise<void>;
|
||||
selectConfiguration(configurationValue: number): Promise<void>;
|
||||
claimInterface(interfaceNumber: number): Promise<void>;
|
||||
releaseInterface(interfaceNumber: number): Promise<void>;
|
||||
transferOut(
|
||||
endpointNumber: number,
|
||||
data: BufferSource,
|
||||
): Promise<USBOutTransferResult>;
|
||||
transferIn(
|
||||
endpointNumber: number,
|
||||
length: number,
|
||||
): Promise<USBInTransferResult>;
|
||||
}
|
||||
|
||||
interface USBConfiguration {
|
||||
readonly configurationValue: number;
|
||||
readonly configurationName: string | null;
|
||||
readonly interfaces: USBInterface[];
|
||||
}
|
||||
|
||||
interface USBInterface {
|
||||
readonly interfaceNumber: number;
|
||||
readonly alternate: USBAlternateInterface;
|
||||
readonly alternates: USBAlternateInterface[];
|
||||
}
|
||||
|
||||
interface USBAlternateInterface {
|
||||
readonly alternateSetting: number;
|
||||
readonly interfaceClass: number;
|
||||
readonly interfaceSubclass: number;
|
||||
readonly interfaceProtocol: number;
|
||||
readonly interfaceName: string | null;
|
||||
readonly endpoints: USBEndpoint[];
|
||||
}
|
||||
|
||||
interface USBEndpoint {
|
||||
readonly endpointNumber: number;
|
||||
readonly direction: 'in' | 'out';
|
||||
readonly type: 'bulk' | 'interrupt' | 'isochronous' | 'control';
|
||||
readonly packetSize: number;
|
||||
}
|
||||
|
||||
interface USBOutTransferResult {
|
||||
readonly bytesWritten: number;
|
||||
readonly status: 'ok' | 'stall' | 'babble';
|
||||
}
|
||||
|
||||
interface USBInTransferResult {
|
||||
readonly data: DataView | null;
|
||||
readonly status: 'ok' | 'stall' | 'babble';
|
||||
}
|
||||
|
||||
interface Navigator {
|
||||
usb?: {
|
||||
requestDevice(
|
||||
options: USBRequestDeviceOptions,
|
||||
): Promise<USBDevice>;
|
||||
};
|
||||
}
|
||||
|
||||
interface USBRequestDeviceOptions {
|
||||
filters: USBDeviceFilter[];
|
||||
}
|
||||
|
||||
interface USBDeviceFilter {
|
||||
vendorId?: number;
|
||||
productId?: number;
|
||||
classCode?: number;
|
||||
subclassCode?: number;
|
||||
protocolCode?: number;
|
||||
serialNumber?: string;
|
||||
}
|
||||
@ -8,6 +8,9 @@ import { defineConfig } from 'vite';
|
||||
import { VitePWA } from 'vite-plugin-pwa';
|
||||
|
||||
export default defineConfig({
|
||||
optimizeDeps: {
|
||||
include: ['@point-of-sale/receipt-printer-encoder'],
|
||||
},
|
||||
plugins: [
|
||||
laravel({
|
||||
input: ['resources/css/app.css', 'resources/js/app.tsx'],
|
||||
|
||||
Loading…
Reference in New Issue
Block a user