Merge branch 'order' into 'main'
This commit is contained in:
51
package-lock.json
generated
51
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.85",
|
"@infinimotion/model-frontend": "^0.0.89",
|
||||||
"@tailwindcss/postcss": "^4.1.14",
|
"@tailwindcss/postcss": "^4.1.14",
|
||||||
|
"ngx-mask": "^20.0.3",
|
||||||
"postcss": "^8.5.6",
|
"postcss": "^8.5.6",
|
||||||
"rxjs": "~7.8.0",
|
"rxjs": "~7.8.0",
|
||||||
"tailwindcss": "^4.1.14",
|
"tailwindcss": "^4.1.14",
|
||||||
@@ -440,6 +441,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-20.2.12.tgz",
|
"resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-20.2.12.tgz",
|
||||||
"integrity": "sha512-hz8GtiMy3N9/e8407ZfrByHD5GEC4SkWtxyUknWuTM9P88AOie0jDZ6CfQg9gQ0OJX+6BAbJV3RpYZA1uzNUqA==",
|
"integrity": "sha512-hz8GtiMy3N9/e8407ZfrByHD5GEC4SkWtxyUknWuTM9P88AOie0jDZ6CfQg9gQ0OJX+6BAbJV3RpYZA1uzNUqA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"parse5": "^8.0.0",
|
"parse5": "^8.0.0",
|
||||||
"tslib": "^2.3.0"
|
"tslib": "^2.3.0"
|
||||||
@@ -490,6 +492,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@angular/common/-/common-20.3.10.tgz",
|
"resolved": "https://registry.npmjs.org/@angular/common/-/common-20.3.10.tgz",
|
||||||
"integrity": "sha512-12fEzvKbEqjqy1fSk9DMYlJz6dF1MJVXuC5BB+oWWJpd+2lfh4xJ62pkvvLGAICI89hfM5n9Cy5kWnXwnqPZsA==",
|
"integrity": "sha512-12fEzvKbEqjqy1fSk9DMYlJz6dF1MJVXuC5BB+oWWJpd+2lfh4xJ62pkvvLGAICI89hfM5n9Cy5kWnXwnqPZsA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": "^2.3.0"
|
"tslib": "^2.3.0"
|
||||||
},
|
},
|
||||||
@@ -506,6 +509,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-20.3.10.tgz",
|
"resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-20.3.10.tgz",
|
||||||
"integrity": "sha512-cW939Lr8GZjPSYfbQKIDNrUaHWmn2M+zBbERThfq5skLuY+xM60bJFv4NqBekfX6YqKLCY62ilUZlnImYIXaqA==",
|
"integrity": "sha512-cW939Lr8GZjPSYfbQKIDNrUaHWmn2M+zBbERThfq5skLuY+xM60bJFv4NqBekfX6YqKLCY62ilUZlnImYIXaqA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": "^2.3.0"
|
"tslib": "^2.3.0"
|
||||||
},
|
},
|
||||||
@@ -519,6 +523,7 @@
|
|||||||
"integrity": "sha512-9BemvpFxA26yIVdu8ROffadMkEdlk/AQQ2Jb486w7RPkrvUQ0pbEJukhv9aryJvhbMopT66S5H/j4ipOUMzmzQ==",
|
"integrity": "sha512-9BemvpFxA26yIVdu8ROffadMkEdlk/AQQ2Jb486w7RPkrvUQ0pbEJukhv9aryJvhbMopT66S5H/j4ipOUMzmzQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/core": "7.28.3",
|
"@babel/core": "7.28.3",
|
||||||
"@jridgewell/sourcemap-codec": "^1.4.14",
|
"@jridgewell/sourcemap-codec": "^1.4.14",
|
||||||
@@ -551,6 +556,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@angular/core/-/core-20.3.10.tgz",
|
"resolved": "https://registry.npmjs.org/@angular/core/-/core-20.3.10.tgz",
|
||||||
"integrity": "sha512-g99Qe+NOVo72OLxowVF9NjCckswWYHmvO7MgeiZTDJbTjF9tXH96dMx7AWq76/GUinV10sNzDysVW16NoAbCRQ==",
|
"integrity": "sha512-g99Qe+NOVo72OLxowVF9NjCckswWYHmvO7MgeiZTDJbTjF9tXH96dMx7AWq76/GUinV10sNzDysVW16NoAbCRQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": "^2.3.0"
|
"tslib": "^2.3.0"
|
||||||
},
|
},
|
||||||
@@ -576,6 +582,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@angular/forms/-/forms-20.3.10.tgz",
|
"resolved": "https://registry.npmjs.org/@angular/forms/-/forms-20.3.10.tgz",
|
||||||
"integrity": "sha512-9yWr51EUauTEINB745AaHwZNTHLpXIm4uxuykxzOg+g2QskEgVfH26uS8G2ogdNuwYpB8wnsXWr34qhM3qgOWw==",
|
"integrity": "sha512-9yWr51EUauTEINB745AaHwZNTHLpXIm4uxuykxzOg+g2QskEgVfH26uS8G2ogdNuwYpB8wnsXWr34qhM3qgOWw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": "^2.3.0"
|
"tslib": "^2.3.0"
|
||||||
},
|
},
|
||||||
@@ -611,6 +618,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-20.3.10.tgz",
|
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-20.3.10.tgz",
|
||||||
"integrity": "sha512-UV8CGoB5P3FmJciI3/I/n3L7C3NVgGh7bIlZ1BaB/qJDtv0Wq0rRAGwmT/Z3gwmrRtfHZWme7/CeQ2CYJmMyUQ==",
|
"integrity": "sha512-UV8CGoB5P3FmJciI3/I/n3L7C3NVgGh7bIlZ1BaB/qJDtv0Wq0rRAGwmT/Z3gwmrRtfHZWme7/CeQ2CYJmMyUQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": "^2.3.0"
|
"tslib": "^2.3.0"
|
||||||
},
|
},
|
||||||
@@ -677,6 +685,7 @@
|
|||||||
"integrity": "sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==",
|
"integrity": "sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ampproject/remapping": "^2.2.0",
|
"@ampproject/remapping": "^2.2.0",
|
||||||
"@babel/code-frame": "^7.27.1",
|
"@babel/code-frame": "^7.27.1",
|
||||||
@@ -1392,9 +1401,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@infinimotion/model-frontend": {
|
"node_modules/@infinimotion/model-frontend": {
|
||||||
"version": "0.0.85",
|
"version": "0.0.89",
|
||||||
"resolved": "https://git.infinimotion.de/api/packages/infinimotion/npm/%40infinimotion%2Fmodel-frontend/-/0.0.85/model-frontend-0.0.85.tgz",
|
"resolved": "https://git.infinimotion.de/api/packages/infinimotion/npm/%40infinimotion%2Fmodel-frontend/-/0.0.89/model-frontend-0.0.89.tgz",
|
||||||
"integrity": "sha512-QPiZNl//Y1JdxtXk+VScc67h1K664z68PUCXRff9fRf4IHlYXtqutc+ainK8vxOVSqqL6EEmDAtbLsRwrG6kRg==",
|
"integrity": "sha512-lvvQy8RWs41Bz52uBgsUKkwn8teGlgxlmG8Rvsgkh+v1IMVWFWVQmfMS7Rznd0lCZRgK1ByihH80X9eAN12idA==",
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/@inquirer/ansi": {
|
"node_modules/@inquirer/ansi": {
|
||||||
@@ -1633,6 +1642,7 @@
|
|||||||
"integrity": "sha512-nqhDw2ZcAUrKNPwhjinJny903bRhI0rQhiDz1LksjeRxqa36i3l75+4iXbOy0rlDpLJGxqtgoPavQjmmyS5UJw==",
|
"integrity": "sha512-nqhDw2ZcAUrKNPwhjinJny903bRhI0rQhiDz1LksjeRxqa36i3l75+4iXbOy0rlDpLJGxqtgoPavQjmmyS5UJw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@inquirer/checkbox": "^4.2.1",
|
"@inquirer/checkbox": "^4.2.1",
|
||||||
"@inquirer/confirm": "^5.1.14",
|
"@inquirer/confirm": "^5.1.14",
|
||||||
@@ -3865,6 +3875,7 @@
|
|||||||
"integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==",
|
"integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"undici-types": "~7.16.0"
|
"undici-types": "~7.16.0"
|
||||||
}
|
}
|
||||||
@@ -4198,6 +4209,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"baseline-browser-mapping": "^2.8.25",
|
"baseline-browser-mapping": "^2.8.25",
|
||||||
"caniuse-lite": "^1.0.30001754",
|
"caniuse-lite": "^1.0.30001754",
|
||||||
@@ -5294,6 +5306,7 @@
|
|||||||
"integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==",
|
"integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"accepts": "^2.0.0",
|
"accepts": "^2.0.0",
|
||||||
"body-parser": "^2.2.0",
|
"body-parser": "^2.2.0",
|
||||||
@@ -6245,7 +6258,8 @@
|
|||||||
"resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-5.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-5.9.0.tgz",
|
||||||
"integrity": "sha512-OMUvF1iI6+gSRYOhMrH4QYothVLN9C3EJ6wm4g7zLJlnaTl8zbaPOr0bTw70l7QxkoM7sVFOWo83u9B2Fe2Zng==",
|
"integrity": "sha512-OMUvF1iI6+gSRYOhMrH4QYothVLN9C3EJ6wm4g7zLJlnaTl8zbaPOr0bTw70l7QxkoM7sVFOWo83u9B2Fe2Zng==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/jiti": {
|
"node_modules/jiti": {
|
||||||
"version": "2.6.1",
|
"version": "2.6.1",
|
||||||
@@ -6339,6 +6353,7 @@
|
|||||||
"integrity": "sha512-LrtUxbdvt1gOpo3gxG+VAJlJAEMhbWlM4YrFQgql98FwF7+K8K12LYO4hnDdUkNjeztYrOXEMqgTajSWgmtI/w==",
|
"integrity": "sha512-LrtUxbdvt1gOpo3gxG+VAJlJAEMhbWlM4YrFQgql98FwF7+K8K12LYO4hnDdUkNjeztYrOXEMqgTajSWgmtI/w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@colors/colors": "1.5.0",
|
"@colors/colors": "1.5.0",
|
||||||
"body-parser": "^1.19.0",
|
"body-parser": "^1.19.0",
|
||||||
@@ -7055,6 +7070,7 @@
|
|||||||
"integrity": "sha512-SL0JY3DaxylDuo/MecFeiC+7pedM0zia33zl0vcjgwcq1q1FWWF1To9EIauPbl8GbMCU0R2e0uJ8bZunhYKD2g==",
|
"integrity": "sha512-SL0JY3DaxylDuo/MecFeiC+7pedM0zia33zl0vcjgwcq1q1FWWF1To9EIauPbl8GbMCU0R2e0uJ8bZunhYKD2g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cli-truncate": "^4.0.0",
|
"cli-truncate": "^4.0.0",
|
||||||
"colorette": "^2.0.20",
|
"colorette": "^2.0.20",
|
||||||
@@ -7693,6 +7709,20 @@
|
|||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/ngx-mask": {
|
||||||
|
"version": "20.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/ngx-mask/-/ngx-mask-20.0.3.tgz",
|
||||||
|
"integrity": "sha512-5bmrgbFGudj0mFN6cPv/TI+cFJxT4l61mLIFskdvaXsJL/Oj7thRmWYqvqHXjCboOcx8gT6T/Zypl5u9l2J8Jg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.3.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@angular/common": ">=14.0.0",
|
||||||
|
"@angular/core": ">=14.0.0",
|
||||||
|
"@angular/forms": ">=14.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/node-addon-api": {
|
"node_modules/node-addon-api": {
|
||||||
"version": "6.1.0",
|
"version": "6.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz",
|
||||||
@@ -8712,6 +8742,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz",
|
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz",
|
||||||
"integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==",
|
"integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": "^2.1.0"
|
"tslib": "^2.1.0"
|
||||||
}
|
}
|
||||||
@@ -8768,6 +8799,7 @@
|
|||||||
"integrity": "sha512-9GUyuksjw70uNpb1MTYWsH9MQHOHY6kwfnkafC24+7aOMZn9+rVMBxRbLvw756mrBFbIsFg6Xw9IkR2Fnn3k+Q==",
|
"integrity": "sha512-9GUyuksjw70uNpb1MTYWsH9MQHOHY6kwfnkafC24+7aOMZn9+rVMBxRbLvw756mrBFbIsFg6Xw9IkR2Fnn3k+Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chokidar": "^4.0.0",
|
"chokidar": "^4.0.0",
|
||||||
"immutable": "^5.0.2",
|
"immutable": "^5.0.2",
|
||||||
@@ -9616,7 +9648,8 @@
|
|||||||
"version": "2.8.1",
|
"version": "2.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||||
"license": "0BSD"
|
"license": "0BSD",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/tuf-js": {
|
"node_modules/tuf-js": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
@@ -9654,6 +9687,7 @@
|
|||||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
@@ -9840,6 +9874,7 @@
|
|||||||
"integrity": "sha512-uzcxnSDVjAopEUjljkWh8EIrg6tlzrjFUfMcR1EVsRDGwf/ccef0qQPRyOrROwhrTDaApueq+ja+KLPlzR/zdg==",
|
"integrity": "sha512-uzcxnSDVjAopEUjljkWh8EIrg6tlzrjFUfMcR1EVsRDGwf/ccef0qQPRyOrROwhrTDaApueq+ja+KLPlzR/zdg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.25.0",
|
"esbuild": "^0.25.0",
|
||||||
"fdir": "^6.5.0",
|
"fdir": "^6.5.0",
|
||||||
@@ -10243,6 +10278,7 @@
|
|||||||
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
|
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/colinhacks"
|
"url": "https://github.com/sponsors/colinhacks"
|
||||||
}
|
}
|
||||||
@@ -10261,7 +10297,8 @@
|
|||||||
"version": "0.15.1",
|
"version": "0.15.1",
|
||||||
"resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.15.1.tgz",
|
"resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.15.1.tgz",
|
||||||
"integrity": "sha512-XE96n56IQpJM7NAoXswY3XRLcWFW83xe0BiAOeMD7K5k5xecOeul3Qcpx6GqEeeHNkW5DWL5zOyTbEfB4eti8w==",
|
"integrity": "sha512-XE96n56IQpJM7NAoXswY3XRLcWFW83xe0BiAOeMD7K5k5xecOeul3Qcpx6GqEeeHNkW5DWL5zOyTbEfB4eti8w==",
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.85",
|
"@infinimotion/model-frontend": "^0.0.89",
|
||||||
"@tailwindcss/postcss": "^4.1.14",
|
"@tailwindcss/postcss": "^4.1.14",
|
||||||
|
"ngx-mask": "^20.0.3",
|
||||||
"postcss": "^8.5.6",
|
"postcss": "^8.5.6",
|
||||||
"rxjs": "~7.8.0",
|
"rxjs": "~7.8.0",
|
||||||
"tailwindcss": "^4.1.14",
|
"tailwindcss": "^4.1.14",
|
||||||
|
|||||||
137
src/_theme-colors.scss
Normal file
137
src/_theme-colors.scss
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
// This file was generated by running 'ng generate @angular/material:theme-color'.
|
||||||
|
// Proceed with caution if making changes to this file.
|
||||||
|
|
||||||
|
@use 'sass:map';
|
||||||
|
@use '@angular/material' as mat;
|
||||||
|
|
||||||
|
// Note: Color palettes are generated from primary: 6366f1, tertiary: dd2979
|
||||||
|
$_palettes: (
|
||||||
|
primary: (
|
||||||
|
0: #000000,
|
||||||
|
10: #07006c,
|
||||||
|
20: #1000a9,
|
||||||
|
25: #201cb4,
|
||||||
|
30: #2f2ebe,
|
||||||
|
35: #3c3dca,
|
||||||
|
40: #494bd6,
|
||||||
|
50: #6366f1,
|
||||||
|
60: #8083ff,
|
||||||
|
70: #a0a3ff,
|
||||||
|
80: #c0c1ff,
|
||||||
|
90: #e1e0ff,
|
||||||
|
95: #f2efff,
|
||||||
|
98: #fcf8ff,
|
||||||
|
99: #fffbff,
|
||||||
|
100: #ffffff,
|
||||||
|
),
|
||||||
|
secondary: (
|
||||||
|
0: #000000,
|
||||||
|
10: #13144a,
|
||||||
|
20: #292a60,
|
||||||
|
25: #34366c,
|
||||||
|
30: #404178,
|
||||||
|
35: #4b4d85,
|
||||||
|
40: #575992,
|
||||||
|
50: #7072ac,
|
||||||
|
60: #8a8bc8,
|
||||||
|
70: #a5a6e4,
|
||||||
|
80: #c0c1ff,
|
||||||
|
90: #e1e0ff,
|
||||||
|
95: #f2efff,
|
||||||
|
98: #fcf8ff,
|
||||||
|
99: #fffbff,
|
||||||
|
100: #ffffff,
|
||||||
|
),
|
||||||
|
tertiary: (
|
||||||
|
0: #000000,
|
||||||
|
10: #3f001c,
|
||||||
|
20: #650031,
|
||||||
|
25: #79003d,
|
||||||
|
30: #8e0048,
|
||||||
|
35: #a40054,
|
||||||
|
40: #ba0060,
|
||||||
|
50: #dd2979,
|
||||||
|
60: #ff4993,
|
||||||
|
70: #ff84ad,
|
||||||
|
80: #ffb1c7,
|
||||||
|
90: #ffd9e2,
|
||||||
|
95: #ffecef,
|
||||||
|
98: #fff8f8,
|
||||||
|
99: #fffbff,
|
||||||
|
100: #ffffff,
|
||||||
|
),
|
||||||
|
neutral: (
|
||||||
|
0: #000000,
|
||||||
|
10: #1b1b23,
|
||||||
|
20: #303038,
|
||||||
|
25: #3b3a44,
|
||||||
|
30: #46464f,
|
||||||
|
35: #52515b,
|
||||||
|
40: #5e5d67,
|
||||||
|
50: #777680,
|
||||||
|
60: #918f9a,
|
||||||
|
70: #acaab5,
|
||||||
|
80: #c7c5d1,
|
||||||
|
90: #e4e1ed,
|
||||||
|
95: #f2effb,
|
||||||
|
98: #fcf8ff,
|
||||||
|
99: #fffbff,
|
||||||
|
100: #ffffff,
|
||||||
|
4: #0d0d15,
|
||||||
|
6: #13131b,
|
||||||
|
12: #1f1f27,
|
||||||
|
17: #292932,
|
||||||
|
22: #34343d,
|
||||||
|
24: #393841,
|
||||||
|
87: #dbd8e4,
|
||||||
|
92: #e9e6f3,
|
||||||
|
94: #efecf8,
|
||||||
|
96: #f5f2fe,
|
||||||
|
),
|
||||||
|
neutral-variant: (
|
||||||
|
0: #000000,
|
||||||
|
10: #1a1a28,
|
||||||
|
20: #2f2f3d,
|
||||||
|
25: #3a3a49,
|
||||||
|
30: #464554,
|
||||||
|
35: #515160,
|
||||||
|
40: #5d5d6d,
|
||||||
|
50: #767586,
|
||||||
|
60: #908fa0,
|
||||||
|
70: #aba9bb,
|
||||||
|
80: #c7c4d7,
|
||||||
|
90: #e3e0f3,
|
||||||
|
95: #f2efff,
|
||||||
|
98: #fcf8ff,
|
||||||
|
99: #fffbff,
|
||||||
|
100: #ffffff,
|
||||||
|
),
|
||||||
|
error: (
|
||||||
|
0: #000000,
|
||||||
|
10: #410002,
|
||||||
|
20: #690005,
|
||||||
|
25: #7e0007,
|
||||||
|
30: #93000a,
|
||||||
|
35: #a80710,
|
||||||
|
40: #ba1a1a,
|
||||||
|
50: #de3730,
|
||||||
|
60: #ff5449,
|
||||||
|
70: #ff897d,
|
||||||
|
80: #ffb4ab,
|
||||||
|
90: #ffdad6,
|
||||||
|
95: #ffedea,
|
||||||
|
98: #fff8f7,
|
||||||
|
99: #fffbff,
|
||||||
|
100: #ffffff,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
$_rest: (
|
||||||
|
secondary: map.get($_palettes, secondary),
|
||||||
|
neutral: map.get($_palettes, neutral),
|
||||||
|
neutral-variant: map.get($_palettes, neutral-variant),
|
||||||
|
error: map.get($_palettes, error),
|
||||||
|
);
|
||||||
|
|
||||||
|
$primary-palette: map.merge(map.get($_palettes, primary), $_rest);
|
||||||
|
$tertiary-palette: map.merge(map.get($_palettes, tertiary), $_rest);
|
||||||
@@ -3,15 +3,16 @@ import { NgModule, provideBrowserGlobalErrorListeners } from '@angular/core';
|
|||||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
import { provideHttpClient, withFetch } from '@angular/common/http';
|
import { provideHttpClient, withFetch } from '@angular/common/http';
|
||||||
|
|
||||||
|
|
||||||
import { AppRoutingModule } from './app-routing-module';
|
import { AppRoutingModule } from './app-routing-module';
|
||||||
import { App } from './app';
|
import { App } from './app';
|
||||||
|
|
||||||
|
import { NgxMaskDirective, NgxMaskPipe, provideNgxMask } from 'ngx-mask';
|
||||||
|
|
||||||
import { MatIconModule } from '@angular/material/icon';
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
import { MatTabsModule } from '@angular/material/tabs';
|
import { MatTabsModule } from '@angular/material/tabs';
|
||||||
import { MatToolbarModule } from '@angular/material/toolbar';
|
import { MatToolbarModule } from '@angular/material/toolbar';
|
||||||
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
||||||
|
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||||
import { MatSnackBarModule } from '@angular/material/snack-bar';
|
import { MatSnackBarModule } from '@angular/material/snack-bar';
|
||||||
import { MatAutocompleteModule } from '@angular/material/autocomplete';
|
import { MatAutocompleteModule } from '@angular/material/autocomplete';
|
||||||
import { MatInputModule } from '@angular/material/input';
|
import { MatInputModule } from '@angular/material/input';
|
||||||
@@ -20,6 +21,7 @@ import { MatButtonModule, MatIconButton } from '@angular/material/button';
|
|||||||
import { MatDividerModule } from '@angular/material/divider';
|
import { MatDividerModule } from '@angular/material/divider';
|
||||||
import { MatDialogClose, MatDialogTitle, MatDialogContent, MatDialogActions } from "@angular/material/dialog";
|
import { MatDialogClose, MatDialogTitle, MatDialogContent, MatDialogActions } from "@angular/material/dialog";
|
||||||
import { MatStepperModule } from '@angular/material/stepper';
|
import { MatStepperModule } from '@angular/material/stepper';
|
||||||
|
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||||
|
|
||||||
import { HeaderComponent } from './header/header.component';
|
import { HeaderComponent } from './header/header.component';
|
||||||
import { HomeComponent } from './home/home.component';
|
import { HomeComponent } from './home/home.component';
|
||||||
@@ -28,7 +30,6 @@ import { MainLayoutComponent } from './layouts/main-layout/main-layout.component
|
|||||||
import { NavbarComponent } from './navbar/navbar.component';
|
import { NavbarComponent } from './navbar/navbar.component';
|
||||||
import { PocModelComponent } from './poc-model-component/poc-model-component';
|
import { PocModelComponent } from './poc-model-component/poc-model-component';
|
||||||
import { ScheduleComponent } from './schedule/schedule.component';
|
import { ScheduleComponent } from './schedule/schedule.component';
|
||||||
|
|
||||||
import { MovieDurationComponent } from './movie-duration/movie-duration.component';
|
import { MovieDurationComponent } from './movie-duration/movie-duration.component';
|
||||||
import { MoviePerformanceComponent } from './movie-performance/movie-performance.component';
|
import { MoviePerformanceComponent } from './movie-performance/movie-performance.component';
|
||||||
import { MoviePosterComponent } from './movie-poster/movie-poster.component';
|
import { MoviePosterComponent } from './movie-poster/movie-poster.component';
|
||||||
@@ -49,6 +50,11 @@ import { MovieImporterComponent } from './movie-importer/movie-importer.componen
|
|||||||
import { MovieImportNoSearchResultComponent } from './movie-import-no-search-result/movie-import-no-search-result.component';
|
import { MovieImportNoSearchResultComponent } from './movie-import-no-search-result/movie-import-no-search-result.component';
|
||||||
import { MovieImportSearchInfoComponent } from './movie-import-search-info/movie-import-search-info.component';
|
import { MovieImportSearchInfoComponent } from './movie-import-search-info/movie-import-search-info.component';
|
||||||
import { LoginDialog } from './login/login.dialog';
|
import { LoginDialog } from './login/login.dialog';
|
||||||
|
import { PerformanceInfoComponent } from './performance-info/performance-info.component';
|
||||||
|
import { ShoppingCartComponent } from './shopping-cart/shopping-cart.component';
|
||||||
|
import { OrderComponent } from './order/order.component';
|
||||||
|
import { SeatSelectionComponent } from './seat-selection/seat-selection.component';
|
||||||
|
import { NoSeatsInHallComponent } from './no-seats-in-hall/no-seats-in-hall.component';
|
||||||
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
@@ -81,6 +87,11 @@ import { LoginDialog } from './login/login.dialog';
|
|||||||
MovieImportNoSearchResultComponent,
|
MovieImportNoSearchResultComponent,
|
||||||
MovieImportSearchInfoComponent,
|
MovieImportSearchInfoComponent,
|
||||||
LoginDialog,
|
LoginDialog,
|
||||||
|
PerformanceInfoComponent,
|
||||||
|
ShoppingCartComponent,
|
||||||
|
OrderComponent,
|
||||||
|
SeatSelectionComponent,
|
||||||
|
NoSeatsInHallComponent,
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
AppRoutingModule,
|
AppRoutingModule,
|
||||||
@@ -92,6 +103,7 @@ import { LoginDialog } from './login/login.dialog';
|
|||||||
MatTabsModule,
|
MatTabsModule,
|
||||||
MatToolbarModule,
|
MatToolbarModule,
|
||||||
MatProgressBarModule,
|
MatProgressBarModule,
|
||||||
|
MatProgressSpinnerModule,
|
||||||
MatSnackBarModule,
|
MatSnackBarModule,
|
||||||
MatAutocompleteModule,
|
MatAutocompleteModule,
|
||||||
MatInputModule,
|
MatInputModule,
|
||||||
@@ -103,13 +115,17 @@ import { LoginDialog } from './login/login.dialog';
|
|||||||
MatDialogTitle,
|
MatDialogTitle,
|
||||||
MatDialogContent,
|
MatDialogContent,
|
||||||
MatDialogActions,
|
MatDialogActions,
|
||||||
MatStepperModule
|
MatCheckboxModule,
|
||||||
|
MatStepperModule,
|
||||||
|
NgxMaskDirective,
|
||||||
|
NgxMaskPipe,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
provideBrowserGlobalErrorListeners(),
|
provideBrowserGlobalErrorListeners(),
|
||||||
provideHttpClient(
|
provideHttpClient(
|
||||||
withFetch(),
|
withFetch(),
|
||||||
)
|
),
|
||||||
|
provideNgxMask(),
|
||||||
],
|
],
|
||||||
bootstrap: [App]
|
bootstrap: [App]
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -10,5 +10,13 @@
|
|||||||
@if ( searchBar() ) {
|
@if ( searchBar() ) {
|
||||||
<app-movie-search (movieSearchResult)="movieSearchResult.emit($event)"></app-movie-search>
|
<app-movie-search (movieSearchResult)="movieSearchResult.emit($event)"></app-movie-search>
|
||||||
}
|
}
|
||||||
|
@if ( backToSchedule() ) {
|
||||||
|
<div class="mt-1 mr-4">
|
||||||
|
<a [routerLink]="['/schedule']" class="opacity-50 hover:underline flex items-center gap-x-1">
|
||||||
|
<mat-icon style="font-size: 20px; width: 20px; height: 20px;">arrow_back</mat-icon>
|
||||||
|
<span>Zurück zur Programmübersicht</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
<mat-divider></mat-divider>
|
<mat-divider></mat-divider>
|
||||||
|
|||||||
@@ -12,4 +12,6 @@ export class MenuHeaderComponent {
|
|||||||
|
|
||||||
searchBar = input<boolean>(false);
|
searchBar = input<boolean>(false);
|
||||||
movieSearchResult = output<string>();
|
movieSearchResult = output<string>();
|
||||||
|
|
||||||
|
backToSchedule = input<boolean>(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
<span class="flex rounded-sm text-black text-sm px-2 py-1.5">
|
<span>
|
||||||
{{ category() }}
|
{{ category() }}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
<span class="flex items-center text-black text-sm rounded-sm px-2 py-1.5">
|
<span class="flex items-center">
|
||||||
|
@if (showIcon()) {
|
||||||
<mat-icon class="mr-1" style="font-size: 20px; width: 20px; height: 20px;" fontIcon="schedule"></mat-icon>
|
<mat-icon class="mr-1" style="font-size: 20px; width: 20px; height: 20px;" fontIcon="schedule"></mat-icon>
|
||||||
|
}
|
||||||
{{ durationText() }}
|
{{ durationText() }}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -8,9 +8,13 @@ import { Component, input, computed } from '@angular/core';
|
|||||||
})
|
})
|
||||||
export class MovieDurationComponent {
|
export class MovieDurationComponent {
|
||||||
duration = input<number>(0);
|
duration = input<number>(0);
|
||||||
|
showIcon = input<boolean>(true);
|
||||||
|
|
||||||
durationText = computed(() => {
|
durationText = computed(() => {
|
||||||
if (this.duration() > 0) {
|
if (this.duration() > 0) {
|
||||||
|
if (!this.showIcon()) {
|
||||||
|
return `${this.duration()} Minuten`;
|
||||||
|
}
|
||||||
return `${this.duration()} Min.`;
|
return `${this.duration()} Min.`;
|
||||||
}
|
}
|
||||||
return 'N/A';
|
return 'N/A';
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex gap-1 justify-between">
|
<div class="flex gap-1 justify-between">
|
||||||
<app-movie-rating [rating]="movie().rating"></app-movie-rating>
|
<app-movie-rating [rating]="movie().rating" class="text-black text-sm rounded-sm shadow-md px-2 py-1.5"></app-movie-rating>
|
||||||
<app-movie-duration [duration]="movie().duration"></app-movie-duration>
|
<app-movie-duration [duration]="movie().duration" class="text-black text-sm rounded-sm px-2 py-1.5"></app-movie-duration>
|
||||||
<app-movie-category [category]="movie().category.name"></app-movie-category>
|
<app-movie-category [category]="movie().category.name" class="text-black text-sm px-2 py-1.5"></app-movie-category>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
<span [class]="ratingColor()" class="text-black flex rounded-sm shadow-md text-sm px-2 py-1.5">
|
<span>
|
||||||
{{ ratingText() }}
|
{{ ratingText() }}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Component, input, computed } from '@angular/core';
|
import { Component, input, computed, HostBinding } from '@angular/core';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-movie-rating',
|
selector: 'app-movie-rating',
|
||||||
@@ -7,6 +7,11 @@ import { Component, input, computed } from '@angular/core';
|
|||||||
styleUrl: './movie-rating.component.css'
|
styleUrl: './movie-rating.component.css'
|
||||||
})
|
})
|
||||||
export class MovieRatingComponent {
|
export class MovieRatingComponent {
|
||||||
|
|
||||||
|
@HostBinding('class') get hostClasses(): string {
|
||||||
|
return this.ratingColor();
|
||||||
|
}
|
||||||
|
|
||||||
rating = input<number>(0);
|
rating = input<number>(0);
|
||||||
|
|
||||||
ratingColor = computed(() => {
|
ratingColor = computed(() => {
|
||||||
|
|||||||
7
src/app/no-seats-in-hall/no-seats-in-hall.component.html
Normal file
7
src/app/no-seats-in-hall/no-seats-in-hall.component.html
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<div class="flex flex-col items-center justify-center py-8 text-gray-500 m-auto">
|
||||||
|
<mat-icon class="material-symbols-outlined text-6xl mb-4 opacity-50" style="font-size: 40px; width: 40px; height: 40px">
|
||||||
|
brightness_alert
|
||||||
|
</mat-icon>
|
||||||
|
<p class="text-lg">Huch?! Keine Sitzplätze?</p>
|
||||||
|
<p class="text-sm">Hast du ein Glück, Stehplätze sind kostenlos.</p>
|
||||||
|
</div>
|
||||||
11
src/app/no-seats-in-hall/no-seats-in-hall.component.ts
Normal file
11
src/app/no-seats-in-hall/no-seats-in-hall.component.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-no-seats-in-hall',
|
||||||
|
standalone: false,
|
||||||
|
templateUrl: './no-seats-in-hall.component.html',
|
||||||
|
styleUrl: './no-seats-in-hall.component.css'
|
||||||
|
})
|
||||||
|
export class NoSeatsInHallComponent {
|
||||||
|
|
||||||
|
}
|
||||||
19
src/app/order/order.component.css
Normal file
19
src/app/order/order.component.css
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
mat-stepper {
|
||||||
|
background: transparent !important;
|
||||||
|
}
|
||||||
|
::ng-deep .mat-step-header {
|
||||||
|
background-color: transparent !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
::ng-deep .mat-horizontal-stepper-header{
|
||||||
|
pointer-events: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.performance-info-space {
|
||||||
|
margin-top: calc(var(--spacing) * 24)
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .checkbox-invalid.mat-mdc-checkbox .mat-internal-form-field {
|
||||||
|
color: red !important;
|
||||||
|
}
|
||||||
161
src/app/order/order.component.html
Normal file
161
src/app/order/order.component.html
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
<div class="w-full h-full relative">
|
||||||
|
|
||||||
|
@if (loadingService.loading$ | async){
|
||||||
|
<div class="w-full h-full flex items-center justify-center">
|
||||||
|
<mat-progress-spinner
|
||||||
|
mode="indeterminate"
|
||||||
|
diameter="50"
|
||||||
|
></mat-progress-spinner>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
@else if (performance()) {
|
||||||
|
|
||||||
|
<div class="absolute top-18 z-20 w-full px-6">
|
||||||
|
<app-performance-info
|
||||||
|
class="w-full h-10"
|
||||||
|
[performance]="performance()!"
|
||||||
|
></app-performance-info>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<mat-stepper orientation="horizontal" linear="true" [disableRipple]="true" (selectionChange)="onStepChange($event)" #stepper>
|
||||||
|
<mat-step>
|
||||||
|
<ng-template matStepLabel>Warenkorb</ng-template>
|
||||||
|
|
||||||
|
<div class="performance-info-space"></div>
|
||||||
|
|
||||||
|
<!-- Seat-Selection Overview -->
|
||||||
|
<div class="mb-4 p-2">
|
||||||
|
@for (seatCategory of seatCategories(); track $index) {
|
||||||
|
<div class="h-2"></div>
|
||||||
|
<app-seat-selection [seatCategory]="seatCategory"></app-seat-selection>
|
||||||
|
}
|
||||||
|
@empty {
|
||||||
|
<app-no-seats-in-hall></app-no-seats-in-hall>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<mat-divider></mat-divider>
|
||||||
|
|
||||||
|
<!-- Total Price -->
|
||||||
|
<div class="flex justify-between p-2 mt-1 items-baseline">
|
||||||
|
<p class="font-semibold text-lg">
|
||||||
|
Tickets gesamt:
|
||||||
|
</p>
|
||||||
|
<p class="font-semibold text-2xl bg-linear-to-r from-indigo-500 to-pink-600 bg-clip-text text-transparent">
|
||||||
|
{{ getPriceDisplay(totalPrice()) }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Buttons -->
|
||||||
|
<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="filled" matStepperNext class="w-1/2" [disabled]="totalSeats()==0">Buchen</button>
|
||||||
|
</div>
|
||||||
|
</mat-step>
|
||||||
|
|
||||||
|
<mat-step [stepControl]="dataForm">
|
||||||
|
<form [formGroup]="dataForm">
|
||||||
|
<ng-template matStepLabel>Anschrift</ng-template>
|
||||||
|
|
||||||
|
<div class="performance-info-space"></div>
|
||||||
|
|
||||||
|
<!-- Name -->
|
||||||
|
<mat-form-field class="w-full mt-8">
|
||||||
|
<mat-label>Name</mat-label>
|
||||||
|
<input matInput formControlName="name" placeholder="Max Mustermann" />
|
||||||
|
@if (fData['name'].hasError('minlength')) { <mat-error>Mindestens 3 Zeichen</mat-error> }
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<!-- E-Mail -->
|
||||||
|
<mat-form-field class="w-full mt-2">
|
||||||
|
<mat-label>E-Mail Adresse</mat-label>
|
||||||
|
<input matInput formControlName="email" placeholder="max.mustermann@edu.fhdw.de" />
|
||||||
|
@if (fData['email'].hasError('email')) { <mat-error>Ungültige E-Mail-Adresse</mat-error> }
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<!-- Checkbox -->
|
||||||
|
<div class="w-full my-4">
|
||||||
|
<mat-checkbox required formControlName="accept" class="checkbox-invalid" [class]="{ 'checkbox-invalid': submitted && fData['accept'].hasError('required') }">
|
||||||
|
Ich akzeptiere die AGB und die Datenbestimmung
|
||||||
|
</mat-checkbox>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Buttons -->
|
||||||
|
<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="submit" mat-button matButton="filled" matStepperNext (click)="stupidCheckboxWorkaround()" class="w-2/3">Sitzplätze reservieren</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</mat-step>
|
||||||
|
<mat-step [stepControl]="paymentForm">
|
||||||
|
<form [formGroup]="paymentForm" (ngSubmit)="onSubmit()">
|
||||||
|
<ng-template matStepLabel>Zahlung</ng-template>
|
||||||
|
|
||||||
|
<div class="performance-info-space"></div>
|
||||||
|
|
||||||
|
<!-- Card Number -->
|
||||||
|
<mat-form-field class="w-full mt-8">
|
||||||
|
<mat-label>Kartennummer</mat-label>
|
||||||
|
<input
|
||||||
|
matInput
|
||||||
|
formControlName="cardNumber"
|
||||||
|
mask="0000 0000 0000 0000"
|
||||||
|
placeholder="1111 2222 3333 4444"
|
||||||
|
/>
|
||||||
|
@if (fPayment['cardNumber'].hasError('pattern')) { <mat-error>Ungültige Kartennummer</mat-error> }
|
||||||
|
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<!-- Card Name -->
|
||||||
|
<mat-form-field class="w-full">
|
||||||
|
<mat-label>Kartenname</mat-label>
|
||||||
|
<input matInput formControlName="cardName" />
|
||||||
|
@if (fPayment['cardName'].hasError('minlength')) { <mat-error>Mindestens 3 Zeichen</mat-error> }
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<!-- Expiry & CVV -->
|
||||||
|
<div class="flex space-x-4">
|
||||||
|
<mat-form-field class="flex-1">
|
||||||
|
<mat-label>Gültig bis (MM/YY)</mat-label>
|
||||||
|
<input
|
||||||
|
matInput
|
||||||
|
formControlName="expiry"
|
||||||
|
mask="00/00"
|
||||||
|
placeholder="MM/YY"
|
||||||
|
[dropSpecialCharacters]="false"
|
||||||
|
/>
|
||||||
|
@if (fPayment['expiry'].hasError('pattern')) { <mat-error>Ungültiges Format</mat-error> }
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field class="flex-1">
|
||||||
|
<mat-label>CVV</mat-label>
|
||||||
|
<input matInput type="password" maxlength="4" formControlName="cvv" />
|
||||||
|
@if (fPayment['cvv'].hasError('pattern')) { <mat-error>3–4 Ziffernt</mat-error> }
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Info -->
|
||||||
|
<div class="flex w-full space-x-2 mt-2 items-center">
|
||||||
|
<mat-icon class="material-symbols-outlined opacity-50" style="font-size: 32px; width: 32px; height: 32px">
|
||||||
|
encrypted
|
||||||
|
</mat-icon>
|
||||||
|
<p class="text-sm opacity-75">
|
||||||
|
Ihre Zahlung wird sicher über unsere Partner verarbeitet.<br>Wir speichern keine Zahlungsinformationen.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Buttons -->
|
||||||
|
<div class="flex space-x-4 mt-8">
|
||||||
|
<button mat-stroked-button color="primary" matStepperPrevious type="button" class="w-1/3">
|
||||||
|
Zurück
|
||||||
|
</button>
|
||||||
|
<button mat-flat-button color="accent" class="w-2/3" matStepperNext type="submit">
|
||||||
|
{{ getPriceDisplay(totalPrice()) }} jetzt bezahlen
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</mat-step>
|
||||||
|
|
||||||
|
</mat-stepper>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
70
src/app/order/order.component.ts
Normal file
70
src/app/order/order.component.ts
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
import { SelectedSeatsService } from './../selected-seats.service';
|
||||||
|
import { LoadingService } from './../loading.service';
|
||||||
|
import { Sitzkategorie, Vorstellung } from '@infinimotion/model-frontend';
|
||||||
|
import { Component, computed, inject, input } from '@angular/core';
|
||||||
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||||
|
import { StepperSelectionEvent } from '@angular/cdk/stepper';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-order',
|
||||||
|
standalone: false,
|
||||||
|
templateUrl: './order.component.html',
|
||||||
|
styleUrl: './order.component.css'
|
||||||
|
})
|
||||||
|
export class OrderComponent {
|
||||||
|
paymentForm!: FormGroup;
|
||||||
|
dataForm!: FormGroup;
|
||||||
|
|
||||||
|
submitted = false;
|
||||||
|
|
||||||
|
constructor(private fb: FormBuilder) {}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.paymentForm = this.fb.group({
|
||||||
|
cardNumber: ['', [Validators.required, Validators.pattern(/^\d{16}$/)]],
|
||||||
|
cardName: ['', [Validators.required, Validators.minLength(3)]],
|
||||||
|
expiry: ['', [Validators.required, Validators.pattern(/^(0[1-9]|1[0-2])\/\d{2}$/)]],
|
||||||
|
cvv: ['', [Validators.required, Validators.pattern(/^\d{3,4}$/)]],
|
||||||
|
});
|
||||||
|
this.dataForm = this.fb.group({
|
||||||
|
name: ['', [Validators.required, Validators.minLength(3)]],
|
||||||
|
email: ['', [Validators.required, Validators.email]],
|
||||||
|
accept: ['', Validators.requiredTrue],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get fData() { return this.dataForm.controls; }
|
||||||
|
get fPayment() { return this.paymentForm.controls; }
|
||||||
|
|
||||||
|
onSubmit() {
|
||||||
|
if (this.paymentForm.invalid) return;
|
||||||
|
console.log('Zahlungsdaten:', this.paymentForm.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
onStepChange(event: StepperSelectionEvent) {
|
||||||
|
this.submitted = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
stupidCheckboxWorkaround() {
|
||||||
|
this.submitted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
performance = input<Vorstellung>();
|
||||||
|
seatCategories = input.required<Sitzkategorie[]>();
|
||||||
|
|
||||||
|
loadingService = inject(LoadingService);
|
||||||
|
private selectedSeatsService = inject(SelectedSeatsService);
|
||||||
|
|
||||||
|
totalPrice = computed(() =>
|
||||||
|
this.selectedSeatsService.getSelectedSeatsList().reduce((sum, seat) => sum + seat.row.category.price, 0)
|
||||||
|
);
|
||||||
|
|
||||||
|
totalSeats = computed(() =>
|
||||||
|
this.selectedSeatsService.getSelectedSeatsList().length
|
||||||
|
);
|
||||||
|
|
||||||
|
getPriceDisplay(price: number): string {
|
||||||
|
return `${(price / 100).toFixed(2)} €`;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
3
src/app/performance-info/performance-info.component.css
Normal file
3
src/app/performance-info/performance-info.component.css
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
.info-box {
|
||||||
|
color: var(--mat-sys-on-surface);
|
||||||
|
}
|
||||||
21
src/app/performance-info/performance-info.component.html
Normal file
21
src/app/performance-info/performance-info.component.html
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<div class="info-box bg-indigo-100 h-22 flex items-center space-x-3 rounded-md p-1 shadow-sm">
|
||||||
|
|
||||||
|
<div class="h-full">
|
||||||
|
<img
|
||||||
|
[src]="movie().image && movie().image !== 'N/A' ? movie().image : 'assets/poster_placeholder.png'"
|
||||||
|
alt="Movie Poster"
|
||||||
|
class="h-full w-auto shadow-xs rounded-sm"
|
||||||
|
(error)="onPosterError($event)"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="text-md">
|
||||||
|
<h3 class="opacity-75">{{ getStartTimeString() }} • {{ performance().hall.name }}</h3>
|
||||||
|
<h1 class="font-semibold mb-0.5">{{ movie().title }}</h1>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<app-movie-rating [rating]="movie().rating" class="rounded-sm shadow-xs px-1 py-0.25 text-sm"></app-movie-rating>
|
||||||
|
<app-movie-duration [duration]="movie().duration" [showIcon]="false" class="ml-1.5 opacity-75"></app-movie-duration>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
30
src/app/performance-info/performance-info.component.ts
Normal file
30
src/app/performance-info/performance-info.component.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import { Component, input } from '@angular/core';
|
||||||
|
import { Vorstellung } from '@infinimotion/model-frontend';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-performance-info',
|
||||||
|
standalone: false,
|
||||||
|
templateUrl: './performance-info.component.html',
|
||||||
|
styleUrl: './performance-info.component.css'
|
||||||
|
})
|
||||||
|
export class PerformanceInfoComponent {
|
||||||
|
performance = input.required<Vorstellung>();
|
||||||
|
|
||||||
|
getStartTimeString(): string {
|
||||||
|
const date = new Date(this.performance().start);
|
||||||
|
return date.toLocaleDateString('de-DE', { weekday: 'short' }) + '. ' + date.toLocaleDateString('de-DE') + ', ' + date.toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' }) + ' Uhr';
|
||||||
|
}
|
||||||
|
|
||||||
|
movie() {
|
||||||
|
return this.performance().movie
|
||||||
|
}
|
||||||
|
|
||||||
|
onPosterError(event: Event) {
|
||||||
|
const img = event.target as HTMLImageElement;
|
||||||
|
const placeholder = 'assets/poster_placeholder.png';
|
||||||
|
|
||||||
|
if (img.src !== window.location.origin + placeholder) {
|
||||||
|
img.src = placeholder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
3
src/app/seat-selection/seat-selection.component.css
Normal file
3
src/app/seat-selection/seat-selection.component.css
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
.seat-name {
|
||||||
|
color: var(--mat-sys-primary);
|
||||||
|
}
|
||||||
11
src/app/seat-selection/seat-selection.component.html
Normal file
11
src/app/seat-selection/seat-selection.component.html
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<h2 class="seat-name mb-1">{{ seatCategory().name }}</h2>
|
||||||
|
<div class="flex items-center justify-between text-lg">
|
||||||
|
<div class="flex items-center space-x-2 w-3/7">
|
||||||
|
<mat-icon style="font-size: 30px; width: 30px; height: 30px">
|
||||||
|
{{ seatCategory().icon }}
|
||||||
|
</mat-icon>
|
||||||
|
<p>{{ getPriceDisplay(seatCategory().price) }}</p>
|
||||||
|
</div>
|
||||||
|
<p>× {{ selectedSeatsByCategory() }}</p>
|
||||||
|
<p class="w-2/7 text-right">{{ getPriceDisplay(totalCategoryPrice()) }}</p>
|
||||||
|
</div>
|
||||||
27
src/app/seat-selection/seat-selection.component.ts
Normal file
27
src/app/seat-selection/seat-selection.component.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import { SelectedSeatsService } from './../selected-seats.service';
|
||||||
|
import { Component, computed, inject, input } from '@angular/core';
|
||||||
|
import { Sitzkategorie } from '@infinimotion/model-frontend';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-seat-selection',
|
||||||
|
standalone: false,
|
||||||
|
templateUrl: './seat-selection.component.html',
|
||||||
|
styleUrl: './seat-selection.component.css'
|
||||||
|
})
|
||||||
|
export class SeatSelectionComponent {
|
||||||
|
seatCategory = input.required<Sitzkategorie>();
|
||||||
|
|
||||||
|
SelectedSeatsService = inject(SelectedSeatsService);
|
||||||
|
|
||||||
|
selectedSeatsByCategory = computed(() =>
|
||||||
|
this.SelectedSeatsService.getSelectedSeatsByCategory(this.seatCategory().id).length
|
||||||
|
);
|
||||||
|
|
||||||
|
totalCategoryPrice = computed(() =>
|
||||||
|
this.selectedSeatsByCategory() * this.seatCategory().price
|
||||||
|
);
|
||||||
|
|
||||||
|
getPriceDisplay(price: number): string {
|
||||||
|
return `${(price / 100).toFixed(2)} €`;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,39 +1,37 @@
|
|||||||
import {Injectable} from '@angular/core';
|
import { Injectable, signal } from '@angular/core';
|
||||||
import {Sitzplatz} from '@infinimotion/model-frontend';
|
import {Sitzplatz} from '@infinimotion/model-frontend';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
})
|
})
|
||||||
export class SelectedSeatsService {
|
export class SelectedSeatsService {
|
||||||
private selectedSeatsList: Sitzplatz[] = [];
|
private selectedSeatsSignal = signal<Sitzplatz[]>([]);
|
||||||
private seatIsSelectable: boolean = true;
|
private seatIsSelectable: boolean = true;
|
||||||
|
|
||||||
|
get selectedSeats() {
|
||||||
|
return this.selectedSeatsSignal;
|
||||||
|
}
|
||||||
|
|
||||||
pushSelectedSeat(selectedSeat: Sitzplatz): void {
|
pushSelectedSeat(selectedSeat: Sitzplatz): void {
|
||||||
this.selectedSeatsList.push(selectedSeat);
|
this.selectedSeatsSignal.update(seats => [...seats, selectedSeat]);
|
||||||
//console.log("Added" + selectedSeat);
|
|
||||||
console.log(this.selectedSeatsList);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
removeSelectedSeat(selectedSeat: Sitzplatz): void {
|
removeSelectedSeat(selectedSeat: Sitzplatz): void {
|
||||||
let removeId = this.selectedSeatsList.indexOf(selectedSeat);
|
this.selectedSeatsSignal.update(seats =>
|
||||||
|
seats.filter(seat => seat.id !== selectedSeat.id)
|
||||||
if(removeId !== -1) {
|
);
|
||||||
this.selectedSeatsList.splice(removeId, 1)
|
|
||||||
}
|
|
||||||
//console.log("Removed" + selectedSeat)
|
|
||||||
console.log(this.selectedSeatsList);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getSelectedSeatsList(): Sitzplatz[] {
|
getSelectedSeatsList(): Sitzplatz[] {
|
||||||
return this.selectedSeatsList;
|
return this.selectedSeatsSignal();
|
||||||
|
}
|
||||||
|
|
||||||
|
getSelectedSeatsByCategory(categoryId: number): Sitzplatz[] {
|
||||||
|
return this.selectedSeatsSignal().filter(seat => seat.row.category.id === categoryId);
|
||||||
}
|
}
|
||||||
|
|
||||||
clearSelectedSeatsList(): void {
|
clearSelectedSeatsList(): void {
|
||||||
this.selectedSeatsList = [];
|
this.selectedSeatsSignal.set([]);
|
||||||
//for (let i = this.selectedSeatsList.length - 1; i >= 0; i--) {
|
|
||||||
// const seat = this.selectedSeatsList[i];
|
|
||||||
// this.removeSelectedSeat(seat);
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getSeatIsSelected(): boolean{
|
getSeatIsSelected(): boolean{
|
||||||
|
|||||||
0
src/app/shopping-cart/shopping-cart.component.css
Normal file
0
src/app/shopping-cart/shopping-cart.component.css
Normal file
1
src/app/shopping-cart/shopping-cart.component.html
Normal file
1
src/app/shopping-cart/shopping-cart.component.html
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<p>shopping-cart works!</p>
|
||||||
11
src/app/shopping-cart/shopping-cart.component.ts
Normal file
11
src/app/shopping-cart/shopping-cart.component.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-shopping-cart',
|
||||||
|
standalone: false,
|
||||||
|
templateUrl: './shopping-cart.component.html',
|
||||||
|
styleUrl: './shopping-cart.component.css'
|
||||||
|
})
|
||||||
|
export class ShoppingCartComponent {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
:host {
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
<div class="m-auto w-200 h-10 bg-gray-300 mb-20" style="clip-path: polygon(0% 0%,100% 0%,90% 100%,10% 100%);">
|
||||||
|
<p class="flex justify-center text-2xl fond-bold p-1">
|
||||||
|
Leinwand
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
@for (row of seatsPerRow(); track $index) {
|
@for (row of seatsPerRow(); track $index) {
|
||||||
<app-seat-row class="flex justify-center" [rowSeatList]="row"></app-seat-row>
|
<app-seat-row class="flex justify-center" [rowSeatList]="row"></app-seat-row>
|
||||||
}
|
}
|
||||||
|
</div>
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
<div class="m-auto w-200 h-10 bg-gray-300 mb-20" style="clip-path: polygon(0% 0%,100% 0%,90% 100%,10% 100%);">
|
<app-menu-header title="Vorstellungstickets kaufen" icon="local_activity" [backToSchedule]="true"></app-menu-header>
|
||||||
<p class="flex justify-center text-2xl fond-bold p-1">
|
|
||||||
Leinwand
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="">
|
|
||||||
<app-theater-layout [seatsPerRow]="seatsPerRow"></app-theater-layout>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
<div class="flex justify-between h-max">
|
||||||
|
|
||||||
|
<app-theater-layout [seatsPerRow]="seatsPerRow" class="m-10 w-7/10"></app-theater-layout>
|
||||||
|
|
||||||
|
<app-order class="m-10 w-3/10" [performance]="performance" [seatCategories]="seatCategories"></app-order>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import {Component, inject, OnInit} from '@angular/core';
|
import {Component, inject, OnInit} from '@angular/core';
|
||||||
import {HttpService} from '../http.service';
|
import {HttpService} from '../http.service';
|
||||||
import {LoadingService} from '../loading.service';
|
import {LoadingService} from '../loading.service';
|
||||||
import {catchError, of, tap} from 'rxjs';
|
import {catchError, forkJoin, of, tap} from 'rxjs';
|
||||||
import {Sitzplatz} from '@infinimotion/model-frontend';
|
import {Sitzkategorie, Sitzplatz, Vorstellung} from '@infinimotion/model-frontend';
|
||||||
import {TheaterSeatState} from '../model/theater-seat-state.model';
|
import {TheaterSeatState} from '../model/theater-seat-state.model';
|
||||||
import {ActivatedRoute} from '@angular/router';
|
import {ActivatedRoute} from '@angular/router';
|
||||||
import {SelectedSeatsService} from '../selected-seats.service';
|
import {SelectedSeatsService} from '../selected-seats.service';
|
||||||
@@ -19,26 +19,33 @@ export class TheaterOverlayComponent implements OnInit {
|
|||||||
|
|
||||||
showId!: number;
|
showId!: number;
|
||||||
seatsPerRow: { seat: Sitzplatz, state: TheaterSeatState }[][] = []
|
seatsPerRow: { seat: Sitzplatz, state: TheaterSeatState }[][] = []
|
||||||
|
performance: Vorstellung | undefined;
|
||||||
|
seatCategories: Sitzkategorie[] = [];
|
||||||
|
|
||||||
constructor(private route: ActivatedRoute, private selectedSeatService : SelectedSeatsService) {}
|
constructor(private route: ActivatedRoute, private selectedSeatService : SelectedSeatsService) {}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.showId = Number(this.route.snapshot.paramMap.get('id')!);
|
this.showId = Number(this.route.snapshot.paramMap.get('id')!);
|
||||||
this.loadShowSeats();
|
|
||||||
this.selectedSeatService.clearSelectedSeatsList();
|
this.selectedSeatService.clearSelectedSeatsList();
|
||||||
|
this.loadPerformanceAndSeats();
|
||||||
}
|
}
|
||||||
|
|
||||||
loadShowSeats() {
|
loadPerformanceAndSeats() {
|
||||||
this.loading.show();
|
this.loading.show();
|
||||||
this.http.getSeatsByShowId(this.showId).pipe(
|
|
||||||
tap((data) => {
|
forkJoin({
|
||||||
this.seatsPerRow = this.converter(data)
|
performance: this.http.getPerformaceById(this.showId),
|
||||||
|
seats: this.http.getSeatsByShowId(this.showId)
|
||||||
|
}).pipe(
|
||||||
|
tap(({ performance, seats }) => {
|
||||||
|
this.performance = performance;
|
||||||
|
this.seatsPerRow = this.converter(seats);
|
||||||
this.loading.hide();
|
this.loading.hide();
|
||||||
}),
|
}),
|
||||||
catchError(err => {
|
catchError(err => {
|
||||||
this.loading.showError(err);
|
this.loading.showError(err);
|
||||||
console.error('Fehler beim Laden der Vorstellung', err);
|
console.error('Fehler beim Laden', err);
|
||||||
return of([]);
|
return of({ performance: null, seats: [] });
|
||||||
})
|
})
|
||||||
).subscribe();
|
).subscribe();
|
||||||
}
|
}
|
||||||
@@ -48,13 +55,26 @@ export class TheaterOverlayComponent implements OnInit {
|
|||||||
state: TheaterSeatState
|
state: TheaterSeatState
|
||||||
}[][] {
|
}[][] {
|
||||||
let rows: { seat: Sitzplatz, state: TheaterSeatState }[][] = [];
|
let rows: { seat: Sitzplatz, state: TheaterSeatState }[][] = [];
|
||||||
|
const categoryMap = new Map<number, Sitzkategorie>();
|
||||||
|
|
||||||
resp.seats.forEach(seat => {
|
resp.seats.forEach(seat => {
|
||||||
if (!rows[seat.row.position]) {
|
if (!rows[seat.row.position]) {
|
||||||
rows[seat.row.position] = [];
|
rows[seat.row.position] = [];
|
||||||
}
|
}
|
||||||
let state = resp.booked.find(other => other.id == seat.id) ? TheaterSeatState.BOOKED : resp.reserved.find(other => other.id == seat.id) ? TheaterSeatState.RESERVED : TheaterSeatState.AVAILABLE;
|
|
||||||
|
let state = resp.booked.find(other => other.id == seat.id) ? TheaterSeatState.BOOKED
|
||||||
|
: resp.reserved.find(other => other.id == seat.id) ? TheaterSeatState.RESERVED
|
||||||
|
: TheaterSeatState.AVAILABLE;
|
||||||
|
|
||||||
rows[seat.row.position].push({seat: seat, state: state});
|
rows[seat.row.position].push({seat: seat, state: state});
|
||||||
|
|
||||||
|
if (seat.row.category && !categoryMap.has(seat.row.category.id)) {
|
||||||
|
categoryMap.set(seat.row.category.id, seat.row.category);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.seatCategories = Array.from(categoryMap.values()).sort((a, b) => a.id - b.id);
|
||||||
|
|
||||||
rows = rows.filter(row => row.length > 0).sort((a, b) => a[0].seat.row.position - b[0].seat.row.position);
|
rows = rows.filter(row => row.length > 0).sort((a, b) => a[0].seat.row.position - b[0].seat.row.position);
|
||||||
rows.forEach(row => row.sort((a, b) => a.seat.position - b.seat.position));
|
rows.forEach(row => row.sort((a, b) => a.seat.position - b.seat.position));
|
||||||
return rows;
|
return rows;
|
||||||
|
|||||||
@@ -5,6 +5,33 @@
|
|||||||
// Learn more about theming and how to use it for your application's
|
// Learn more about theming and how to use it for your application's
|
||||||
// custom components at https://material.angular.dev/guide/theming
|
// custom components at https://material.angular.dev/guide/theming
|
||||||
@use '@angular/material' as mat;
|
@use '@angular/material' as mat;
|
||||||
|
@use './_theme-colors' as theme-colors;
|
||||||
|
|
||||||
|
// Light Theme
|
||||||
|
html {
|
||||||
|
color-scheme: light;
|
||||||
|
@include mat.theme((
|
||||||
|
color: (
|
||||||
|
theme-type: light,
|
||||||
|
primary: theme-colors.$primary-palette,
|
||||||
|
tertiary: theme-colors.$tertiary-palette,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dark Theme
|
||||||
|
html.dark {
|
||||||
|
color-scheme: dark;
|
||||||
|
@include mat.theme((
|
||||||
|
color: (
|
||||||
|
theme-type: dark,
|
||||||
|
primary: theme-colors.$primary-palette,
|
||||||
|
tertiary: theme-colors.$tertiary-palette,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@include mat.progress-bar-overrides((
|
@include mat.progress-bar-overrides((
|
||||||
active-indicator-color: white,
|
active-indicator-color: white,
|
||||||
@@ -34,16 +61,9 @@
|
|||||||
backdrop-filter: blur(2px);
|
backdrop-filter: blur(2px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mat-step-header .mat-step-icon:not(.mat-step-icon-selected):not(.mat-step-icon-completed):not(.mat-step-icon-state-edit) {
|
||||||
html {
|
background-color: #bbb;
|
||||||
@include mat.theme((
|
color: white;
|
||||||
color: (
|
|
||||||
primary: mat.$azure-palette,
|
|
||||||
tertiary: mat.$blue-palette,
|
|
||||||
),
|
|
||||||
typography: Roboto,
|
|
||||||
density: 0,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap" rel="stylesheet">
|
||||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined" rel="stylesheet">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<app-root></app-root>
|
<app-root></app-root>
|
||||||
|
|||||||
Reference in New Issue
Block a user