Add reservation and purchase functionality
Introduces ReservationSuccess, ReservationFailed, PurchaseSuccess, PurchaseFailed, TicketSmall, and TicketList components for handling and displaying reservation and purchase outcomes. Updates order flow logic in OrderComponent to support reservation and purchase states, disables/enables form inputs during submission, and integrates new UI feedback. Also adds angularx-qrcode dependency and updates @infinimotion/model-frontend version.
This commit is contained in:
273
package-lock.json
generated
273
package-lock.json
generated
@@ -16,8 +16,9 @@
|
|||||||
"@angular/material": "^20.2.9",
|
"@angular/material": "^20.2.9",
|
||||||
"@angular/platform-browser": "^20.3.0",
|
"@angular/platform-browser": "^20.3.0",
|
||||||
"@angular/router": "^20.3.0",
|
"@angular/router": "^20.3.0",
|
||||||
"@infinimotion/model-frontend": "^0.0.89",
|
"@infinimotion/model-frontend": "^0.0.102",
|
||||||
"@tailwindcss/postcss": "^4.1.14",
|
"@tailwindcss/postcss": "^4.1.14",
|
||||||
|
"angularx-qrcode": "^20.0.0",
|
||||||
"ngx-mask": "^20.0.3",
|
"ngx-mask": "^20.0.3",
|
||||||
"postcss": "^8.5.6",
|
"postcss": "^8.5.6",
|
||||||
"rxjs": "~7.8.0",
|
"rxjs": "~7.8.0",
|
||||||
@@ -1401,9 +1402,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@infinimotion/model-frontend": {
|
"node_modules/@infinimotion/model-frontend": {
|
||||||
"version": "0.0.89",
|
"version": "0.0.102",
|
||||||
"resolved": "https://git.infinimotion.de/api/packages/infinimotion/npm/%40infinimotion%2Fmodel-frontend/-/0.0.89/model-frontend-0.0.89.tgz",
|
"resolved": "https://git.infinimotion.de/api/packages/infinimotion/npm/%40infinimotion%2Fmodel-frontend/-/0.0.102/model-frontend-0.0.102.tgz",
|
||||||
"integrity": "sha512-lvvQy8RWs41Bz52uBgsUKkwn8teGlgxlmG8Rvsgkh+v1IMVWFWVQmfMS7Rznd0lCZRgK1ByihH80X9eAN12idA==",
|
"integrity": "sha512-NJV9bSBubdOZ1GBIe9To3o/hh6AZscJcTyaZY2nGmMxH+GhtvO1AHmjhrQeRjwAKFiwZMEwEg4ktFiOAp3MTMQ==",
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/@inquirer/ansi": {
|
"node_modules/@inquirer/ansi": {
|
||||||
@@ -3995,6 +3996,19 @@
|
|||||||
"node": ">= 14.0.0"
|
"node": ">= 14.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/angularx-qrcode": {
|
||||||
|
"version": "20.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/angularx-qrcode/-/angularx-qrcode-20.0.0.tgz",
|
||||||
|
"integrity": "sha512-WZolRZztQsQxOXqodNSDicxPWNO79t/AT4wts+DxwYdtdXb1RELfZjtax9oGMQQ6mEZ6bwk5GqBGEDB3Y+cSqw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"qrcode": "1.5.4",
|
||||||
|
"tslib": "^2.3.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@angular/core": "^20.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ansi-escapes": {
|
"node_modules/ansi-escapes": {
|
||||||
"version": "7.2.0",
|
"version": "7.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.2.0.tgz",
|
||||||
@@ -4387,6 +4401,15 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/camelcase": {
|
||||||
|
"version": "5.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
|
||||||
|
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/caniuse-lite": {
|
"node_modules/caniuse-lite": {
|
||||||
"version": "1.0.30001754",
|
"version": "1.0.30001754",
|
||||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001754.tgz",
|
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001754.tgz",
|
||||||
@@ -4547,7 +4570,6 @@
|
|||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"color-name": "~1.1.4"
|
"color-name": "~1.1.4"
|
||||||
@@ -4560,7 +4582,6 @@
|
|||||||
"version": "1.1.4",
|
"version": "1.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/colorette": {
|
"node_modules/colorette": {
|
||||||
@@ -4806,6 +4827,15 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/decamelize": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/depd": {
|
"node_modules/depd": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
||||||
@@ -4843,6 +4873,12 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/dijkstrajs": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/dom-serialize": {
|
"node_modules/dom-serialize": {
|
||||||
"version": "2.2.1",
|
"version": "2.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz",
|
||||||
@@ -5447,6 +5483,19 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/find-up": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"locate-path": "^5.0.0",
|
||||||
|
"path-exists": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/flatted": {
|
"node_modules/flatted": {
|
||||||
"version": "3.3.3",
|
"version": "3.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
|
||||||
@@ -5586,7 +5635,6 @@
|
|||||||
"version": "2.0.5",
|
"version": "2.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
||||||
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
|
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
|
||||||
"dev": true,
|
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "6.* || 8.* || >= 10.*"
|
"node": "6.* || 8.* || >= 10.*"
|
||||||
@@ -7136,6 +7184,18 @@
|
|||||||
"@lmdb/lmdb-win32-x64": "3.4.2"
|
"@lmdb/lmdb-win32-x64": "3.4.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/locate-path": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"p-locate": "^4.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/lodash": {
|
"node_modules/lodash": {
|
||||||
"version": "4.17.21",
|
"version": "4.17.21",
|
||||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||||
@@ -8159,6 +8219,33 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
|
"node_modules/p-limit": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
|
||||||
|
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"p-try": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/p-locate": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"p-limit": "^2.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/p-map": {
|
"node_modules/p-map": {
|
||||||
"version": "7.0.4",
|
"version": "7.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.4.tgz",
|
||||||
@@ -8172,6 +8259,15 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/p-try": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/package-json-from-dist": {
|
"node_modules/package-json-from-dist": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
|
||||||
@@ -8322,6 +8418,15 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/path-exists": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/path-is-absolute": {
|
"node_modules/path-is-absolute": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||||
@@ -8426,6 +8531,15 @@
|
|||||||
"node": ">=16.20.0"
|
"node": ">=16.20.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/pngjs": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.13.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/postcss": {
|
"node_modules/postcss": {
|
||||||
"version": "8.5.6",
|
"version": "8.5.6",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
|
||||||
@@ -8516,6 +8630,125 @@
|
|||||||
"node": ">=0.9"
|
"node": ">=0.9"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/qrcode": {
|
||||||
|
"version": "1.5.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.4.tgz",
|
||||||
|
"integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"dijkstrajs": "^1.0.1",
|
||||||
|
"pngjs": "^5.0.0",
|
||||||
|
"yargs": "^15.3.1"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"qrcode": "bin/qrcode"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.13.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/qrcode/node_modules/ansi-regex": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/qrcode/node_modules/cliui": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
|
||||||
|
"integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"string-width": "^4.2.0",
|
||||||
|
"strip-ansi": "^6.0.0",
|
||||||
|
"wrap-ansi": "^6.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/qrcode/node_modules/emoji-regex": {
|
||||||
|
"version": "8.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||||
|
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/qrcode/node_modules/is-fullwidth-code-point": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/qrcode/node_modules/string-width": {
|
||||||
|
"version": "4.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||||
|
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"emoji-regex": "^8.0.0",
|
||||||
|
"is-fullwidth-code-point": "^3.0.0",
|
||||||
|
"strip-ansi": "^6.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/qrcode/node_modules/strip-ansi": {
|
||||||
|
"version": "6.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||||
|
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-regex": "^5.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/qrcode/node_modules/y18n": {
|
||||||
|
"version": "4.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
|
||||||
|
"integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
|
"node_modules/qrcode/node_modules/yargs": {
|
||||||
|
"version": "15.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
|
||||||
|
"integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"cliui": "^6.0.0",
|
||||||
|
"decamelize": "^1.2.0",
|
||||||
|
"find-up": "^4.1.0",
|
||||||
|
"get-caller-file": "^2.0.1",
|
||||||
|
"require-directory": "^2.1.1",
|
||||||
|
"require-main-filename": "^2.0.0",
|
||||||
|
"set-blocking": "^2.0.0",
|
||||||
|
"string-width": "^4.2.0",
|
||||||
|
"which-module": "^2.0.0",
|
||||||
|
"y18n": "^4.0.0",
|
||||||
|
"yargs-parser": "^18.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/qrcode/node_modules/yargs-parser": {
|
||||||
|
"version": "18.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
|
||||||
|
"integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"camelcase": "^5.0.0",
|
||||||
|
"decamelize": "^1.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/qs": {
|
"node_modules/qs": {
|
||||||
"version": "6.14.0",
|
"version": "6.14.0",
|
||||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
|
||||||
@@ -8583,7 +8816,6 @@
|
|||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
||||||
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
|
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
@@ -8599,6 +8831,12 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/require-main-filename": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
"node_modules/requires-port": {
|
"node_modules/requires-port": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
||||||
@@ -8867,6 +9105,12 @@
|
|||||||
"node": ">= 18"
|
"node": ">= 18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/set-blocking": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
"node_modules/setprototypeof": {
|
"node_modules/setprototypeof": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
|
||||||
@@ -10009,11 +10253,16 @@
|
|||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/which-module": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
"node_modules/wrap-ansi": {
|
"node_modules/wrap-ansi": {
|
||||||
"version": "6.2.0",
|
"version": "6.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
|
||||||
"integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
|
"integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ansi-styles": "^4.0.0",
|
"ansi-styles": "^4.0.0",
|
||||||
@@ -10118,7 +10367,6 @@
|
|||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
@@ -10128,7 +10376,6 @@
|
|||||||
"version": "4.3.0",
|
"version": "4.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"color-convert": "^2.0.1"
|
"color-convert": "^2.0.1"
|
||||||
@@ -10144,14 +10391,12 @@
|
|||||||
"version": "8.0.0",
|
"version": "8.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": {
|
"node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||||
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
@@ -10161,7 +10406,6 @@
|
|||||||
"version": "4.2.3",
|
"version": "4.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"emoji-regex": "^8.0.0",
|
"emoji-regex": "^8.0.0",
|
||||||
@@ -10176,7 +10420,6 @@
|
|||||||
"version": "6.0.1",
|
"version": "6.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ansi-regex": "^5.0.1"
|
"ansi-regex": "^5.0.1"
|
||||||
|
|||||||
@@ -30,8 +30,9 @@
|
|||||||
"@angular/material": "^20.2.9",
|
"@angular/material": "^20.2.9",
|
||||||
"@angular/platform-browser": "^20.3.0",
|
"@angular/platform-browser": "^20.3.0",
|
||||||
"@angular/router": "^20.3.0",
|
"@angular/router": "^20.3.0",
|
||||||
"@infinimotion/model-frontend": "^0.0.89",
|
"@infinimotion/model-frontend": "^0.0.102",
|
||||||
"@tailwindcss/postcss": "^4.1.14",
|
"@tailwindcss/postcss": "^4.1.14",
|
||||||
|
"angularx-qrcode": "^20.0.0",
|
||||||
"ngx-mask": "^20.0.3",
|
"ngx-mask": "^20.0.3",
|
||||||
"postcss": "^8.5.6",
|
"postcss": "^8.5.6",
|
||||||
"rxjs": "~7.8.0",
|
"rxjs": "~7.8.0",
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { AppRoutingModule } from './app-routing-module';
|
|||||||
import { App } from './app';
|
import { App } from './app';
|
||||||
|
|
||||||
import { NgxMaskDirective, NgxMaskPipe, provideNgxMask } from 'ngx-mask';
|
import { NgxMaskDirective, NgxMaskPipe, provideNgxMask } from 'ngx-mask';
|
||||||
|
import { QRCodeComponent } from 'angularx-qrcode';
|
||||||
|
|
||||||
import { MatIconModule } from '@angular/material/icon';
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
import { MatTabsModule } from '@angular/material/tabs';
|
import { MatTabsModule } from '@angular/material/tabs';
|
||||||
@@ -55,6 +56,12 @@ import { ShoppingCartComponent } from './shopping-cart/shopping-cart.component';
|
|||||||
import { OrderComponent } from './order/order.component';
|
import { OrderComponent } from './order/order.component';
|
||||||
import { SeatSelectionComponent } from './seat-selection/seat-selection.component';
|
import { SeatSelectionComponent } from './seat-selection/seat-selection.component';
|
||||||
import { NoSeatsInHallComponent } from './no-seats-in-hall/no-seats-in-hall.component';
|
import { NoSeatsInHallComponent } from './no-seats-in-hall/no-seats-in-hall.component';
|
||||||
|
import { ReservationSuccessComponent } from './reservation-success/reservation-success.component';
|
||||||
|
import { ReservationFailedComponent } from './reservation-failed/reservation-failed.component';
|
||||||
|
import { PurchaseSuccessComponent } from './purchase-success/purchase-success.component';
|
||||||
|
import { PurchaseFailedComponent } from './purchase-failed/purchase-failed.component';
|
||||||
|
import { TicketSmallComponent } from './ticket-small/ticket-small.component';
|
||||||
|
import { TicketListComponent } from './ticket-list/ticket-list.component';
|
||||||
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
@@ -92,6 +99,12 @@ import { NoSeatsInHallComponent } from './no-seats-in-hall/no-seats-in-hall.comp
|
|||||||
OrderComponent,
|
OrderComponent,
|
||||||
SeatSelectionComponent,
|
SeatSelectionComponent,
|
||||||
NoSeatsInHallComponent,
|
NoSeatsInHallComponent,
|
||||||
|
ReservationSuccessComponent,
|
||||||
|
ReservationFailedComponent,
|
||||||
|
PurchaseSuccessComponent,
|
||||||
|
PurchaseFailedComponent,
|
||||||
|
TicketSmallComponent,
|
||||||
|
TicketListComponent,
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
AppRoutingModule,
|
AppRoutingModule,
|
||||||
@@ -119,6 +132,7 @@ import { NoSeatsInHallComponent } from './no-seats-in-hall/no-seats-in-hall.comp
|
|||||||
MatStepperModule,
|
MatStepperModule,
|
||||||
NgxMaskDirective,
|
NgxMaskDirective,
|
||||||
NgxMaskPipe,
|
NgxMaskPipe,
|
||||||
|
QRCodeComponent,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
provideBrowserGlobalErrorListeners(),
|
provideBrowserGlobalErrorListeners(),
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
Erscheinungsjahr: {{ movie().year }}
|
Erscheinungsjahr: {{ movie().year }}
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<button matFab extended class="mb-3" (click)="importMovie(movie().imdbID, movie().title)" [disabled]="this.buttonDisabled">
|
<button matFab extended class="mb-3" (click)="importMovie(movie().imdbID!, movie().title!)" [disabled]="this.buttonDisabled">
|
||||||
<mat-icon>{{ buttonIcon }}</mat-icon>
|
<mat-icon>{{ buttonIcon }}</mat-icon>
|
||||||
{{ buttonText }}
|
{{ buttonText }}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<div class="w-full h-full relative">
|
<div class="w-full h-full relative">
|
||||||
|
|
||||||
@if (loadingService.loading$ | async){
|
@if (!performance() && (loadingService.loading$ | async)){
|
||||||
<div class="w-full h-full flex items-center justify-center">
|
<div class="w-full h-full flex items-center justify-center">
|
||||||
<mat-progress-spinner
|
<mat-progress-spinner
|
||||||
mode="indeterminate"
|
mode="indeterminate"
|
||||||
@@ -17,6 +17,16 @@
|
|||||||
></app-performance-info>
|
></app-performance-info>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@if(isSubmitting) {
|
||||||
|
<div class="absolute top-55 z-25 w-full px-6 my-auto">
|
||||||
|
<mat-progress-spinner
|
||||||
|
class="m-auto"
|
||||||
|
mode="indeterminate"
|
||||||
|
diameter="100"
|
||||||
|
></mat-progress-spinner>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
<mat-stepper orientation="horizontal" linear="true" [disableRipple]="true" (selectionChange)="onStepChange($event)" #stepper>
|
<mat-stepper orientation="horizontal" linear="true" [disableRipple]="true" (selectionChange)="onStepChange($event)" #stepper>
|
||||||
<mat-step>
|
<mat-step>
|
||||||
<ng-template matStepLabel>Warenkorb</ng-template>
|
<ng-template matStepLabel>Warenkorb</ng-template>
|
||||||
@@ -48,8 +58,8 @@
|
|||||||
|
|
||||||
<!-- Buttons -->
|
<!-- Buttons -->
|
||||||
<div class="flex space-x-5 mt-10">
|
<div class="flex space-x-5 mt-10">
|
||||||
<button mat-button matButton="outlined" matStepperNext class="w-1/2" [disabled]="totalSeats()==0">Reservieren</button>
|
<button mat-button matButton="outlined" matStepperNext class="w-1/2" [disabled]="totalSeats()==0" (click)="reservationClicked()">Reservieren</button>
|
||||||
<button mat-button matButton="filled" matStepperNext class="w-1/2" [disabled]="totalSeats()==0">Buchen</button>
|
<button mat-button matButton="filled" matStepperNext class="w-1/2" [disabled]="totalSeats()==0" (click)="purchaseClicked()">Kaufen</button>
|
||||||
</div>
|
</div>
|
||||||
</mat-step>
|
</mat-step>
|
||||||
|
|
||||||
@@ -59,6 +69,16 @@
|
|||||||
|
|
||||||
<div class="performance-info-space"></div>
|
<div class="performance-info-space"></div>
|
||||||
|
|
||||||
|
@if (seatsReserved && !isSubmitting) {
|
||||||
|
<div class="h-4"></div>
|
||||||
|
@if (successful) {
|
||||||
|
<app-reservation-success [order]="this.createdOrder"></app-reservation-success>
|
||||||
|
} @else {
|
||||||
|
<app-reservation-failed></app-reservation-failed>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@else {
|
||||||
|
|
||||||
<!-- Name -->
|
<!-- Name -->
|
||||||
<mat-form-field class="w-full mt-8">
|
<mat-form-field class="w-full mt-8">
|
||||||
<mat-label>Name</mat-label>
|
<mat-label>Name</mat-label>
|
||||||
@@ -82,9 +102,12 @@
|
|||||||
|
|
||||||
<!-- Buttons -->
|
<!-- Buttons -->
|
||||||
<div class="flex space-x-5 mt-10">
|
<div class="flex space-x-5 mt-10">
|
||||||
<button type="button" mat-button matButton="outlined" (click)="stepper.reset()" class="w-1/3">Zurück</button>
|
<button type="button" mat-button matButton="outlined" (click)="stepper.reset()" class="w-1/3" [disabled]="isSubmitting">Zurück</button>
|
||||||
<button type="submit" mat-button matButton="filled" matStepperNext (click)="stupidCheckboxWorkaround()" class="w-2/3">Sitzplätze reservieren</button>
|
<button type="submit" mat-button matButton="filled" (click)="nextPhaseButtonClicked(stepper)" class="w-2/3" [disabled]="isSubmitting">{{ secondPhaseButtonText }}</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
</mat-step>
|
</mat-step>
|
||||||
<mat-step [stepControl]="paymentForm">
|
<mat-step [stepControl]="paymentForm">
|
||||||
@@ -93,6 +116,16 @@
|
|||||||
|
|
||||||
<div class="performance-info-space"></div>
|
<div class="performance-info-space"></div>
|
||||||
|
|
||||||
|
@if (seatsPurchased && !isSubmitting) {
|
||||||
|
<div class="h-4"></div>
|
||||||
|
@if (successful) {
|
||||||
|
<app-purchase-success [tickets]="createdTickets"></app-purchase-success>
|
||||||
|
} @else {
|
||||||
|
<app-purchase-failed></app-purchase-failed>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@else {
|
||||||
|
|
||||||
<!-- Card Number -->
|
<!-- Card Number -->
|
||||||
<mat-form-field class="w-full mt-8">
|
<mat-form-field class="w-full mt-8">
|
||||||
<mat-label>Kartennummer</mat-label>
|
<mat-label>Kartennummer</mat-label>
|
||||||
@@ -146,13 +179,14 @@
|
|||||||
|
|
||||||
<!-- Buttons -->
|
<!-- Buttons -->
|
||||||
<div class="flex space-x-4 mt-8">
|
<div class="flex space-x-4 mt-8">
|
||||||
<button mat-stroked-button color="primary" matStepperPrevious type="button" class="w-1/3">
|
<button mat-stroked-button color="primary" matStepperPrevious type="button" [disabled]="isSubmitting" class="w-1/3">
|
||||||
Zurück
|
Zurück
|
||||||
</button>
|
</button>
|
||||||
<button mat-flat-button color="accent" class="w-2/3" matStepperNext type="submit">
|
<button mat-flat-button color="accent" matStepperNext type="submit" [disabled]="isSubmitting" (click)="makePurchase()" class="w-2/3">
|
||||||
{{ getPriceDisplay(totalPrice()) }} jetzt bezahlen
|
{{ getPriceDisplay(totalPrice()) }} jetzt bezahlen
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
</form>
|
</form>
|
||||||
</mat-step>
|
</mat-step>
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
import { SelectedSeatsService } from './../selected-seats.service';
|
import { SelectedSeatsService } from './../selected-seats.service';
|
||||||
import { LoadingService } from './../loading.service';
|
import { LoadingService } from './../loading.service';
|
||||||
import { Sitzkategorie, Vorstellung } from '@infinimotion/model-frontend';
|
import { Bestellung, Eintrittskarte, Sitzkategorie, Sitzplatz, Vorstellung } from '@infinimotion/model-frontend';
|
||||||
import { Component, computed, inject, input } from '@angular/core';
|
import { Component, computed, inject, input } from '@angular/core';
|
||||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||||
import { StepperSelectionEvent } from '@angular/cdk/stepper';
|
import { StepperSelectionEvent } from '@angular/cdk/stepper';
|
||||||
|
import { HttpService } from '../http.service';
|
||||||
|
import { catchError, tap, finalize } from 'rxjs';
|
||||||
|
import { MatStepper } from '@angular/material/stepper';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-order',
|
selector: 'app-order',
|
||||||
@@ -17,8 +20,17 @@ export class OrderComponent {
|
|||||||
|
|
||||||
submitted = false;
|
submitted = false;
|
||||||
|
|
||||||
|
performance = input<Vorstellung>();
|
||||||
|
seatCategories = input.required<Sitzkategorie[]>();
|
||||||
|
|
||||||
|
loadingService = inject(LoadingService);
|
||||||
|
private httpService = inject(HttpService)
|
||||||
|
private selectedSeatsService = inject(SelectedSeatsService);
|
||||||
|
|
||||||
constructor(private fb: FormBuilder) {}
|
constructor(private fb: FormBuilder) {}
|
||||||
|
|
||||||
|
// Form-Validation
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.paymentForm = this.fb.group({
|
this.paymentForm = this.fb.group({
|
||||||
cardNumber: ['', [Validators.required, Validators.pattern(/^\d{16}$/)]],
|
cardNumber: ['', [Validators.required, Validators.pattern(/^\d{16}$/)]],
|
||||||
@@ -37,23 +49,31 @@ export class OrderComponent {
|
|||||||
get fPayment() { return this.paymentForm.controls; }
|
get fPayment() { return this.paymentForm.controls; }
|
||||||
|
|
||||||
onSubmit() {
|
onSubmit() {
|
||||||
|
if (this.dataForm.invalid) return;
|
||||||
if (this.paymentForm.invalid) return;
|
if (this.paymentForm.invalid) return;
|
||||||
console.log('Zahlungsdaten:', this.paymentForm.value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onStepChange(event: StepperSelectionEvent) {
|
onStepChange(event: StepperSelectionEvent) {
|
||||||
this.submitted = false;
|
this.submitted = false;
|
||||||
|
|
||||||
|
if(event.selectedIndex != 0) {
|
||||||
|
this.selectedSeatsService.setSeatIsSelectableFalse()
|
||||||
|
} else {
|
||||||
|
this.selectedSeatsService.setSeatIsSelectableTrue()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
stupidCheckboxWorkaround() {
|
nextPhaseButtonClicked(stepper: MatStepper) {
|
||||||
this.submitted = true;
|
this.submitted = true;
|
||||||
|
if (this.dataForm.invalid) return;
|
||||||
|
|
||||||
|
if (this.submissionMode === "reservation") {
|
||||||
|
this.makeReservation();
|
||||||
|
} else if (this.submissionMode === "purchase") {
|
||||||
|
stepper.next();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
performance = input<Vorstellung>();
|
|
||||||
seatCategories = input.required<Sitzkategorie[]>();
|
|
||||||
|
|
||||||
loadingService = inject(LoadingService);
|
|
||||||
private selectedSeatsService = inject(SelectedSeatsService);
|
|
||||||
|
|
||||||
totalPrice = computed(() =>
|
totalPrice = computed(() =>
|
||||||
this.selectedSeatsService.getSelectedSeatsList().reduce((sum, seat) => sum + seat.row.category.price, 0)
|
this.selectedSeatsService.getSelectedSeatsList().reduce((sum, seat) => sum + seat.row.category.price, 0)
|
||||||
@@ -67,4 +87,138 @@ export class OrderComponent {
|
|||||||
return `${(price / 100).toFixed(2)} €`;
|
return `${(price / 100).toFixed(2)} €`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isSubmitting: boolean = false;
|
||||||
|
secondPhaseButtonText: string = "Loading..."
|
||||||
|
submissionMode!: 'reservation' | 'purchase';
|
||||||
|
|
||||||
|
seatsReserved: boolean = false;
|
||||||
|
seatsPurchased: boolean = false;
|
||||||
|
successful: boolean = false;
|
||||||
|
|
||||||
|
reservationClicked() {
|
||||||
|
this.submissionMode = "reservation";
|
||||||
|
if (this.totalSeats() > 1) {
|
||||||
|
this.secondPhaseButtonText = "Sitzplätze reservieren"
|
||||||
|
} else {
|
||||||
|
this.secondPhaseButtonText = "Sitzplatz reservieren"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
purchaseClicked() {
|
||||||
|
this.submissionMode = "purchase"
|
||||||
|
this.secondPhaseButtonText = "Weiter zur Zahlung"
|
||||||
|
}
|
||||||
|
|
||||||
|
makeReservation() {
|
||||||
|
this.loadingService.show();
|
||||||
|
this.disableInputs()
|
||||||
|
|
||||||
|
const order = this.generateNewOrderObject(this.dataForm.value.email, false);
|
||||||
|
const seats = this.selectedSeatsService.getSelectedSeatsList();
|
||||||
|
const performance = this.performance()!;
|
||||||
|
this.successful = true;
|
||||||
|
this.sendToBackend(order, seats, performance);
|
||||||
|
this.seatsReserved = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
makePurchase() {
|
||||||
|
this.loadingService.show();
|
||||||
|
this.disableInputs()
|
||||||
|
|
||||||
|
const order = this.generateNewOrderObject(this.dataForm.value.email, true);
|
||||||
|
const seats = this.selectedSeatsService.getSelectedSeatsList();
|
||||||
|
const performance = this.performance()!;
|
||||||
|
this.successful = true;
|
||||||
|
this.sendToBackend(order, seats, performance);
|
||||||
|
this.seatsPurchased = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
createdOrder!: Bestellung;
|
||||||
|
createdTickets!: Eintrittskarte[];
|
||||||
|
|
||||||
|
sendToBackend(order: Bestellung, seats: Sitzplatz[], performance: Vorstellung) {
|
||||||
|
this.httpService.addOrder(order).pipe(
|
||||||
|
tap(createdOrder => {
|
||||||
|
this.createdOrder = createdOrder;
|
||||||
|
|
||||||
|
const ticketCreations = seats.map(seat => {
|
||||||
|
const ticket = this.generateNewTicketObject(performance, seat, createdOrder);
|
||||||
|
return this.httpService.addTicket(ticket);
|
||||||
|
});
|
||||||
|
|
||||||
|
Promise.all(ticketCreations.map(obs => obs.toPromise()))
|
||||||
|
.then(createdTickets => {
|
||||||
|
this.createdTickets = createdTickets.filter(
|
||||||
|
(ticket): ticket is Eintrittskarte => ticket !== undefined
|
||||||
|
);
|
||||||
|
|
||||||
|
this.loadingService.hide();
|
||||||
|
this.enableInputs();
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
this.loadingService.showError(err);
|
||||||
|
this.successful = false;
|
||||||
|
console.error('Fehler beim Anlegen der Eintrittskarten', err);
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
catchError(err => {
|
||||||
|
this.loadingService.showError(err);
|
||||||
|
this.successful = false;
|
||||||
|
console.error('Fehler beim Anlegen der Bestellung', err);
|
||||||
|
return [];
|
||||||
|
}),
|
||||||
|
finalize(() => {
|
||||||
|
this.enableInputs();
|
||||||
|
})
|
||||||
|
).subscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private generateCode(length: number = 6): string {
|
||||||
|
const chars = "ABCDEFGHJKLMNPQRSUVWXYZ23456789";
|
||||||
|
let result = "";
|
||||||
|
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
const randomIndex = Math.floor(Math.random() * chars.length);
|
||||||
|
result += chars[randomIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private generateNewOrderObject(mail: string, isBooked: boolean): Bestellung {
|
||||||
|
return{
|
||||||
|
id: 0, // Wird durch Backend gesetzt
|
||||||
|
mail: mail,
|
||||||
|
code: this.generateCode(length=6),
|
||||||
|
reserved: new Date(),
|
||||||
|
booked: isBooked ? new Date() : null,
|
||||||
|
cancelled: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private generateNewTicketObject(show: Vorstellung, seat: Sitzplatz, order: Bestellung): Eintrittskarte {
|
||||||
|
return {
|
||||||
|
id: 0, // Wird durch Backend gesetzt
|
||||||
|
code: 'T' + this.generateCode(length=7),
|
||||||
|
show: show,
|
||||||
|
seat: seat,
|
||||||
|
order: order
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private disableInputs() {
|
||||||
|
this.dataForm.disable();
|
||||||
|
this.paymentForm.disable();
|
||||||
|
this.isSubmitting = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private enableInputs() {
|
||||||
|
this.dataForm.enable();
|
||||||
|
this.paymentForm.enable();
|
||||||
|
this.isSubmitting = false;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
11
src/app/purchase-failed/purchase-failed.component.html
Normal file
11
src/app/purchase-failed/purchase-failed.component.html
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<div class="bg-red-100 text-red-500 rounded-md shadow-sm w-full h-fit p-6 py-8 items-center justify-center flex flex-col space-y-2">
|
||||||
|
<mat-icon class="material-symbols-outlined mb-5" style="font-size: 50px; width: 50px; height: 50px">
|
||||||
|
warning
|
||||||
|
</mat-icon>
|
||||||
|
<h1 class="text-xl font-bold">Kauf fehlgeschlagen!</h1>
|
||||||
|
<p class="text-center">Leider konnten Ihre Sitzplätze nicht gebucht werden.<br>Dies kann passieren, wenn andere Nutzer gleichzeitig versucht haben, dieselben Sitzplätze zu kaufen.</p>
|
||||||
|
|
||||||
|
<button mat-button matButton="filled" class="error-button mt-4 w-80">Erneut versuchen</button>
|
||||||
|
<button routerLink="/schedule" mat-button matButton="outlined" color="accent" class="error-button w-80">Zurück zur Programmauswahl</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
11
src/app/purchase-failed/purchase-failed.component.ts
Normal file
11
src/app/purchase-failed/purchase-failed.component.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-purchase-failed',
|
||||||
|
standalone: false,
|
||||||
|
templateUrl: './purchase-failed.component.html',
|
||||||
|
styleUrl: './purchase-failed.component.css',
|
||||||
|
})
|
||||||
|
export class PurchaseFailedComponent {
|
||||||
|
|
||||||
|
}
|
||||||
11
src/app/purchase-success/purchase-success.component.html
Normal file
11
src/app/purchase-success/purchase-success.component.html
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<div class="bg-green-200 rounded-md shadow-sm w-full h-fit p-6 py-8 items-center justify-center flex flex-col space-y-2">
|
||||||
|
<h1 class="text-xl font-bold">Vielen Dank für Ihren Einkauf!</h1>
|
||||||
|
<p class="text-center">Ihre Sitzplätze wurden erfolgreich gebucht.</p>
|
||||||
|
|
||||||
|
<app-ticket-list [tickets]="tickets()" class="w-8/10"></app-ticket-list>
|
||||||
|
|
||||||
|
<button mat-button disabled="true" matButton="filled" class="success-button w-80 mt-2">Tickets herunterladen</button>
|
||||||
|
<button routerLink="/schedule" mat-button matButton="outlined" color="accent" class="success-button w-80">Zurück zur Programmauswahl</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
12
src/app/purchase-success/purchase-success.component.ts
Normal file
12
src/app/purchase-success/purchase-success.component.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { Eintrittskarte } from '@infinimotion/model-frontend';
|
||||||
|
import { Component, input } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-purchase-success',
|
||||||
|
standalone: false,
|
||||||
|
templateUrl: './purchase-success.component.html',
|
||||||
|
styleUrl: './purchase-success.component.css',
|
||||||
|
})
|
||||||
|
export class PurchaseSuccessComponent {
|
||||||
|
tickets = input.required<Eintrittskarte[]>();
|
||||||
|
}
|
||||||
11
src/app/reservation-failed/reservation-failed.component.html
Normal file
11
src/app/reservation-failed/reservation-failed.component.html
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<div class="bg-red-100 text-red-500 rounded-md shadow-sm w-full h-fit p-6 py-8 items-center justify-center flex flex-col space-y-2">
|
||||||
|
<mat-icon class="material-symbols-outlined mb-5" style="font-size: 50px; width: 50px; height: 50px">
|
||||||
|
warning
|
||||||
|
</mat-icon>
|
||||||
|
<h1 class="text-xl font-bold">Reservierung fehlgeschlagen!</h1>
|
||||||
|
<p class="text-center">Leider konnten Ihre Sitzplätze nicht reserviert werden.<br>Dies kann passieren, wenn andere Nutzer gleichzeitig versucht haben, dieselben Sitzplätze zu reservieren.</p>
|
||||||
|
|
||||||
|
<button mat-button matButton="filled" class="error-button mt-4 w-80">Erneut versuchen</button>
|
||||||
|
<button routerLink="/schedule" mat-button matButton="outlined" color="accent" class="error-button w-80">Zurück zur Programmauswahl</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
11
src/app/reservation-failed/reservation-failed.component.ts
Normal file
11
src/app/reservation-failed/reservation-failed.component.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-reservation-failed',
|
||||||
|
standalone: false,
|
||||||
|
templateUrl: './reservation-failed.component.html',
|
||||||
|
styleUrl: './reservation-failed.component.css',
|
||||||
|
})
|
||||||
|
export class ReservationFailedComponent {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<div class="bg-green-200 rounded-md shadow-sm w-full h-fit p-6 py-8 items-center justify-center flex flex-col space-y-2">
|
||||||
|
<h1 class="text-xl font-bold">Reservierung erfolgreich!</h1>
|
||||||
|
<!-- <p class="text-center">Ihre Sitzplätze wurden reserviert. </p> -->
|
||||||
|
|
||||||
|
<p class="text-center">Ihre Sitzplätze wurden erfolgreich reserviert. Bitte nennen sie den folgenden Code an der Kasse, um Ihre Reservierung in eine Buchung umzuwandeln.</p>
|
||||||
|
<div class="bg-white text-5xl font-mono rounded-md shadow-sm w-fit h-fit p-4 py-2 my-4">
|
||||||
|
<strong>{{ order().code }}</strong>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button routerLink="/schedule" mat-button matButton="filled" class="success-button mt-4 w-80">Zurück zur Programmauswahl</button>
|
||||||
|
<button [disabled]="true" mat-button matButton="outlined" color="accent" class="success-button w-80">Tickets jetzt online bezahlen</button>
|
||||||
|
<div class="text-green-500 cursor-pointer w-fit mt-1" (click)="cancelReservation()">
|
||||||
|
Reservierung stornieren
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
16
src/app/reservation-success/reservation-success.component.ts
Normal file
16
src/app/reservation-success/reservation-success.component.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { Bestellung } from '@infinimotion/model-frontend';
|
||||||
|
import { Component, input } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-reservation-success',
|
||||||
|
standalone: false,
|
||||||
|
templateUrl: './reservation-success.component.html',
|
||||||
|
styleUrl: './reservation-success.component.css',
|
||||||
|
})
|
||||||
|
export class ReservationSuccessComponent {
|
||||||
|
order = input.required<Bestellung>();
|
||||||
|
|
||||||
|
cancelReservation() {
|
||||||
|
// Logic to cancel the reservation
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
@keyframes blink {
|
||||||
|
0% {
|
||||||
|
color: #6366f1;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
color: #ffde05;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
color: #6366f1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.blink {
|
||||||
|
animation: blink 1s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
<button (click)="updateSelectedSeats(this.seat())" [disabled]="state() == TheaterSeatState.BOOKED || state() == TheaterSeatState.RESERVED || !seatService.getSeatIsSelected()" class="mx-0.5 hover:opacity-50">
|
<button (click)="updateSelectedSeats(this.seat())" [disabled]="state() == TheaterSeatState.BOOKED || state() == TheaterSeatState.RESERVED || !seatService.getSeatIsSelectable()" class="mx-0.5">
|
||||||
<mat-icon [ngStyle]="{color: isSelectedAndAvaliable() ? '#6366f1': getSeatStateColor() }" style="font-size: 30px; width: 30px; height: 30px">
|
<mat-icon
|
||||||
|
[class]="isHoverable()? 'hover:opacity-50' : ''"
|
||||||
|
[ngStyle]="{color: isSelectedAndAvaliable() ? '#6366f1': getSeatStateColor() }"
|
||||||
|
[style]="!seatService.getSeatIsSelectable()? 'transition: color 0.5s ease, transform 0.3s ease-in-out;' : ''"
|
||||||
|
style="font-size: 30px; width: 30px; height: 30px;">
|
||||||
{{ seat().row.category.icon }}
|
{{ seat().row.category.icon }}
|
||||||
</mat-icon>
|
</mat-icon>
|
||||||
|
<!-- [ngClass]="{'blink': isSelectedAndAvaliable() && !seatService.getSeatIsSelectable()}" -->
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
|||||||
@@ -19,17 +19,29 @@ export class SeatComponent{
|
|||||||
protected readonly TheaterSeatState = TheaterSeatState;
|
protected readonly TheaterSeatState = TheaterSeatState;
|
||||||
|
|
||||||
getSeatStateColor(): string {
|
getSeatStateColor(): string {
|
||||||
|
if (!this.seatService.getSeatIsSelectable()) return 'gray'
|
||||||
switch (this.state()) {
|
switch (this.state()) {
|
||||||
case TheaterSeatState.RESERVED:
|
case TheaterSeatState.RESERVED:
|
||||||
return 'orange';
|
return '#d6c9a9';
|
||||||
case TheaterSeatState.BOOKED:
|
case TheaterSeatState.BOOKED:
|
||||||
return 'red';
|
return '#d9abab';
|
||||||
default:
|
default:
|
||||||
case TheaterSeatState.AVAILABLE:
|
case TheaterSeatState.AVAILABLE:
|
||||||
return 'black';
|
return 'black';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isHoverable(): boolean {
|
||||||
|
switch (this.state()) {
|
||||||
|
default:
|
||||||
|
case TheaterSeatState.AVAILABLE:
|
||||||
|
return this.seatService.getSeatIsSelectable();
|
||||||
|
case TheaterSeatState.RESERVED:
|
||||||
|
case TheaterSeatState.BOOKED:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
updateSelectedSeats(selectedSeat: Sitzplatz) : void {
|
updateSelectedSeats(selectedSeat: Sitzplatz) : void {
|
||||||
if(!this.selected){
|
if(!this.selected){
|
||||||
this.seatService.pushSelectedSeat(selectedSeat);
|
this.seatService.pushSelectedSeat(selectedSeat);
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export class SelectedSeatsService {
|
|||||||
this.selectedSeatsSignal.set([]);
|
this.selectedSeatsSignal.set([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
getSeatIsSelected(): boolean{
|
getSeatIsSelectable(): boolean{
|
||||||
return this.seatIsSelectable;
|
return this.seatIsSelectable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ export class TheaterOverlayComponent implements OnInit {
|
|||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.showId = Number(this.route.snapshot.paramMap.get('id')!);
|
this.showId = Number(this.route.snapshot.paramMap.get('id')!);
|
||||||
this.selectedSeatService.clearSelectedSeatsList();
|
this.selectedSeatService.clearSelectedSeatsList();
|
||||||
|
this.selectedSeatService.setSeatIsSelectableTrue();
|
||||||
this.loadPerformanceAndSeats();
|
this.loadPerformanceAndSeats();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
41
src/app/ticket-list/ticket-list.component.css
Normal file
41
src/app/ticket-list/ticket-list.component.css
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
.ticket-container {
|
||||||
|
max-height: 200px;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 8px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ticket-container::before,
|
||||||
|
.ticket-container::after {
|
||||||
|
content: '';
|
||||||
|
position: sticky;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 20px;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ticket-container::before {
|
||||||
|
top: 0;
|
||||||
|
background: linear-gradient(to bottom, rgba(0,0,0,0.1), transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ticket-container::after {
|
||||||
|
bottom: 0;
|
||||||
|
background: linear-gradient(to top, rgba(0,0,0,0.1), transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.ticket-container::-webkit-scrollbar {
|
||||||
|
width: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ticket-container::-webkit-scrollbar-track {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ticket-container::-webkit-scrollbar-thumb {
|
||||||
|
background-color: rgba(0,0,0,0.2);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
9
src/app/ticket-list/ticket-list.component.html
Normal file
9
src/app/ticket-list/ticket-list.component.html
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<div class="ticket-container">
|
||||||
|
@for (ticket of tickets(); track $index) {
|
||||||
|
<app-ticket-small [ticket]="ticket"></app-ticket-small>
|
||||||
|
@if ($index + 1 != tickets().length) {
|
||||||
|
<div class="h-2"></div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
12
src/app/ticket-list/ticket-list.component.ts
Normal file
12
src/app/ticket-list/ticket-list.component.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { Component, input } from '@angular/core';
|
||||||
|
import { Eintrittskarte } from '@infinimotion/model-frontend';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-ticket-list',
|
||||||
|
standalone: false,
|
||||||
|
templateUrl: './ticket-list.component.html',
|
||||||
|
styleUrl: './ticket-list.component.css',
|
||||||
|
})
|
||||||
|
export class TicketListComponent {
|
||||||
|
tickets = input.required<Eintrittskarte[]>();
|
||||||
|
}
|
||||||
0
src/app/ticket-small/ticket-small.component.css
Normal file
0
src/app/ticket-small/ticket-small.component.css
Normal file
16
src/app/ticket-small/ticket-small.component.html
Normal file
16
src/app/ticket-small/ticket-small.component.html
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<div class="bg-white rounded-md shadow-sm w-full h-fit p-1 flex space-x-1 justify-between items-center">
|
||||||
|
<mat-icon class="opacity-50" style="font-size: 40px; width: 40px; height: 40px">
|
||||||
|
local_activity
|
||||||
|
</mat-icon>
|
||||||
|
<div class="flex flex-col flex-1 text-sm leading-tight ml-1">
|
||||||
|
<p>{{ticket().seat.row.category.name}} • Reihe {{convertIntoRowName(ticket().seat.row.position)}} Platz {{ticket().seat.position}}</p>
|
||||||
|
<div class="flex items-center space-x-2">
|
||||||
|
<p>Ticketcode:</p>
|
||||||
|
<p class="font-mono">
|
||||||
|
<strong>{{ ticket().code }}</strong>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<qrcode [qrdata]="ticket().code" [width]="42" [errorCorrectionLevel]="'M'"></qrcode>
|
||||||
|
</div>
|
||||||
|
|
||||||
16
src/app/ticket-small/ticket-small.component.ts
Normal file
16
src/app/ticket-small/ticket-small.component.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { Eintrittskarte } from '@infinimotion/model-frontend';
|
||||||
|
import { Component, input } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-ticket-small',
|
||||||
|
standalone: false,
|
||||||
|
templateUrl: './ticket-small.component.html',
|
||||||
|
styleUrl: './ticket-small.component.css',
|
||||||
|
})
|
||||||
|
export class TicketSmallComponent {
|
||||||
|
ticket = input.required<Eintrittskarte>();
|
||||||
|
|
||||||
|
convertIntoRowName(n: number): string {
|
||||||
|
return String.fromCharCode(64 + n);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -66,6 +66,47 @@ html.dark {
|
|||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.error-button {
|
||||||
|
@include mat.button-overrides((
|
||||||
|
outlined-ripple-color: rgba(255, 0, 0, 0.1),
|
||||||
|
));
|
||||||
|
|
||||||
|
&.mdc-button:not(.mat-mdc-outlined-button):not(:disabled) {
|
||||||
|
background-color: var(--color-red-500) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.mdc-button.mat-mdc-outlined-button:not(:disabled) {
|
||||||
|
color: var(--color-red-500) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.mdc-button.mat-mdc-outlined-button .mat-mdc-button-persistent-ripple::before {
|
||||||
|
background-color: var(--color-red-500) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.success-button {
|
||||||
|
@include mat.button-overrides((
|
||||||
|
outlined-ripple-color: rgba(0, 255, 0, 0.1),
|
||||||
|
));
|
||||||
|
|
||||||
|
&.mdc-button:not(.mat-mdc-outlined-button):not(:disabled) {
|
||||||
|
background-color: var(--color-green-500) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.mdc-button.mat-mdc-outlined-button:not(:disabled) {
|
||||||
|
color: var(--mat-sys-on-surface) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.mdc-button.mat-mdc-outlined-button .mat-mdc-button-persistent-ripple::before {
|
||||||
|
background-color: var(--color-green-500) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
body {
|
body {
|
||||||
// Default the application to a light color theme. This can be changed to
|
// Default the application to a light color theme. This can be changed to
|
||||||
// `dark` to enable the dark color theme, or to `light dark` to defer to the
|
// `dark` to enable the dark color theme, or to `light dark` to defer to the
|
||||||
|
|||||||
Reference in New Issue
Block a user