diff --git a/angular.json b/angular.json index a96c275..95ed18c 100644 --- a/angular.json +++ b/angular.json @@ -38,7 +38,8 @@ "glob": "_redirects", "input": "src", "output": "/" - } + }, + "src/manifest.webmanifest" ], "stylePreprocessorOptions": { "includePaths": [ @@ -53,7 +54,9 @@ "src/styles/styles.scss", "src/styles/tailwind.scss" ], - "scripts": [] + "scripts": [], + "serviceWorker": true, + "ngswConfigPath": "ngsw-config.json" }, "configurations": { "production": { @@ -114,7 +117,8 @@ "assets": [ "src/favicon-16x16.png", "src/favicon-32x32.png", - "src/assets" + "src/assets", + "src/manifest.webmanifest" ], "styles": [ "src/styles/styles.scss" diff --git a/ngsw-config.json b/ngsw-config.json new file mode 100644 index 0000000..f8bf210 --- /dev/null +++ b/ngsw-config.json @@ -0,0 +1,30 @@ +{ + "$schema": "./node_modules/@angular/service-worker/config/schema.json", + "index": "/index.html", + "assetGroups": [ + { + "name": "app", + "installMode": "prefetch", + "resources": { + "files": [ + "/favicon.ico", + "/index.html", + "/manifest.webmanifest", + "/*.css", + "/*.js" + ] + } + }, + { + "name": "assets", + "installMode": "lazy", + "updateMode": "prefetch", + "resources": { + "files": [ + "/assets/**", + "/*.(svg|cur|jpg|jpeg|png|apng|webp|avif|gif|otf|ttf|woff|woff2)" + ] + } + } + ] +} diff --git a/package-lock.json b/package-lock.json index 6a96e4b..18f8667 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,6 +20,7 @@ "@angular/platform-browser": "13.0.2", "@angular/platform-browser-dynamic": "13.0.2", "@angular/router": "13.0.2", + "@angular/service-worker": "^13.0.2", "@costlydeveloper/ngx-awesome-popup": "^3.1.3", "@fullcalendar/angular": "4.4.5-beta", "@fullcalendar/core": "4.4.2", @@ -77,7 +78,9 @@ "@types/node": "12.20.21", "@typescript-eslint/eslint-plugin": "4.30.0", "@typescript-eslint/parser": "4.30.0", + "@webpixels/css": "^1.1.92", "autoprefixer": "10.3.3", + "bootstrap": "^5.1.3", "chroma-js": "2.1.2", "eslint": "7.32.0", "eslint-plugin-import": "2.24.2", @@ -238,20 +241,6 @@ } } }, - "node_modules/@angular-devkit/build-angular/node_modules/acorn": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", - "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", - "dev": true, - "optional": true, - "peer": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/@angular-devkit/build-angular/node_modules/autoprefixer": { "version": "10.4.2", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.2.tgz", @@ -1408,6 +1397,24 @@ "rxjs": "^6.5.3 || ^7.4.0" } }, + "node_modules/@angular/service-worker": { + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/@angular/service-worker/-/service-worker-13.0.2.tgz", + "integrity": "sha512-cH5adbvwI+cr6qKVdvNfQ5B0I//3NJwXkP04ifllPUIw7Te1pUfVb8eIN8UI/80aqG0uu/uIes7QoovSIayqOA==", + "dependencies": { + "tslib": "^2.3.0" + }, + "bin": { + "ngsw-config": "ngsw-config.js" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/common": "13.0.2", + "@angular/core": "13.0.2" + } + }, "node_modules/@assemblyscript/loader": { "version": "0.10.1", "resolved": "https://registry.npmjs.org/@assemblyscript/loader/-/loader-0.10.1.tgz", @@ -4488,6 +4495,16 @@ "@xtuc/long": "4.2.2" } }, + "node_modules/@webpixels/css": { + "version": "1.1.92", + "resolved": "https://registry.npmjs.org/@webpixels/css/-/css-1.1.92.tgz", + "integrity": "sha512-eFJIZAJxmRYes0XagsUMetsk6doBQR7yA064s2x60FpB7LdvgMktMq1eWG3qp9y4xwLOurvQXelffYPVQARmzA==", + "dev": true, + "peerDependencies": { + "@popperjs/core": "^2.9.2", + "bootstrap": "^5.1.3" + } + }, "node_modules/@xtuc/ieee754": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", @@ -5178,6 +5195,19 @@ "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", "dev": true }, + "node_modules/bootstrap": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.1.3.tgz", + "integrity": "sha512-fcQztozJ8jToQWXxVuEyXWW+dSo8AiXWKwiSSrKWsRB/Qt+Ewwza+JWoLKiTuQLaEPhdNAJ7+Dosc9DOIqNy7Q==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + }, + "peerDependencies": { + "@popperjs/core": "^2.10.2" + } + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -12269,9 +12299,12 @@ } }, "node_modules/prismjs": { - "version": "1.25.0", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.25.0.tgz", - "integrity": "sha512-WCjJHl1KEWbnkQom1+SzftbtXMKQoezOCYs5rECqMN+jP+apI7ftoflyqigqzopSO3hMhTEb0mFClA8lkolgEg==" + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.27.0.tgz", + "integrity": "sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==", + "engines": { + "node": ">=6" + } }, "node_modules/process-nextick-args": { "version": "2.0.1", @@ -15571,14 +15604,6 @@ "webpack-subresource-integrity": "5.1.0" }, "dependencies": { - "acorn": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", - "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", - "dev": true, - "optional": true, - "peer": true - }, "autoprefixer": { "version": "10.4.2", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.2.tgz", @@ -15621,8 +15646,7 @@ "version": "6.0.3", "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz", "integrity": "sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==", - "dev": true, - "requires": {} + "dev": true }, "postcss": { "version": "8.4.5", @@ -15711,8 +15735,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-3.0.3.tgz", "integrity": "sha512-rPPZRLPmEKgLk/KlXMqRaNkYTUpE7YC+bOIQFN5xcu1Vp11Y4faIXv6/Jpft6FMnl6YRxZqDZG0qQOW80stzxQ==", - "dev": true, - "requires": {} + "dev": true }, "postcss-image-set-function": { "version": "4.0.6", @@ -15736,15 +15759,13 @@ "version": "5.0.4", "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-5.0.4.tgz", "integrity": "sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==", - "dev": true, - "requires": {} + "dev": true }, "postcss-overflow-shorthand": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-3.0.3.tgz", "integrity": "sha512-CxZwoWup9KXzQeeIxtgOciQ00tDtnylYIlJBBODqkgS/PU2jISuWOL/mYLHmZb9ZhZiCaNKsCRiLp22dZUtNsg==", - "dev": true, - "requires": {} + "dev": true }, "postcss-place": { "version": "7.0.4", @@ -16291,6 +16312,14 @@ "tslib": "^2.3.0" } }, + "@angular/service-worker": { + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/@angular/service-worker/-/service-worker-13.0.2.tgz", + "integrity": "sha512-cH5adbvwI+cr6qKVdvNfQ5B0I//3NJwXkP04ifllPUIw7Te1pUfVb8eIN8UI/80aqG0uu/uIes7QoovSIayqOA==", + "requires": { + "tslib": "^2.3.0" + } + }, "@assemblyscript/loader": { "version": "0.10.1", "resolved": "https://registry.npmjs.org/@assemblyscript/loader/-/loader-0.10.1.tgz", @@ -17580,32 +17609,27 @@ "@fullcalendar/daygrid": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/@fullcalendar/daygrid/-/daygrid-4.4.2.tgz", - "integrity": "sha512-axjfMhxEXHShV3r2TZjf+2niJ1C6LdAxkHKmg7mVq4jXtUQHOldU5XsjV0v2lUAt1urJBFi2zajfK8798ukL3Q==", - "requires": {} + "integrity": "sha512-axjfMhxEXHShV3r2TZjf+2niJ1C6LdAxkHKmg7mVq4jXtUQHOldU5XsjV0v2lUAt1urJBFi2zajfK8798ukL3Q==" }, "@fullcalendar/interaction": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/@fullcalendar/interaction/-/interaction-4.4.2.tgz", - "integrity": "sha512-3ItpGFnxcYQT4NClqhq93QTQwOI8x3mlMf5M4DgK5avVaSzpv9g8p+opqeotK2yzpFeINps06cuQyB1h7vcv1Q==", - "requires": {} + "integrity": "sha512-3ItpGFnxcYQT4NClqhq93QTQwOI8x3mlMf5M4DgK5avVaSzpv9g8p+opqeotK2yzpFeINps06cuQyB1h7vcv1Q==" }, "@fullcalendar/list": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/@fullcalendar/list/-/list-4.4.2.tgz", - "integrity": "sha512-buhfd0w0PavH3EVZ6DR6kvjb+wPDe16XEpNcPkTpvIxnAziwGBvcUeHUBd9KvtEhOcvs9sAKoYKbU4xwHFK0Wg==", - "requires": {} + "integrity": "sha512-buhfd0w0PavH3EVZ6DR6kvjb+wPDe16XEpNcPkTpvIxnAziwGBvcUeHUBd9KvtEhOcvs9sAKoYKbU4xwHFK0Wg==" }, "@fullcalendar/moment": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/@fullcalendar/moment/-/moment-4.4.2.tgz", - "integrity": "sha512-PBrjxxDEG3RO+8SOA3a1YA7yoGI3bgnltiGY3ehOtJwFIMsUQDSSr5aMoWyRpz7MXgp2YOQY5rzMEIp2A8eK9w==", - "requires": {} + "integrity": "sha512-PBrjxxDEG3RO+8SOA3a1YA7yoGI3bgnltiGY3ehOtJwFIMsUQDSSr5aMoWyRpz7MXgp2YOQY5rzMEIp2A8eK9w==" }, "@fullcalendar/rrule": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/@fullcalendar/rrule/-/rrule-4.4.2.tgz", - "integrity": "sha512-pUKHFp62SZbW9X3vvxc8IMnoWpQ6Nt2IBwwPFPAWmebCnUhyDfMf3tpKaV9slUYvW0Cch4Y58tv0EySP27Q2jg==", - "requires": {} + "integrity": "sha512-pUKHFp62SZbW9X3vvxc8IMnoWpQ6Nt2IBwwPFPAWmebCnUhyDfMf3tpKaV9slUYvW0Cch4Y58tv0EySP27Q2jg==" }, "@fullcalendar/timegrid": { "version": "4.4.2", @@ -17703,8 +17727,7 @@ "version": "13.2.2", "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-13.2.2.tgz", "integrity": "sha512-BkykqIIJgSllNT6f+8IoZtJaI8uKdq62LteoWMMtvAxXErqRLN6CcDxPWjP8tfeEKeCYZ41ueZwNK5V05SsCIg==", - "dev": true, - "requires": {} + "dev": true }, "@nodelib/fs.scandir": { "version": "2.1.5", @@ -17957,15 +17980,13 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/@tailwindcss/aspect-ratio/-/aspect-ratio-0.2.1.tgz", "integrity": "sha512-aDFi80aHQ3JM3symJ5iKU70lm151ugIGFCI0yRZGpyjgQSDS+Fbe93QwypC1tCEllQE8p0S7TUu20ih1b9IKLA==", - "dev": true, - "requires": {} + "dev": true }, "@tailwindcss/line-clamp": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/@tailwindcss/line-clamp/-/line-clamp-0.2.1.tgz", "integrity": "sha512-Hq2KJY1+T2v7jw/mnT3mnC7CKbp5kj1XTqzSb2xbEt1j+JkxIR6N3ijsN/WevZtsKJfVE1KOejA/3IRKuhZEsQ==", - "dev": true, - "requires": {} + "dev": true }, "@tailwindcss/typography": { "version": "0.4.1", @@ -18568,6 +18589,12 @@ "@xtuc/long": "4.2.2" } }, + "@webpixels/css": { + "version": "1.1.92", + "resolved": "https://registry.npmjs.org/@webpixels/css/-/css-1.1.92.tgz", + "integrity": "sha512-eFJIZAJxmRYes0XagsUMetsk6doBQR7yA064s2x60FpB7LdvgMktMq1eWG3qp9y4xwLOurvQXelffYPVQARmzA==", + "dev": true + }, "@xtuc/ieee754": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", @@ -18618,15 +18645,13 @@ "version": "1.8.0", "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", - "dev": true, - "requires": {} + "dev": true }, "acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "requires": {} + "dev": true }, "acorn-node": { "version": "1.8.2", @@ -18723,8 +18748,7 @@ "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "requires": {} + "dev": true }, "ansi-colors": { "version": "4.1.1", @@ -19097,6 +19121,12 @@ "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", "dev": true }, + "bootstrap": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.1.3.tgz", + "integrity": "sha512-fcQztozJ8jToQWXxVuEyXWW+dSo8AiXWKwiSSrKWsRB/Qt+Ewwza+JWoLKiTuQLaEPhdNAJ7+Dosc9DOIqNy7Q==", + "dev": true + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -19281,8 +19311,7 @@ "version": "5.2.2", "resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-5.2.2.tgz", "integrity": "sha512-g38K9Cm5WRwlaH6g03B9OEz/0qRizI+2I7n+Gz+L5DxXJAPAiWQvwlYNm1V1jkdpUv95bOe/ASm2vfi/G560jQ==", - "dev": true, - "requires": {} + "dev": true }, "clean-stack": { "version": "2.2.0", @@ -20910,8 +20939,7 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/eslint-plugin-prefer-arrow/-/eslint-plugin-prefer-arrow-1.2.3.tgz", "integrity": "sha512-J9I5PKCOJretVuiZRGvPQxCbllxGAV/viI20JO3LYblAodofBxyMnZAJ+WGeClHgANnSJberTNoFWWjrWKBuXQ==", - "dev": true, - "requires": {} + "dev": true }, "eslint-scope": { "version": "5.1.1", @@ -21750,8 +21778,7 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", - "dev": true, - "requires": {} + "dev": true }, "ieee754": { "version": "1.2.1", @@ -22564,8 +22591,7 @@ "version": "1.7.0", "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-1.7.0.tgz", "integrity": "sha512-pzum1TL7j90DTE86eFt48/s12hqwQuiD+e5aXx2Dc9wDEn2LfGq6RoAxEZZjFiN0RDSCOnosEKRZWxbQ+iMpQQ==", - "dev": true, - "requires": {} + "dev": true }, "karma-source-map-support": { "version": "1.4.0", @@ -24181,8 +24207,7 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-8.0.0.tgz", "integrity": "sha512-FvO2GzMUaTN0t1fBULDeIvxr5IvbDXcIatt6pnJghc736nqNgsGao5NT+5+WVLAQiTt6Cb3YUms0jiPaXhL//g==", - "dev": true, - "requires": {} + "dev": true }, "postcss-custom-selectors": { "version": "6.0.0", @@ -24197,8 +24222,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz", "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==", - "dev": true, - "requires": {} + "dev": true }, "postcss-import": { "version": "14.0.2", @@ -24215,8 +24239,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-4.0.1.tgz", "integrity": "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==", - "dev": true, - "requires": {} + "dev": true }, "postcss-js": { "version": "3.0.3", @@ -24269,15 +24292,13 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-5.0.0.tgz", "integrity": "sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==", - "dev": true, - "requires": {} + "dev": true }, "postcss-modules-extract-imports": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", - "dev": true, - "requires": {} + "dev": true }, "postcss-modules-local-by-default": { "version": "4.0.0", @@ -24330,15 +24351,13 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz", "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==", - "dev": true, - "requires": {} + "dev": true }, "postcss-replace-overflow-wrap": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz", "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==", - "dev": true, - "requires": {} + "dev": true }, "postcss-selector-not": { "version": "5.0.0", @@ -24384,9 +24403,9 @@ "dev": true }, "prismjs": { - "version": "1.25.0", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.25.0.tgz", - "integrity": "sha512-WCjJHl1KEWbnkQom1+SzftbtXMKQoezOCYs5rECqMN+jP+apI7ftoflyqigqzopSO3hMhTEb0mFClA8lkolgEg==" + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.27.0.tgz", + "integrity": "sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==" }, "process-nextick-args": { "version": "2.0.1", @@ -25051,8 +25070,7 @@ "version": "0.0.2", "resolved": "https://registry.npmjs.org/rxjs-for-await/-/rxjs-for-await-0.0.2.tgz", "integrity": "sha512-IJ8R/ZCFMHOcDIqoABs82jal00VrZx8Xkgfe7TOKoaRPAW5nH/VFlG23bXpeGdrmtqI9UobFPgUKgCuFc7Lncw==", - "dev": true, - "requires": {} + "dev": true }, "safe-buffer": { "version": "5.1.2", @@ -26601,8 +26619,7 @@ "version": "8.4.2", "resolved": "https://registry.npmjs.org/ws/-/ws-8.4.2.tgz", "integrity": "sha512-Kbk4Nxyq7/ZWqr/tarI9yIt/+iNNFOjBXEWgTb4ydaNHBNGgvf2QHbS9fdfsndfjFlFwEd4Al+mw83YkaD10ZA==", - "dev": true, - "requires": {} + "dev": true } } }, @@ -26742,8 +26759,7 @@ "version": "8.2.3", "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", - "dev": true, - "requires": {} + "dev": true }, "xtend": { "version": "4.0.2", diff --git a/package.json b/package.json index b639833..8282a4e 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "@angular/platform-browser": "13.0.2", "@angular/platform-browser-dynamic": "13.0.2", "@angular/router": "13.0.2", + "@angular/service-worker": "^13.0.2", "@costlydeveloper/ngx-awesome-popup": "^3.1.3", "@fullcalendar/angular": "4.4.5-beta", "@fullcalendar/core": "4.4.2", @@ -81,7 +82,9 @@ "@types/node": "12.20.21", "@typescript-eslint/eslint-plugin": "4.30.0", "@typescript-eslint/parser": "4.30.0", + "@webpixels/css": "^1.1.92", "autoprefixer": "10.3.3", + "bootstrap": "^5.1.3", "chroma-js": "2.1.2", "eslint": "7.32.0", "eslint-plugin-import": "2.24.2", diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 510aec1..b7d7a61 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,17 +1,34 @@ -import { Component } from '@angular/core'; +import { Component, OnInit, ViewEncapsulation } from '@angular/core'; +import { SwUpdate } from '@angular/service-worker'; +import { interval } from 'rxjs'; @Component({ - selector : 'app-root', + selector: 'app-root', templateUrl: './app.component.html', - styleUrls : ['./app.component.scss'] + styleUrls: ['./app.component.scss'], + encapsulation: ViewEncapsulation.None }) -export class AppComponent -{ - /** - * Constructor - */ - constructor() - { +export class AppComponent implements OnInit { + hasUpdate = false; + + constructor( + private swUpdate: SwUpdate, + ) { } + ngOnInit(): void { + // check for platform update + if (this.swUpdate.isEnabled) { + interval(60000).subscribe(() => this.swUpdate.checkForUpdate().then(() => { + // checking for updates + })); + } + this.swUpdate.available.subscribe(() => { + this.hasUpdate = true; + }); + } + + reloadSite(): void { + location.reload(); + } } diff --git a/src/app/app.module.ts b/src/app/app.module.ts index ec66f1e..1c3241f 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -16,6 +16,10 @@ import { tesoModule } from '@teso/teso.module'; import { NgxAwesomePopupModule,ConfirmBoxConfigModule} from '@costlydeveloper/ngx-awesome-popup'; import { ProductDescriptionShort } from './pipes/productDescriptionShort.pipe'; import { CouponWorthPipe } from './pipes/coupon-worth.pipe'; +import { GoldTransactionComponent } from './pages/admin/GoldPurchase/gold-transaction.component'; +import { ServiceWorkerModule } from '@angular/service-worker'; +import { environment } from 'environments/environment.prod'; +import { TempShopModule } from './pages/admin/TempShop/temp-shop.module'; const routerConfig: ExtraOptions = { preloadingStrategy : PreloadAllModules, @@ -25,6 +29,7 @@ const routerConfig: ExtraOptions = { @NgModule({ declarations: [ AppComponent, + GoldTransactionComponent, ], imports : [ @@ -37,7 +42,7 @@ const routerConfig: ExtraOptions = { tesoModule, tesoConfigModule.forRoot(appConfig), tesoMockApiModule.forRoot(mockApiServices), - +TempShopModule, // Core module of your application CoreModule, @@ -45,7 +50,13 @@ const routerConfig: ExtraOptions = { LayoutModule, // 3rd party modules that require global configuration via forRoot - MarkdownModule.forRoot({}) + MarkdownModule.forRoot({}), + ServiceWorkerModule.register('ngsw-worker.js', { + enabled: environment.production, + // Register the ServiceWorker as soon as the app is stable + // or after 30 seconds (whichever comes first). + registrationStrategy: 'registerWhenStable:30000' + }) ], providers: [ { provide: LocationStrategy, useClass: PathLocationStrategy }, diff --git a/src/app/core/auth/auth.interceptor.ts b/src/app/core/auth/auth.interceptor.ts index 8c07b1e..8489e71 100644 --- a/src/app/core/auth/auth.interceptor.ts +++ b/src/app/core/auth/auth.interceptor.ts @@ -36,10 +36,10 @@ export class AuthInterceptor implements HttpInterceptor { newReq = req.clone({ headers: req.headers.set('Authorization', 'Bearer ' + this._authService.relevantToken) .append('Accept', '*/*') - .append('Content-Type', 'application/json; charset=utf-8;') - .append("Access-Control-Allow-Origin", "*") - .append("Access-Control-Allow-Methods", "DELETE, POST, GET, OPTIONS") - .append("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Requested-With") + // .append('Content-Type', 'application/json; charset=utf-8;') + // .append("Access-Control-Allow-Origin", "*") + // .append("Access-Control-Allow-Methods", "DELETE, POST, GET, OPTIONS") + // .append("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Requested-With") }); } diff --git a/src/app/core/auth/auth.service.ts b/src/app/core/auth/auth.service.ts index 627463e..1eefecc 100644 --- a/src/app/core/auth/auth.service.ts +++ b/src/app/core/auth/auth.service.ts @@ -6,8 +6,7 @@ import { AuthUtils } from 'app/core/auth/auth.utils'; import { UserService } from 'app/core/user/user.service'; @Injectable() -export class AuthService -{ +export class AuthService { private _authenticated: boolean = false; /** @@ -16,8 +15,7 @@ export class AuthService constructor( private _httpClient: HttpClient, private _userService: UserService - ) - { + ) { } // ----------------------------------------------------------------------------------------------------- @@ -27,23 +25,19 @@ export class AuthService /** * Setter & getter for access token */ - set accessToken(token: string) - { + set accessToken(token: string) { localStorage.setItem('accessToken', token); } - get accessToken(): string - { + get accessToken(): string { return localStorage.getItem('accessToken') ?? ''; } - set relevantToken(token: string) - { + set relevantToken(token: string) { localStorage.setItem('relevantToken', token); } - get relevantToken(): string - { + get relevantToken(): string { return localStorage.getItem('relevantToken') ?? ''; } @@ -56,8 +50,7 @@ export class AuthService * * @param email */ - forgotPassword(email: string): Observable - { + forgotPassword(email: string): Observable { return this._httpClient.post('api/auth/forgot-password', email); } @@ -66,8 +59,7 @@ export class AuthService * * @param password */ - resetPassword(password: string): Observable - { + resetPassword(password: string): Observable { return this._httpClient.post('api/auth/reset-password', password); } @@ -76,11 +68,9 @@ export class AuthService * * @param credentials */ - signIn(credentials: { email: string; password: string }): Observable - { + signIn(credentials: { email: string; password: string }): Observable { // Throw error, if the user is already logged in - if ( this._authenticated ) - { + if (this._authenticated) { return throwError('User is already logged in.'); } @@ -89,7 +79,7 @@ export class AuthService // Store the access token in the local storage this.accessToken = response.accessToken; -this.relevantToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOiIyMjY0ODQyNzciLCJqdGkiOiI2ZWJiYzhlZC1hNTFkLTRjNTMtODBkOC05YWU2OTYyMzU4M2YiLCJpc3MiOiJURVNPIEFVVEggU0VSVkVSIiwiZXhwIjoxNjQ2NDA4NTU4LCJuYmYiOiIyMjY1NDQyNzciLCJzdWIiOiIxVEVTQlUwMDAwMDAwMCIsImJ1c2luZXNzSUQiOiIxVEVTQlUwMDAwMDAwMCIsInN1YnNjcmlwdGlvblBsYW4iOiJUVFMwMDEiLCJidXNpbmVzc05hbWUiOiJUZXNvIEdoYW5hIiwiYXVkIjoiVEVTTyJ9.HPgBtbrw8yFzzCUwMx7_3QbfX7LloANFZ-HFzLglDio"; + this.relevantToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOiItMTU5MDU0MDI0NCIsImp0aSI6IjRiYzI2MzdlLWY5OGEtNGZhOS04YmQ3LWNhOWYxYzQzYzAyNyIsImlzcyI6IlRFU08gQVVUSCBTRVJWRVIiLCJleHAiOjE2NDg4ODY1MDEsIm5iZiI6Ii0xNTkwNDgwMjQ0Iiwic3ViIjoiMVRFU0JVMDAwMDAwMDAiLCJidXNpbmVzc0lEIjoiMVRFU0JVMDAwMDAwMDAiLCJzdWJzY3JpcHRpb25QbGFuIjoiVFRTMDAxIiwiYnVzaW5lc3NOYW1lIjoiVGVzbyBHaGFuYSIsImF1ZCI6IlRFU08ifQ.mQg8CylBYrvA-L2570Rr-NOqIuuPeNhHAzorN6OW_Rs"; // Set the authenticated flag to true this._authenticated = true; @@ -105,8 +95,7 @@ this.relevantToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOiIyMjY0ODQyN /** * Sign in using the access token */ - signInUsingToken(): Observable - { + signInUsingToken(): Observable { // Renew token return this._httpClient.post('api/auth/refresh-access-token', { accessToken: this.accessToken @@ -136,8 +125,7 @@ this.relevantToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOiIyMjY0ODQyN /** * Sign out */ - signOut(): Observable - { + signOut(): Observable { // Remove the access token from the local storage localStorage.removeItem('accessToken'); @@ -153,8 +141,7 @@ this.relevantToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOiIyMjY0ODQyN * * @param user */ - signUp(user: { name: string; email: string; password: string; company: string }): Observable - { + signUp(user: { name: string; email: string; password: string; company: string }): Observable { return this._httpClient.post('api/auth/sign-up', user); } @@ -163,15 +150,14 @@ this.relevantToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOiIyMjY0ODQyN * * @param credentials */ - unlockSession(credentials: { email: string; password: string }): Observable - { + unlockSession(credentials: { email: string; password: string }): Observable { return this._httpClient.post('api/auth/unlock-session', credentials); } /** * Check the authentication status */ - check(): Observable { + check(): Observable { // Check if the user is logged in if (this._authenticated) { return of(true); diff --git a/src/app/models/generalModel.ts b/src/app/models/generalModel.ts index d8731a2..4d34826 100644 --- a/src/app/models/generalModel.ts +++ b/src/app/models/generalModel.ts @@ -2,4 +2,5 @@ export interface ProductUpload { highlight: boolean; file: File; imageSRC:string; + id?:string; } \ No newline at end of file diff --git a/src/app/pages/admin/Coupons/Active/active-coupons.component.html b/src/app/pages/admin/Coupons/Active/active-coupons.component.html index e638750..ed0fd85 100644 --- a/src/app/pages/admin/Coupons/Active/active-coupons.component.html +++ b/src/app/pages/admin/Coupons/Active/active-coupons.component.html @@ -1,7 +1,7 @@
+ #couponsTable *ngIf="!isScreenSmall"> @@ -51,10 +51,12 @@ Range @@ -96,15 +98,15 @@ @@ -113,7 +115,7 @@ @@ -121,5 +123,65 @@
@@ -9,7 +9,7 @@ - {{transaction.targetProduct}} + {{transaction.targetProduct |couponProductShort}} - - {{transaction.lower}} - {{transaction.upper}} + + {{transaction.lower}} % - {{transaction.upper}} % - + 100% OFF
- - +
+ [pageSizeOptions]="[10, 20, 25,50, 100]" [length]="couponsDataSource.data.length" [showFirstLastButtons]="true">
+ + + + + + + + + + + + + + + + + + + + + + +
+ Coupons + +
+ + {{transaction.targetProduct |couponProductShort}} + + + {{transaction.quantity}} left + + + {{transaction.lower}} - {{transaction.upper}} + + + + {{transaction.type}} + +
+
+ + +
+ + Active + + + {{transaction.expiration | date:"dd-MM-yyyy"}} + +
+
+ +
\ No newline at end of file diff --git a/src/app/pages/admin/Coupons/Active/active-coupons.component.ts b/src/app/pages/admin/Coupons/Active/active-coupons.component.ts index 82212cd..2b815b5 100644 --- a/src/app/pages/admin/Coupons/Active/active-coupons.component.ts +++ b/src/app/pages/admin/Coupons/Active/active-coupons.component.ts @@ -1,6 +1,15 @@ -import { ChangeDetectionStrategy, Component, Input, OnInit, ViewChild } from '@angular/core'; +import { AfterViewInit, ChangeDetectionStrategy, Component, Input, OnInit, ViewChild } from '@angular/core'; +import { MatDialog } from '@angular/material/dialog'; +import { MatPaginator } from '@angular/material/paginator'; import { MatSort } from '@angular/material/sort'; import { MatTableDataSource } from '@angular/material/table'; +import { ConfirmBoxEvokeService } from '@costlydeveloper/ngx-awesome-popup'; +import { tesoMediaWatcherService } from '@teso/services/media-watcher'; +import { CouponsModel } from 'app/models/couponsModel'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; +import { CouponsService } from '../coupons.service'; +import { DetailsDialogComponent } from '../DetailsDialog/details-dialog.component'; @Component({ selector: 'activecoupons', @@ -9,15 +18,56 @@ import { MatTableDataSource } from '@angular/material/table'; changeDetection: ChangeDetectionStrategy.OnPush, exportAs: 'activecoupons' }) -export class ActiveCouponsComponent implements OnInit { +export class ActiveCouponsComponent implements OnInit, AfterViewInit { @Input() couponsDataSource: MatTableDataSource; couponsTableColumns: string[] = ['product', 'quantity', 'type', 'condition', 'range', 'claimed', 'status', 'expiration', 'actions']; + couponsTableColumnsMobile: string[] = ['product', 'status']; @ViewChild('couponsTable', { read: MatSort }) couponsTableMatSort: MatSort; - constructor() { } + isScreenSmall: boolean; + @ViewChild(MatPaginator) paginator: MatPaginator; + private _unsubscribeAll: Subject = new Subject(); + + constructor(private _tesoMediaWatcherService: tesoMediaWatcherService, private confirmBoxEvokeService: ConfirmBoxEvokeService,public dialog: MatDialog, + private _couponsService: CouponsService) { } + + ngAfterViewInit(): void { + this.couponsDataSource.paginator = this.paginator; + } ngOnInit(): void { + this._tesoMediaWatcherService.onMediaChange$ + .pipe(takeUntil(this._unsubscribeAll)) + .subscribe(({ matchingAliases }) => { + + // Check if the screen is small + this.isScreenSmall = !matchingAliases.includes('md'); + }); } + trackByFn(index: number, item: any): any { return item.id || index; } + + ngOnDestroy(): void { + // Unsubscribe from all subscriptions + this._unsubscribeAll.next(); + this._unsubscribeAll.complete(); + } + + cancel(item: CouponsModel) { + this._couponsService.cancelCoupon(item).subscribe((response) => { + this.confirmBoxEvokeService.info("Coupon Cancelled", `Coupon for ${item.targetProduct} has been cancelled successfully`, "OK") + .subscribe(response => { + window.location.reload(); + }); + }); + } + showDetails(item:CouponsModel){ + const dialogRef = this.dialog.open(DetailsDialogComponent, { + disableClose: true, + hasBackdrop: true, + data: { coupon: item }, + + }); + } } diff --git a/src/app/pages/admin/Coupons/AdminNewCoupons/admin-new-coupons.component.html b/src/app/pages/admin/Coupons/AdminNewCoupons/admin-new-coupons.component.html index e3e997a..a4c7f2e 100644 --- a/src/app/pages/admin/Coupons/AdminNewCoupons/admin-new-coupons.component.html +++ b/src/app/pages/admin/Coupons/AdminNewCoupons/admin-new-coupons.component.html @@ -59,8 +59,8 @@
- - + + {{type.typeName}} @@ -76,7 +76,8 @@
- +
@@ -87,51 +88,76 @@
- +
-
+
Percentage Off From   - + %   To   - + %  
-
+
+
+
+ + Percentage Off + +
+
+ + Free + +
+ +
+
+
Percentage Off
From   - + %  
To   - + %  
+
+ + Percentage Off + +
+ FREE +
+
Coupon Worth - From {{selectedProduct.unitPrice | couponWorth:fromWorth}} To {{selectedProduct.unitPrice | couponWorth:toWorth}} + From {{selectedProduct.unitPrice | couponWorth:newCoupon.lower}} To {{selectedProduct.unitPrice | + couponWorth:newCoupon.upper}}
@@ -139,31 +165,32 @@ Date Of Expiration - + Time   - +
-
- - - Date Of Expiration - - - -
-
- - Time   - - +
+ + + Date Of Expiration + + + +
+
+ + Time   + + +
-
- +
diff --git a/src/app/pages/admin/Coupons/AdminNewCoupons/admin-new-coupons.component.ts b/src/app/pages/admin/Coupons/AdminNewCoupons/admin-new-coupons.component.ts index eb3b99d..8097a9e 100644 --- a/src/app/pages/admin/Coupons/AdminNewCoupons/admin-new-coupons.component.ts +++ b/src/app/pages/admin/Coupons/AdminNewCoupons/admin-new-coupons.component.ts @@ -1,8 +1,9 @@ import { Component, Inject, OnDestroy, OnInit } from '@angular/core'; -import { MAT_DIALOG_DATA, MatDialog } from '@angular/material/dialog'; +import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog'; +import { ConfirmBoxEvokeService } from '@costlydeveloper/ngx-awesome-popup'; import { tesoMediaWatcherService } from '@teso/services/media-watcher'; import { TesoBusinessDetail } from 'app/models/businessModel'; -import { CouponsType } from 'app/models/couponsModel'; +import { CouponsModel, CouponsType } from 'app/models/couponsModel'; import { ProductsModel } from 'app/models/productsModel'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; @@ -18,21 +19,38 @@ import { CouponsService } from '../coupons.service'; export class AdminNewCouponsComponent implements OnInit, OnDestroy { businessTargetted: TesoBusinessDetail = {}; products: ProductsModel[] = []; - fromWorth:number; - toWorth:number; + selectedProduct: any = {}; selectedType: any = {}; isLoading: boolean = false; isScreenSmall: boolean; couponTypes: CouponsType[] = []; + newCoupon: CouponsModel = { + couponID: '', + businessID: '', + targetProduct: '', + type: '', + quantity: 0, + lower: 0, + condition: '', + upper: 0, + numberClaimed: 0, + status: '', + expiration: new Date(), + }; + expiryDate: Date = new Date(); + expiryTime: Date = new Date(); private _unsubscribeAll: Subject = new Subject(); constructor(@Inject(MAT_DIALOG_DATA) public data: { product: any }, public dialog: MatDialog, private _productService: ProductsService, - private _couponService: CouponsService, private _tesoMediaWatcherService: tesoMediaWatcherService,) { } + private _couponService: CouponsService, private _tesoMediaWatcherService: tesoMediaWatcherService, + public dialogRef: MatDialogRef, + private confirmBoxEvokeService: ConfirmBoxEvokeService,) { } ngOnInit(): void { this._couponService.categories$.pipe(takeUntil(this._unsubscribeAll)).subscribe((d) => { this.couponTypes = d; + console.log(d) }); this._productService.data$.pipe(takeUntil(this._unsubscribeAll)).subscribe((d) => { this.products = d; @@ -55,9 +73,23 @@ export class AdminNewCouponsComponent implements OnInit, OnDestroy { this.businessTargetted = result; }); } + + ngOnDestroy(): void { // Unsubscribe from all subscriptions this._unsubscribeAll.next(); this._unsubscribeAll.complete(); } + generateCoupon() { + this.newCoupon.targetProduct = this.selectedProduct.productID; + this.newCoupon.target = this.businessTargetted; + this.newCoupon.expiration = new Date(this.expiryDate.toString() + " " + this.expiryTime.toString()); + this._couponService.generateCoupon(this.newCoupon).subscribe(() => { + this.confirmBoxEvokeService.success("Coupons Generated", `Coupons generated for ${this.selectedProduct.productName} successfully`, "OK").subscribe(response => { + window.location.reload(); + this.dialogRef.close(); + }); + }); + + } } diff --git a/src/app/pages/admin/Coupons/DetailsDialog/details-dialog.component.html b/src/app/pages/admin/Coupons/DetailsDialog/details-dialog.component.html new file mode 100644 index 0000000..e21bada --- /dev/null +++ b/src/app/pages/admin/Coupons/DetailsDialog/details-dialog.component.html @@ -0,0 +1,142 @@ +
+
+

Coupons For {{newCoupon.targetProduct}}

+
+ + +
+ +
+
+
+ + Product Name + +
+
+ + {{newCoupon.targetProduct}} + +
+
+
+
+ + Original Price + +
+
+ + {{selectedProduct.unitPrice | currency:"GHS "}} + +
+
+
+
+ + Coupon Type + +
+
+ + {{newCoupon.type}} + +
+
+ +
+
+ + Coupon Condition + +
+
+ +
+
+
+
+ + Number Of Coupons + +
+
+ + + +
+ +
+
+
+
+ + Percentage Off + +
+
+ + From {{newCoupon.lower}}% To {{newCoupon.upper}}% + +
+
+
+
+ + Percentage Off + +
+ + From   + + %   + +
+
+ + To   + + %   + +
+
+
+
+ + Coupon Worth + +
+
+ + From {{selectedProduct.unitPrice | couponWorth:newCoupon.lower}} To {{selectedProduct.unitPrice | + couponWorth:newCoupon.upper}} + +
+
+ +
+
+ + Date Of Expiration + +
+
+ + {{newCoupon.expiration | date:"medium"}} + +
+
+
+
+ + Date Of Expiration + + + {{newCoupon.expiration | date:"medium"}} + +
+
+
+ +
\ No newline at end of file diff --git a/src/app/pages/admin/Coupons/DetailsDialog/details-dialog.component.scss b/src/app/pages/admin/Coupons/DetailsDialog/details-dialog.component.scss new file mode 100644 index 0000000..4ec9e31 --- /dev/null +++ b/src/app/pages/admin/Coupons/DetailsDialog/details-dialog.component.scss @@ -0,0 +1,11 @@ +.columns { + display: flex; + flex-direction: row; + flex-wrap: wrap; + width: 100%; +} + +.column { + flex: 50%; + font-size: medium; +} \ No newline at end of file diff --git a/src/app/pages/admin/Monthly_Desires/desires.component.spec.ts b/src/app/pages/admin/Coupons/DetailsDialog/details-dialog.component.spec.ts similarity index 52% rename from src/app/pages/admin/Monthly_Desires/desires.component.spec.ts rename to src/app/pages/admin/Coupons/DetailsDialog/details-dialog.component.spec.ts index e9f31f1..3ce4041 100644 --- a/src/app/pages/admin/Monthly_Desires/desires.component.spec.ts +++ b/src/app/pages/admin/Coupons/DetailsDialog/details-dialog.component.spec.ts @@ -1,20 +1,20 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { DesiresComponent } from './desires.component'; +import { DetailsDialogComponent } from './details-dialog.component'; -describe('DesiresComponent', () => { - let component: DesiresComponent; - let fixture: ComponentFixture; +describe('DetailsDialogComponent', () => { + let component: DetailsDialogComponent; + let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ DesiresComponent ] + declarations: [ DetailsDialogComponent ] }) .compileComponents(); }); beforeEach(() => { - fixture = TestBed.createComponent(DesiresComponent); + fixture = TestBed.createComponent(DetailsDialogComponent); component = fixture.componentInstance; fixture.detectChanges(); }); diff --git a/src/app/pages/admin/Coupons/DetailsDialog/details-dialog.component.ts b/src/app/pages/admin/Coupons/DetailsDialog/details-dialog.component.ts new file mode 100644 index 0000000..d6302fa --- /dev/null +++ b/src/app/pages/admin/Coupons/DetailsDialog/details-dialog.component.ts @@ -0,0 +1,69 @@ +import { Component, Inject, OnInit } from '@angular/core'; +import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { ConfirmBoxEvokeService } from '@costlydeveloper/ngx-awesome-popup'; +import { tesoMediaWatcherService } from '@teso/services/media-watcher'; +import { CouponsType, CouponsModel } from 'app/models/couponsModel'; +import { ProductsModel } from 'app/models/productsModel'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; +import { ProductsService } from '../../Products/products.service'; +import { CouponsService } from '../coupons.service'; +import { NewCouponsComponent } from '../NewCoupons/new-coupons.component'; + +@Component({ + selector: 'app-details-dialog', + templateUrl: './details-dialog.component.html', + styleUrls: ['./details-dialog.component.scss'] +}) +export class DetailsDialogComponent implements OnInit { + products: ProductsModel[] = []; + selectedProduct: ProductsModel = { + productName: '', + businessID: '', + productDesc: '', + productID: '', + categoryID: '', + unitPrice: 0, + productImage: '', + images: [] + }; + selectedType: any = {}; + isLoading: boolean = false; + couponTypes: CouponsType[] = []; + fromWorth: number; + toWorth: number; + isScreenSmall: boolean; + newCoupon: CouponsModel = { + couponID: '', + businessID: '', + targetProduct: '', + type: '', + quantity: 0, + lower: 0, + condition: '', + upper: 0, + numberClaimed: 0, + status: '', + expiration: new Date(), + }; + expiryDate: Date = new Date(); + expiryTime: Date = new Date(); + private _unsubscribeAll: Subject = new Subject(); + + constructor( + public dialog: MatDialog, private _productService: ProductsService, + private _couponService: CouponsService, private _tesoMediaWatcherService: tesoMediaWatcherService, + public dialogRef: MatDialogRef,@Inject(MAT_DIALOG_DATA) public data: { coupon: CouponsModel }, + private confirmBoxEvokeService: ConfirmBoxEvokeService,) { + this.newCoupon = data.coupon; + this._productService.data$.pipe(takeUntil(this._unsubscribeAll)).subscribe((d) => { + this.products = d; + this.selectedProduct = this.products.find((p)=> p.productName == this.newCoupon.targetProduct); + }); + console.log(this.newCoupon) + } + + ngOnInit(): void { + } + +} diff --git a/src/app/pages/admin/Coupons/Inactive/inactive-coupons.component.html b/src/app/pages/admin/Coupons/Inactive/inactive-coupons.component.html index 4dd2758..3987112 100644 --- a/src/app/pages/admin/Coupons/Inactive/inactive-coupons.component.html +++ b/src/app/pages/admin/Coupons/Inactive/inactive-coupons.component.html @@ -9,7 +9,7 @@ - {{transaction.targetProduct}} + {{transaction.targetProduct | couponProductShort}} @@ -52,7 +52,7 @@ - {{transaction.lower}} - {{transaction.upper}} + {{transaction.lower}}% - {{transaction.upper}}% 100% OFF @@ -74,9 +74,12 @@ Status - + Expired + + Cancelled + diff --git a/src/app/pages/admin/Coupons/Inactive/inactive-coupons.component.ts b/src/app/pages/admin/Coupons/Inactive/inactive-coupons.component.ts index c627e52..f5b2eec 100644 --- a/src/app/pages/admin/Coupons/Inactive/inactive-coupons.component.ts +++ b/src/app/pages/admin/Coupons/Inactive/inactive-coupons.component.ts @@ -1,4 +1,5 @@ -import { ChangeDetectionStrategy, Component, Input, OnInit, ViewChild } from '@angular/core'; +import { AfterViewInit, ChangeDetectionStrategy, Component, Input, OnInit, ViewChild } from '@angular/core'; +import { MatPaginator } from '@angular/material/paginator'; import { MatSort } from '@angular/material/sort'; import { MatTableDataSource } from '@angular/material/table'; @@ -9,12 +10,16 @@ import { MatTableDataSource } from '@angular/material/table'; changeDetection: ChangeDetectionStrategy.OnPush, exportAs: 'inactivecoupons' }) -export class InactiveCouponsComponent implements OnInit { +export class InactiveCouponsComponent implements OnInit,AfterViewInit { @Input() couponsDataSource: MatTableDataSource; couponsTableColumns: string[] = ['product', 'quantity', 'type', 'condition', 'range', 'claimed', 'status', 'expiration', 'actions']; @ViewChild('couponsTable', { read: MatSort }) couponsTableMatSort: MatSort; + @ViewChild(MatPaginator) paginator: MatPaginator; constructor() { } + ngAfterViewInit(): void { + this.couponsDataSource.paginator = this.paginator; + } ngOnInit(): void { } trackByFn(index: number, item: any): any { diff --git a/src/app/pages/admin/Coupons/NewCoupons/new-coupons.component.html b/src/app/pages/admin/Coupons/NewCoupons/new-coupons.component.html index a9aa5b6..e3f772e 100644 --- a/src/app/pages/admin/Coupons/NewCoupons/new-coupons.component.html +++ b/src/app/pages/admin/Coupons/NewCoupons/new-coupons.component.html @@ -44,8 +44,8 @@
- - + + {{type.typeName}} @@ -61,7 +61,7 @@
- +
@@ -72,52 +72,76 @@
- +
-
+
Percentage Off From   - + %   To   - + %  
-
+
+
+
+ + Percentage Off + +
+
+ + Free + +
+ +
+
+
Percentage Off
From   - + %  
To   - + %  
+
+ + Percentage Off + +
+ FREE +
+
Coupon Worth - From {{selectedProduct.unitPrice | couponWorth:fromWorth}} To {{selectedProduct.unitPrice | - couponWorth:toWorth}} + From {{selectedProduct.unitPrice | couponWorth:newCoupon.lower}} To {{selectedProduct.unitPrice | + couponWorth:newCoupon.upper}}
@@ -125,11 +149,11 @@ Date Of Expiration - + Time   - +
@@ -138,18 +162,18 @@ Date Of Expiration - +
Time   - +
- +
diff --git a/src/app/pages/admin/Coupons/NewCoupons/new-coupons.component.ts b/src/app/pages/admin/Coupons/NewCoupons/new-coupons.component.ts index 46bba6a..51c6be3 100644 --- a/src/app/pages/admin/Coupons/NewCoupons/new-coupons.component.ts +++ b/src/app/pages/admin/Coupons/NewCoupons/new-coupons.component.ts @@ -1,7 +1,8 @@ import { Component, Inject, OnDestroy, OnInit } from '@angular/core'; -import { MAT_DIALOG_DATA, MatDialog } from '@angular/material/dialog'; +import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog'; +import { ConfirmBoxEvokeService } from '@costlydeveloper/ngx-awesome-popup'; import { tesoMediaWatcherService } from '@teso/services/media-watcher'; -import { CouponsType } from 'app/models/couponsModel'; +import { CouponsModel, CouponsType } from 'app/models/couponsModel'; import { ProductsModel } from 'app/models/productsModel'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; @@ -13,23 +14,51 @@ import { CouponsService } from '../coupons.service'; templateUrl: './new-coupons.component.html', styleUrls: ['./new-coupons.component.scss'] }) -export class NewCouponsComponent implements OnInit,OnDestroy { +export class NewCouponsComponent implements OnInit, OnDestroy { products: ProductsModel[] = []; - selectedProduct:any={}; - selectedType:any={}; + selectedProduct: ProductsModel = { + productName: '', + businessID: '', + productDesc: '', + productID: '', + categoryID: '', + unitPrice: 0, + productImage: '', + images: [] + }; + selectedType: any = {}; isLoading: boolean = false; - couponTypes:CouponsType[]=[]; - fromWorth:number; - toWorth:number; - isScreenSmall:boolean; + couponTypes: CouponsType[] = []; + fromWorth: number; + toWorth: number; + isScreenSmall: boolean; + newCoupon: CouponsModel = { + couponID: '', + businessID: '', + targetProduct: '', + type: '', + quantity: 0, + lower: 0, + condition: '', + upper: 0, + numberClaimed: 0, + status: '', + expiration: new Date(), + }; + expiryDate: Date = new Date(); + expiryTime: Date = new Date(); private _unsubscribeAll: Subject = new Subject(); - constructor(@Inject(MAT_DIALOG_DATA) public data: { product: any }, - public dialog: MatDialog, private _productService: ProductsService, - private _couponService: CouponsService, private _tesoMediaWatcherService: tesoMediaWatcherService,) { } + + constructor( + public dialog: MatDialog, private _productService: ProductsService, + private _couponService: CouponsService, private _tesoMediaWatcherService: tesoMediaWatcherService, + public dialogRef: MatDialogRef, + private confirmBoxEvokeService: ConfirmBoxEvokeService,) { } ngOnInit(): void { this._couponService.categories$.pipe(takeUntil(this._unsubscribeAll)).subscribe((d) => { this.couponTypes = d; + console.log(d) }); this._productService.data$.pipe(takeUntil(this._unsubscribeAll)).subscribe((d) => { this.products = d; @@ -47,4 +76,15 @@ export class NewCouponsComponent implements OnInit,OnDestroy { this._unsubscribeAll.next(); this._unsubscribeAll.complete(); } + generateCoupon() { + this.newCoupon.targetProduct = this.selectedProduct.productID; + this.newCoupon.expiration = new Date(this.expiryDate.toString() + " " + this.expiryTime.toString()); + this._couponService.generateCoupon(this.newCoupon).subscribe(() => { + this.confirmBoxEvokeService.success("Coupons Generated", `Coupons generated for ${this.selectedProduct.productName} successfully`, "OK").subscribe(response => { + window.location.reload(); + this.dialogRef.close(); + }); + }); + + } } diff --git a/src/app/pages/admin/Coupons/coupons.component.ts b/src/app/pages/admin/Coupons/coupons.component.ts index a24f3c5..0f7966b 100644 --- a/src/app/pages/admin/Coupons/coupons.component.ts +++ b/src/app/pages/admin/Coupons/coupons.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, ViewChild } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; import { MatTableDataSource } from '@angular/material/table'; import { Router } from '@angular/router'; @@ -11,6 +11,7 @@ import { CouponsService } from './coupons.service'; import { NewCouponsComponent } from './NewCoupons/new-coupons.component'; import jwt_decode from 'jwt-decode'; import { AuthService } from 'app/core/auth/auth.service'; +import { MatPaginator } from '@angular/material/paginator'; @Component({ selector: 'app-coupons', @@ -41,7 +42,7 @@ export class CouponsComponent implements OnInit { const tokenInfo = this.getDecodedAccessToken(this._authService.relevantToken); - if (tokenInfo.businessID != "1TESBU00000000") { + if (tokenInfo.businessID == "1TESBU00000000") { const dialogRef = this.dialog.open(AdminNewCouponsComponent, { disableClose: true, hasBackdrop: true, diff --git a/src/app/pages/admin/Coupons/coupons.module.ts b/src/app/pages/admin/Coupons/coupons.module.ts index 57a8796..9094329 100644 --- a/src/app/pages/admin/Coupons/coupons.module.ts +++ b/src/app/pages/admin/Coupons/coupons.module.ts @@ -27,6 +27,8 @@ import { InactiveCouponsComponent } from './Inactive/inactive-coupons.component' import { NewCouponsComponent } from './NewCoupons/new-coupons.component'; import { AdminNewCouponsComponent } from './AdminNewCoupons/admin-new-coupons.component'; import { CouponWorthPipe } from 'app/pipes/coupon-worth.pipe'; +import { CouponProductShort } from 'app/pipes/couponProductShort.pipe'; +import { DetailsDialogComponent } from './DetailsDialog/details-dialog.component'; @NgModule({ @@ -36,7 +38,9 @@ import { CouponWorthPipe } from 'app/pipes/coupon-worth.pipe'; InactiveCouponsComponent, NewCouponsComponent, AdminNewCouponsComponent, - CouponWorthPipe + CouponWorthPipe, + CouponProductShort, + DetailsDialogComponent ], imports: [ RouterModule.forChild(couponsRoutes), diff --git a/src/app/pages/admin/Coupons/coupons.service.ts b/src/app/pages/admin/Coupons/coupons.service.ts index 1cbc770..d8ed387 100644 --- a/src/app/pages/admin/Coupons/coupons.service.ts +++ b/src/app/pages/admin/Coupons/coupons.service.ts @@ -2,7 +2,6 @@ import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { BehaviorSubject, Observable } from 'rxjs'; import { tap } from 'rxjs/operators'; -import { analytics as analyticsData } from 'app/mock-api/dashboards/analytics/data'; import { environment } from 'environments/environment'; import { CouponsModel, CouponsType } from 'app/models/couponsModel'; @@ -56,4 +55,25 @@ export class CouponsService { }) ); } + generateCoupon(coupon: CouponsModel): Observable { + return this._httpClient.post(environment.apiURL + "newcoupon", coupon) + .pipe( + tap((response: CouponsModel) => { + })); + } + + generatePersonalizedCoupon(coupon: CouponsModel): Observable { + return this._httpClient.post(environment.apiURL + "coupon-personalized", coupon) + .pipe( + tap((response: CouponsModel) => { + })); + } + + cancelCoupon(item: CouponsModel): Observable { + return this._httpClient.post(environment.apiURL + `cancelcoupon`, item).pipe( + tap((response: any) => { + console.log(response) + }) + ); + } } diff --git a/src/app/pages/admin/Followers/PersonalizedCoupons/personalized-coupons.component.html b/src/app/pages/admin/Followers/PersonalizedCoupons/personalized-coupons.component.html index 9eb1593..8dc1fc4 100644 --- a/src/app/pages/admin/Followers/PersonalizedCoupons/personalized-coupons.component.html +++ b/src/app/pages/admin/Followers/PersonalizedCoupons/personalized-coupons.component.html @@ -65,7 +65,7 @@
- + DISCOUNT FREEBIE @@ -80,7 +80,7 @@
- +
@@ -91,7 +91,7 @@
- +
@@ -102,12 +102,12 @@ From   - + %   To   - + %   @@ -116,7 +116,7 @@ Coupon Worth - From GH¢200 To GH¢800 + From {{selectedProduct.unitPrice | personalizedcouponWorth:newCoupon.lower}} To {{selectedProduct.unitPrice | personalizedcouponWorth:newCoupon.upper}}
@@ -124,15 +124,15 @@ Date Of Expiration - + Time   - +
- +
diff --git a/src/app/pages/admin/Followers/PersonalizedCoupons/personalized-coupons.component.spec.ts b/src/app/pages/admin/Followers/PersonalizedCoupons/personalized-coupons.component.spec.ts deleted file mode 100644 index c17c6da..0000000 --- a/src/app/pages/admin/Followers/PersonalizedCoupons/personalized-coupons.component.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { PersonalizedCouponsComponent } from './personalized-coupons.component'; - -describe('PersonalizedCouponsComponent', () => { - let component: PersonalizedCouponsComponent; - let fixture: ComponentFixture; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [ PersonalizedCouponsComponent ] - }) - .compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(PersonalizedCouponsComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/src/app/pages/admin/Followers/PersonalizedCoupons/personalized-coupons.component.ts b/src/app/pages/admin/Followers/PersonalizedCoupons/personalized-coupons.component.ts index 0bf9e58..d7ad984 100644 --- a/src/app/pages/admin/Followers/PersonalizedCoupons/personalized-coupons.component.ts +++ b/src/app/pages/admin/Followers/PersonalizedCoupons/personalized-coupons.component.ts @@ -1,15 +1,20 @@ import { Component, Inject, OnInit } from '@angular/core'; -import { MatDialog, MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { AuthService } from 'app/core/auth/auth.service'; import { TesoBusinessDetail } from 'app/models/businessModel'; -import { CouponsType } from 'app/models/couponsModel'; +import { CouponsModel, CouponsType } from 'app/models/couponsModel'; import { ProductsModel } from 'app/models/productsModel'; import { TesoUserDetails } from 'app/models/userModel'; import { environment } from 'environments/environment'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; -import { CouponsService } from '../../Coupons/coupons.service'; +import jwt_decode from 'jwt-decode'; import { BusinessLookUpComponent } from '../../Products/BusinessLookUp/business-look-up.component'; import { ProductsService } from '../../Products/products.service'; +import { ConfirmBoxEvokeService } from '@costlydeveloper/ngx-awesome-popup'; +import { tesoMediaWatcherService } from '@teso/services/media-watcher'; +import { AdminNewCouponsComponent } from '../../Coupons/AdminNewCoupons/admin-new-coupons.component'; +import { CouponsService } from '../../Coupons/coupons.service'; @Component({ selector: 'app-personalized-coupons', @@ -17,26 +22,54 @@ import { ProductsService } from '../../Products/products.service'; styleUrls: ['./personalized-coupons.component.scss'] }) export class PersonalizedCouponsComponent implements OnInit { - tesoGhana:boolean = false; + tesoGhana: boolean = false; businessTargetted: TesoBusinessDetail = {}; - private _unsubscribeAll: Subject = new Subject(); isLoading: boolean = false; products: ProductsModel[] = []; selectedProduct: any = {}; selectedType: string; couponTypes: CouponsType[] = []; - constructor(private _productService: ProductsService, - @Inject(MAT_DIALOG_DATA) public data: { subscriber: TesoUserDetails }, - public dialog: MatDialog,) { } + newCoupon: CouponsModel = { + couponID: '', + businessID: '', + targetProduct: '', + type: '', + quantity: 0, + lower: 0, + condition: '', + upper: 0, + numberClaimed: 0, + status: '', + expiration: new Date(), + }; + expiryDate: Date = new Date(); + expiryTime: Date = new Date(); + private _unsubscribeAll: Subject = new Subject(); + + constructor(private _productService: ProductsService, + @Inject(MAT_DIALOG_DATA) public data: { subscriber: TesoUserDetails }, + private _couponService: CouponsService, private _tesoMediaWatcherService: tesoMediaWatcherService, + public dialogRef: MatDialogRef, + private confirmBoxEvokeService: ConfirmBoxEvokeService, + public dialog: MatDialog, private _authService: AuthService) { + + const tokenInfo = this.getDecodedAccessToken(this._authService.relevantToken); + + if (tokenInfo.businessID == "1TESBU00000000") { + this.tesoGhana = true; + } + } ngOnInit(): void { this._productService.data$.pipe(takeUntil(this._unsubscribeAll)).subscribe((d) => { this.products = d; }); } + imageLoader(path: string): string { return environment.apiURL + `followeruserdp/${path}`; } + lookupBusiness() { const dialogRef = this.dialog.open(BusinessLookUpComponent, { disableClose: true, @@ -47,4 +80,25 @@ export class PersonalizedCouponsComponent implements OnInit { this.businessTargetted = result; }); } + + getDecodedAccessToken(token: string): any { + try { + return jwt_decode(token); + } catch (Error) { + return null; + } + } + + generateCoupon() { + this.newCoupon.targetProduct = this.selectedProduct.productID; + this.newCoupon.target = this.businessTargetted; + this.newCoupon.status = this.data.subscriber.userGuid; + this.newCoupon.expiration = new Date(this.expiryDate.toString() + " " + this.expiryTime.toString()); + this._couponService.generatePersonalizedCoupon(this.newCoupon).subscribe(() => { + this.confirmBoxEvokeService.success("Coupons Generated", `Coupons generated for ${this.selectedProduct.productName} successfully`, "OK").subscribe(response => { + this.dialogRef.close(); + }); + }); + + } } diff --git a/src/app/pages/admin/Followers/followers.component.html b/src/app/pages/admin/Followers/followers.component.html index 24f6f57..3fb0af2 100644 --- a/src/app/pages/admin/Followers/followers.component.html +++ b/src/app/pages/admin/Followers/followers.component.html @@ -6,7 +6,7 @@
Subscribers
-
Here is a list of all your subscribers on Teso (850) +
Here is a list of all your subscribers on Teso ({{data.length}})
diff --git a/src/app/pages/admin/Followers/followers.module.ts b/src/app/pages/admin/Followers/followers.module.ts index b3b3faf..adad6d8 100644 --- a/src/app/pages/admin/Followers/followers.module.ts +++ b/src/app/pages/admin/Followers/followers.module.ts @@ -19,13 +19,15 @@ import { MatInputModule } from '@angular/material/input'; import { PersonalizedCouponsComponent } from './PersonalizedCoupons/personalized-coupons.component'; import { MatDialogModule } from '@angular/material/dialog'; import { MatSelectModule } from '@angular/material/select'; +import { PersonalizedCouponWorthPipe } from 'app/pipes/personalized-coupon-worth.pipe'; @NgModule({ declarations: [ FollowersComponent, - PersonalizedCouponsComponent + PersonalizedCouponsComponent, + PersonalizedCouponWorthPipe ], imports: [ RouterModule.forChild(followersRoutes), diff --git a/src/app/pages/admin/GoldPurchase/gold-transaction.component.html b/src/app/pages/admin/GoldPurchase/gold-transaction.component.html new file mode 100644 index 0000000..31c451f --- /dev/null +++ b/src/app/pages/admin/GoldPurchase/gold-transaction.component.html @@ -0,0 +1 @@ +

gold-transaction works!

diff --git a/src/app/pages/admin/GoldPurchase/gold-transaction.component.scss b/src/app/pages/admin/GoldPurchase/gold-transaction.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/pages/admin/GoldPurchase/gold-transaction.component.spec.ts b/src/app/pages/admin/GoldPurchase/gold-transaction.component.spec.ts new file mode 100644 index 0000000..551b9b4 --- /dev/null +++ b/src/app/pages/admin/GoldPurchase/gold-transaction.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { GoldTransactionComponent } from './gold-transaction.component'; + +describe('GoldTransactionComponent', () => { + let component: GoldTransactionComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ GoldTransactionComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(GoldTransactionComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/pages/admin/GoldPurchase/gold-transaction.component.ts b/src/app/pages/admin/GoldPurchase/gold-transaction.component.ts new file mode 100644 index 0000000..b16f367 --- /dev/null +++ b/src/app/pages/admin/GoldPurchase/gold-transaction.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-gold-transaction', + templateUrl: './gold-transaction.component.html', + styleUrls: ['./gold-transaction.component.scss'] +}) +export class GoldTransactionComponent implements OnInit { + + constructor() { } + + ngOnInit(): void { + } + +} diff --git a/src/app/pages/admin/Monthly_Desires/ListDesires/list-desires.component.html b/src/app/pages/admin/Monthly_Desires/ListDesires/list-desires.component.html index 50a29d7..0ad8ec9 100644 --- a/src/app/pages/admin/Monthly_Desires/ListDesires/list-desires.component.html +++ b/src/app/pages/admin/Monthly_Desires/ListDesires/list-desires.component.html @@ -1,12 +1,13 @@
-
+
Monthly Desires
-
Here is a list of all products in your Teso (850) +
({{data.mostWishedProduct.length}}) wanted + products
@@ -22,7 +23,7 @@
Monthly Desires
-
(850) products +
({{data.mostWishedProduct.length}}) products
@@ -45,14 +46,17 @@
- product image + product image
- -
@@ -63,9 +67,14 @@

{{product.category}}

- {{product.productID}} - {{product.productName}} -

{{product.desires}}

+ {{product.productID}} + {{product.productName}} +

+ + {{product.desires}} +

@@ -74,9 +83,10 @@ - -
There are no monthly desires yet! -
-
+
+
+
There are no monthly desires + yet! +
\ No newline at end of file diff --git a/src/app/pages/admin/Monthly_Desires/ListDesires/list-desires.component.ts b/src/app/pages/admin/Monthly_Desires/ListDesires/list-desires.component.ts index d1c5da6..f0b4a9e 100644 --- a/src/app/pages/admin/Monthly_Desires/ListDesires/list-desires.component.ts +++ b/src/app/pages/admin/Monthly_Desires/ListDesires/list-desires.component.ts @@ -13,6 +13,8 @@ import { AdminCouponDialogComponent } from '../../Products/AdminCouponDialog/adm import { CouponDialogComponent } from '../../Products/CouponDialog/coupon-dialog.component'; import { ProductsService } from '../../Products/products.service'; import { DesiresService } from '../desires.service'; +import jwt_decode from 'jwt-decode'; +import { AuthService } from 'app/core/auth/auth.service'; @Component({ selector: 'app-list-desires', @@ -28,7 +30,7 @@ export class ListDesiresComponent implements OnInit { availableP:any[]=[]; private _unsubscribeAll: Subject = new Subject(); constructor(private router: Router, private _tesoMediaWatcherService: tesoMediaWatcherService, - public dialog: MatDialog, private confirmBoxEvokeService: ConfirmBoxEvokeService, + public dialog: MatDialog, private confirmBoxEvokeService: ConfirmBoxEvokeService, private _authService: AuthService, private _desireService: DesiresService,private _productService: ProductsService) { } ngOnDestroy(): void { @@ -60,7 +62,8 @@ export class ListDesiresComponent implements OnInit { generateCoupon(product: any) { const selectedproduct = this._productService.getProduct(product); - if (!true) { + const tokenInfo = this.getDecodedAccessToken(this._authService.relevantToken); + if (tokenInfo.businessID != "1TESBU00000000") { const dialogRef = this.dialog.open(CouponDialogComponent, { disableClose: true, hasBackdrop: true, @@ -79,4 +82,11 @@ export class ListDesiresComponent implements OnInit { imageLoader(path: string): string { return environment.apiURL + `imagingproducts/${path}`; } + getDecodedAccessToken(token: string): any { + try { + return jwt_decode(token); + } catch (Error) { + return null; + } + } } diff --git a/src/app/pages/admin/Products/AdminCouponDialog/admin-coupon-dialog.component.html b/src/app/pages/admin/Products/AdminCouponDialog/admin-coupon-dialog.component.html index 6ec0998..164a1da 100644 --- a/src/app/pages/admin/Products/AdminCouponDialog/admin-coupon-dialog.component.html +++ b/src/app/pages/admin/Products/AdminCouponDialog/admin-coupon-dialog.component.html @@ -53,8 +53,8 @@
- - + + {{type.typeName}} @@ -70,7 +70,7 @@
- +
@@ -81,7 +81,7 @@
- +
@@ -92,12 +92,12 @@ From   - + %   To   - + %  
@@ -106,7 +106,8 @@ Coupon Worth - From GH¢200 To GH¢800 + From {{data.product.unitPrice | productcouponWorth:newCoupon.lower}} To {{data.product.unitPrice | + productcouponWorth:newCoupon.upper}}
@@ -114,15 +115,15 @@ Date Of Expiration - + Time   - +
- +
@@ -172,8 +173,8 @@ Coupon Type - - + + {{type.typeName}} @@ -189,7 +190,7 @@
- +
@@ -199,7 +200,7 @@ Number Of Coupons - +
@@ -210,14 +211,14 @@
From   - + %  
To   - + %  
@@ -226,7 +227,8 @@ Coupon Worth - From GH¢200 To GH¢800 + From {{data.product.unitPrice | productcouponWorth:newCoupon.lower}} To {{data.product.unitPrice | + productcouponWorth:newCoupon.upper}}
@@ -234,17 +236,17 @@ Date Of Expiration - +
Time   - +
- +
diff --git a/src/app/pages/admin/Products/AdminCouponDialog/admin-coupon-dialog.component.ts b/src/app/pages/admin/Products/AdminCouponDialog/admin-coupon-dialog.component.ts index 026cd88..fbbd6a4 100644 --- a/src/app/pages/admin/Products/AdminCouponDialog/admin-coupon-dialog.component.ts +++ b/src/app/pages/admin/Products/AdminCouponDialog/admin-coupon-dialog.component.ts @@ -1,12 +1,14 @@ +import { Time } from '@angular/common'; import { Component, Inject, OnDestroy, OnInit } from '@angular/core'; -import { MatDialog, MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; import { ConfirmBoxEvokeService } from '@costlydeveloper/ngx-awesome-popup'; import { tesoMediaWatcherService } from '@teso/services/media-watcher'; import { TesoBusinessDetail } from 'app/models/businessModel'; -import { CouponsType } from 'app/models/couponsModel'; +import { CouponsModel, CouponsType } from 'app/models/couponsModel'; import { ProductsModel } from 'app/models/productsModel'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; +import { CouponsModule } from '../../Coupons/coupons.module'; import { CouponsService } from '../../Coupons/coupons.service'; import { BusinessLookUpComponent } from '../BusinessLookUp/business-look-up.component'; import { ProductsService } from '../products.service'; @@ -16,17 +18,32 @@ import { ProductsService } from '../products.service'; templateUrl: './admin-coupon-dialog.component.html', styleUrls: ['./admin-coupon-dialog.component.scss'] }) -export class AdminCouponDialogComponent implements OnInit,OnDestroy { - businessTargetted:TesoBusinessDetail={}; +export class AdminCouponDialogComponent implements OnInit, OnDestroy { + businessTargetted: TesoBusinessDetail = {}; private _unsubscribeAll: Subject = new Subject(); - isLoading:boolean=false; - selectedProduct:any={}; - selectedType:any={}; - couponTypes:CouponsType[]=[]; + isLoading: boolean = false; + selectedProduct: any = {}; + selectedType: any = {}; + couponTypes: CouponsType[] = []; isScreenSmall: boolean; + newCoupon: CouponsModel = { + couponID: '', + businessID: '', + targetProduct: '', + type: '', + quantity: 0, + lower: 0, + condition: '', + upper: 0, + numberClaimed: 0, + status: '', + expiration: new Date(), + }; + expiryDate: Date = new Date(); + expiryTime: Date = new Date(); constructor( - private _couponService: CouponsService,@Inject(MAT_DIALOG_DATA) public data: { product: ProductsModel }, - public dialog: MatDialog,private _tesoMediaWatcherService: tesoMediaWatcherService, + private _couponService: CouponsService, @Inject(MAT_DIALOG_DATA) public data: { product: ProductsModel }, + public dialog: MatDialog, private _tesoMediaWatcherService: tesoMediaWatcherService, public dialogRef: MatDialogRef, private confirmBoxEvokeService: ConfirmBoxEvokeService,) { } ngOnInit(): void { @@ -34,12 +51,12 @@ export class AdminCouponDialogComponent implements OnInit,OnDestroy { this.couponTypes = d; }); this._tesoMediaWatcherService.onMediaChange$ - .pipe(takeUntil(this._unsubscribeAll)) - .subscribe(({ matchingAliases }) => { + .pipe(takeUntil(this._unsubscribeAll)) + .subscribe(({ matchingAliases }) => { - // Check if the screen is small - this.isScreenSmall = !matchingAliases.includes('md'); - }); + // Check if the screen is small + this.isScreenSmall = !matchingAliases.includes('md'); + }); } lookupBusiness() { const dialogRef = this.dialog.open(BusinessLookUpComponent, { @@ -56,4 +73,20 @@ export class AdminCouponDialogComponent implements OnInit,OnDestroy { this._unsubscribeAll.next(); this._unsubscribeAll.complete(); } + + generateCoupon() { + if (this.businessTargetted.businessId != null) { + this.newCoupon.businessID = this.businessTargetted.businessId; + this.newCoupon.targetProduct = this.data.product.productID; + this.newCoupon.target = this.businessTargetted; + this.newCoupon.expiration = new Date(this.expiryDate.toString() + " " + this.expiryTime.toString()); + this._couponService.generateCoupon(this.newCoupon).subscribe(() => { + this.confirmBoxEvokeService.success("Coupons Generated", `Coupons generated for ${this.data.product.productName} successfully`, "OK").subscribe(response => { + this.dialogRef.close(); + }); + }); + } else { + this.confirmBoxEvokeService.warning("Select a business", "To add a coupon please select a business to continue", "OK").subscribe(); + } + } } diff --git a/src/app/pages/admin/Products/BusinessLookUp/business-look-up.component.html b/src/app/pages/admin/Products/BusinessLookUp/business-look-up.component.html index d783e01..869374d 100644 --- a/src/app/pages/admin/Products/BusinessLookUp/business-look-up.component.html +++ b/src/app/pages/admin/Products/BusinessLookUp/business-look-up.component.html @@ -15,7 +15,7 @@ = new Subject(); - constructor(private _businessService: ProfileService, public dialogRef: MatDialogRef, + constructor(private _businessService: ProfileService, public dialogRef: MatDialogRef,public dialog: MatDialog ) { } ngOnInit(): void { @@ -35,4 +36,16 @@ export class BusinessLookUpComponent implements OnInit { selectBusiness(business: TesoBusinessDetail) { this.dialogRef.close(business); } + + temporaryShop() { + const dialogRef = this.dialog.open(TempShopComponent, { + disableClose: true, + hasBackdrop: true, + }); + + dialogRef.afterClosed().subscribe((result: TesoBusinessDetail) => { + if(result != null) + this.dialogRef.close(result); + }); + } } diff --git a/src/app/pages/admin/Products/CouponDialog/coupon-dialog.component.html b/src/app/pages/admin/Products/CouponDialog/coupon-dialog.component.html index 56cebe3..5f2df68 100644 --- a/src/app/pages/admin/Products/CouponDialog/coupon-dialog.component.html +++ b/src/app/pages/admin/Products/CouponDialog/coupon-dialog.component.html @@ -39,8 +39,8 @@
- - + + {{type.typeName}} @@ -56,7 +56,7 @@
- +
@@ -67,7 +67,7 @@
- +
@@ -78,12 +78,12 @@ From   - + %   To   - + %   @@ -92,7 +92,8 @@ Coupon Worth - From GH¢200 To GH¢800 + From {{data.product.unitPrice | productcouponWorth:newCoupon.lower}} To {{data.product.unitPrice | + productcouponWorth:newCoupon.upper}}
@@ -100,15 +101,15 @@ Date Of Expiration - + Time   - +
- +
@@ -144,8 +145,8 @@ Coupon Type - - + + {{type.typeName}} @@ -161,7 +162,7 @@
- +
@@ -171,7 +172,7 @@ Number Of Coupons - +
@@ -182,14 +183,14 @@
From   - + %  
To   - + %  
@@ -198,7 +199,8 @@ Coupon Worth - From GH¢200 To GH¢800 + From {{data.product.unitPrice | productcouponWorth:newCoupon.lower}} To {{data.product.unitPrice | + productcouponWorth:newCoupon.upper}}
@@ -206,17 +208,17 @@ Date Of Expiration - +
Time   - +
- +
diff --git a/src/app/pages/admin/Products/CouponDialog/coupon-dialog.component.ts b/src/app/pages/admin/Products/CouponDialog/coupon-dialog.component.ts index a24db22..63df018 100644 --- a/src/app/pages/admin/Products/CouponDialog/coupon-dialog.component.ts +++ b/src/app/pages/admin/Products/CouponDialog/coupon-dialog.component.ts @@ -1,12 +1,13 @@ import { Component, Inject, OnDestroy, OnInit } from '@angular/core'; -import { MatDialog, MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; import { ConfirmBoxEvokeService } from '@costlydeveloper/ngx-awesome-popup'; import { tesoMediaWatcherService } from '@teso/services/media-watcher'; -import { CouponsType } from 'app/models/couponsModel'; +import { CouponsModel, CouponsType } from 'app/models/couponsModel'; import { ProductsModel } from 'app/models/productsModel'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; import { CouponsService } from '../../Coupons/coupons.service'; +import { AdminCouponDialogComponent } from '../AdminCouponDialog/admin-coupon-dialog.component'; import { ProductsService } from '../products.service'; @Component({ @@ -14,16 +15,31 @@ import { ProductsService } from '../products.service'; templateUrl: './coupon-dialog.component.html', styleUrls: ['./coupon-dialog.component.scss'] }) -export class CouponDialogComponent implements OnInit,OnDestroy { +export class CouponDialogComponent implements OnInit, OnDestroy { private _unsubscribeAll: Subject = new Subject(); - isLoading:boolean=false; - selectedProduct:any={}; - selectedType:any={}; - couponTypes:CouponsType[]=[]; + isLoading: boolean = false; + selectedProduct: any = {}; + selectedType: any = {}; + couponTypes: CouponsType[] = []; isScreenSmall: boolean; + newCoupon: CouponsModel = { + couponID: '', + businessID: '', + targetProduct: '', + type: '', + quantity: 0, + lower: 0, + condition: '', + upper: 0, + numberClaimed: 0, + status: '', + expiration: new Date(), + }; + expiryDate: Date = new Date(); + expiryTime: Date = new Date(); constructor( - private _couponService: CouponsService,@Inject(MAT_DIALOG_DATA) public data: { product: ProductsModel }, - public dialog: MatDialog,private _productService: ProductsService,private _tesoMediaWatcherService: tesoMediaWatcherService, + private _couponService: CouponsService, @Inject(MAT_DIALOG_DATA) public data: { product: ProductsModel }, + public dialog: MatDialog, private _tesoMediaWatcherService: tesoMediaWatcherService, public dialogRef: MatDialogRef, private confirmBoxEvokeService: ConfirmBoxEvokeService,) { } ngOnInit(): void { @@ -31,12 +47,12 @@ export class CouponDialogComponent implements OnInit,OnDestroy { this.couponTypes = d; }); this._tesoMediaWatcherService.onMediaChange$ - .pipe(takeUntil(this._unsubscribeAll)) - .subscribe(({ matchingAliases }) => { + .pipe(takeUntil(this._unsubscribeAll)) + .subscribe(({ matchingAliases }) => { - // Check if the screen is small - this.isScreenSmall = !matchingAliases.includes('md'); - }); + // Check if the screen is small + this.isScreenSmall = !matchingAliases.includes('md'); + }); } ngOnDestroy(): void { @@ -44,4 +60,14 @@ export class CouponDialogComponent implements OnInit,OnDestroy { this._unsubscribeAll.next(); this._unsubscribeAll.complete(); } + generateCoupon() { + this.newCoupon.targetProduct = this.data.product.productID; + this.newCoupon.expiration = new Date(this.expiryDate.toString() + " " + this.expiryTime.toString()); + this._couponService.generateCoupon(this.newCoupon).subscribe(() => { + this.confirmBoxEvokeService.success("Coupons Generated", `Coupons generated for ${this.data.product.productName} successfully`, "OK").subscribe(response => { + this.dialogRef.close(); + }); + }); + + } } diff --git a/src/app/pages/admin/Products/Details/details-product.component.html b/src/app/pages/admin/Products/Details/details-product.component.html index 67e9bfc..0130370 100644 --- a/src/app/pages/admin/Products/Details/details-product.component.html +++ b/src/app/pages/admin/Products/Details/details-product.component.html @@ -11,7 +11,7 @@

- Product # {{product.productID}} + Product : {{product.productName}}

Add up to 10 high quality images of the product
-
- {{productImages.length}} out of +
+ {{product.images.length}} out of 10
@@ -84,12 +84,12 @@
- -
@@ -175,19 +175,19 @@
Add up to 10 high quality images of the product
-
0 out of +
{{product.images.length}} out of 10
- -
diff --git a/src/app/pages/admin/Products/Details/details-product.component.ts b/src/app/pages/admin/Products/Details/details-product.component.ts index 2ac6ba2..2ac5309 100644 --- a/src/app/pages/admin/Products/Details/details-product.component.ts +++ b/src/app/pages/admin/Products/Details/details-product.component.ts @@ -5,6 +5,7 @@ import { ConfirmBoxEvokeService } from '@costlydeveloper/ngx-awesome-popup'; import { tesoMediaWatcherService } from '@teso/services/media-watcher'; import { ProductUpload } from 'app/models/generalModel'; import { ProductsModel } from 'app/models/productsModel'; +import { environment } from 'environments/environment'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; import { ProductsService } from '../products.service'; @@ -54,7 +55,7 @@ export class DetailsProductComponent implements OnInit { private router: Router, private route: ActivatedRoute, private productServie: ProductsService) { var snapshot = route.snapshot; this.productID = snapshot.paramMap.get('id'); - this.product = productServie.getProduct(this.productID); + this.product = productServie.getProduct(this.productID); if(this.product == null){ this.router.navigate(['products']); } @@ -70,16 +71,7 @@ export class DetailsProductComponent implements OnInit { }); } quillConfig = modules; - items = [ - { id: 1, name: 'Python' }, - { id: 2, name: 'Node Js' }, - { id: 3, name: 'Java' }, - { id: 4, name: 'PHP', disabled: true }, - { id: 5, name: 'Django' }, - { id: 6, name: 'Angular' }, - { id: 7, name: 'Vue' }, - { id: 8, name: 'ReactJs' }, - ]; + ngOnDestroy(): void { // Unsubscribe from all subscriptions this._unsubscribeAll.next(); @@ -146,6 +138,8 @@ export class DetailsProductComponent implements OnInit { } } - + imageLoader(path: string): string { + return environment.apiURL + `imagingproducts/${path}`; + } } diff --git a/src/app/pages/admin/Products/EditProduct/edit-product.component.html b/src/app/pages/admin/Products/EditProduct/edit-product.component.html index 2ccf471..f72dc6c 100644 --- a/src/app/pages/admin/Products/EditProduct/edit-product.component.html +++ b/src/app/pages/admin/Products/EditProduct/edit-product.component.html @@ -11,7 +11,7 @@

- Product # {{product.productID}} + Product : {{product.productName}}

Product Name - +
- Product Category + Product Category - - Lowdsadsadsadsadsadsa - Standardasdsadasdas - Highsadsadsadas + + + {{category.catName}} +
@@ -49,7 +50,7 @@ GH¢   - + @@ -61,7 +62,7 @@
- @@ -78,18 +79,18 @@ quality images of the product
- {{productImages.length}} out of + {{product.images.length}} out of 10
-
+
+ (click)="submit()">Save Changes
@@ -152,10 +152,11 @@ Product Category - - Lowdsadsadsadsadsadsa - Standardasdsadasdas - Highsadsadsadas + + + {{category.catName}} +
@@ -218,8 +219,7 @@
- +
diff --git a/src/app/pages/admin/Products/EditProduct/edit-product.component.ts b/src/app/pages/admin/Products/EditProduct/edit-product.component.ts index cbea794..43a62f6 100644 --- a/src/app/pages/admin/Products/EditProduct/edit-product.component.ts +++ b/src/app/pages/admin/Products/EditProduct/edit-product.component.ts @@ -1,9 +1,9 @@ -import { Component, OnInit } from '@angular/core'; +import { ChangeDetectorRef, Component, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { ConfirmBoxEvokeService } from '@costlydeveloper/ngx-awesome-popup'; import { tesoMediaWatcherService } from '@teso/services/media-watcher'; import { ProductUpload } from 'app/models/generalModel'; -import { ProductsModel } from 'app/models/productsModel'; +import { ProductCategory, ProductImages, ProductsModel } from 'app/models/productsModel'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; import { ProductsService } from '../products.service'; @@ -26,7 +26,7 @@ const modules = { [{ 'align': [] }], ['clean'], -['link'] + ['link'] ] }; @@ -47,16 +47,28 @@ export class EditProductComponent implements OnInit { isScreenSmall: boolean; productImages: ProductUpload[] = []; product: ProductsModel; + categories: ProductCategory[] = []; private _unsubscribeAll: Subject = new Subject(); constructor(private _tesoMediaWatcherService: tesoMediaWatcherService, - private confirmBoxEvokeService: ConfirmBoxEvokeService, - private router: Router, private route: ActivatedRoute, private productServie: ProductsService) { + private confirmBoxEvokeService: ConfirmBoxEvokeService, private changeDetector: ChangeDetectorRef, + private router: Router, private route: ActivatedRoute, private _productService: ProductsService) { var snapshot = route.snapshot; this.productID = snapshot.paramMap.get('id'); - this.product = productServie.getProduct(this.productID); - if(this.product == null){ + this.product = _productService.getProduct(this.productID); + if (this.product == null) { this.router.navigate(['products']); } + + this._productService.categories$.pipe(takeUntil(this._unsubscribeAll)).subscribe((d) => { + this.categories = d; + this.changeDetector.markForCheck(); + // this.isLoading = false; + if (this.product != null) { + this.product.categoryID = d.find((e) => e.catName = this.product.categoryID).catCode; + } + }); + + } ngOnInit(): void { @@ -85,16 +97,6 @@ export class EditProductComponent implements OnInit { this._unsubscribeAll.complete(); } - // readURL(event: Event): void { - // if (event.target.files && event.target.files[0]) { - // const file = event.target.files[0]; - - // const reader = new FileReader(); - // reader.onload = e => this.imageSrc = reader.result; - - // reader.readAsDataURL(file); - // } - // } onFileSelected(event) { const file: File = event.target.files[0]; @@ -105,6 +107,7 @@ export class EditProductComponent implements OnInit { file: file, highlight: true, imageSRC: "", + id: `${this.productImages.length + 1}`, }; let reader = new FileReader(); reader.onload = (event: any) => { @@ -112,6 +115,13 @@ export class EditProductComponent implements OnInit { } reader.readAsDataURL(file); + var prodImage: ProductImages = { + id: productImage.id, + productID: '%local%', + path: productImage.imageSRC + }; + + this.product.images.push(prodImage); this.productImages.push(productImage); } else { @@ -119,6 +129,7 @@ export class EditProductComponent implements OnInit { file: file, highlight: false, imageSRC: "", + id: `${this.productImages.length + 1}`, }; let reader = new FileReader(); @@ -127,6 +138,12 @@ export class EditProductComponent implements OnInit { } reader.readAsDataURL(file); + var prodImage: ProductImages = { + id: productImage.id, + productID: '%local%', + path: productImage.imageSRC + }; + this.product.images.push(prodImage); this.productImages.push(productImage); } } else { @@ -134,12 +151,15 @@ export class EditProductComponent implements OnInit { } } - removeImage(item: ProductUpload) { - this.productImages = this.productImages.filter((e) => e != item); + removeImage(item: ProductImages) { + this.product.images = this.product.images.filter((e) => e != item); + if (item.productID == '%local%') { + this.productImages = this.productImages.filter((e) => e.id != item.id); + } } submit() { - if (this.productImages.length == 0) { + if (this.product.images.length == 0) { this.confirmBoxEvokeService.warning("Add Product Images", "To add a new product, you must add at least one image to support it", "OK").subscribe(); } else { diff --git a/src/app/pages/admin/Products/NewProduct/new-product.component.html b/src/app/pages/admin/Products/NewProduct/new-product.component.html index bcf918b..eb29b13 100644 --- a/src/app/pages/admin/Products/NewProduct/new-product.component.html +++ b/src/app/pages/admin/Products/NewProduct/new-product.component.html @@ -28,7 +28,7 @@ Product Name - +
@@ -36,7 +36,7 @@ Product Category - + {{category.catName}} @@ -50,7 +50,7 @@ GH¢   - +
@@ -146,7 +146,7 @@ Product Name - +
@@ -154,7 +154,7 @@ Product Category - + {{category.catName}} @@ -168,7 +168,7 @@ Price GH¢   - +
@@ -180,7 +180,7 @@
- @@ -211,7 +211,7 @@ - +
diff --git a/src/app/pages/admin/Products/NewProduct/new-product.component.ts b/src/app/pages/admin/Products/NewProduct/new-product.component.ts index cb99b4f..51aedc0 100644 --- a/src/app/pages/admin/Products/NewProduct/new-product.component.ts +++ b/src/app/pages/admin/Products/NewProduct/new-product.component.ts @@ -6,6 +6,13 @@ import { takeUntil } from 'rxjs/operators'; import { ConfirmBoxEvokeService } from '@costlydeveloper/ngx-awesome-popup'; import { ProductsService } from '../products.service'; import { ProductCategory, ProductsModel } from 'app/models/productsModel'; +import { MatDialog } from '@angular/material/dialog'; +import { AuthService } from 'app/core/auth/auth.service'; +import jwt_decode from 'jwt-decode'; +import { AdminCouponDialogComponent } from '../AdminCouponDialog/admin-coupon-dialog.component'; +import { CouponDialogComponent } from '../CouponDialog/coupon-dialog.component'; +import { ConfirmBoxResponse } from '@costlydeveloper/ngx-awesome-popup/ngx-awesome-popup/types/confirm-box/core/classes'; +import { Router } from '@angular/router'; const modules = { toolbar: [ ['bold', 'italic', 'underline', 'strike'], @@ -25,7 +32,7 @@ const modules = { [{ 'align': [] }], ['clean'], -['link'] + ['link'] ] }; @@ -44,7 +51,7 @@ export class NewProductComponent implements OnInit, OnDestroy { isScreenSmall: boolean; productImages: ProductUpload[] = []; categories: ProductCategory[] = []; - newProduct: ProductsModel={ + newProduct: ProductsModel = { productName: '', businessID: '', productDesc: '', @@ -55,102 +62,151 @@ export class NewProductComponent implements OnInit, OnDestroy { images: [] }; - private _unsubscribeAll: Subject < any > = new Subject(); -constructor(private _tesoMediaWatcherService: tesoMediaWatcherService, - private confirmBoxEvokeService: ConfirmBoxEvokeService, - private changeDetector:ChangeDetectorRef, - private _productService: ProductsService) { } - -ngOnInit(): void { - this._tesoMediaWatcherService.onMediaChange$ - .pipe(takeUntil(this._unsubscribeAll)) - .subscribe(({ matchingAliases }) => { - - // Check if the screen is small - this.isScreenSmall = !matchingAliases.includes('md'); + private _unsubscribeAll: Subject = new Subject(); + constructor(private _tesoMediaWatcherService: tesoMediaWatcherService, + private changeDetector: ChangeDetectorRef, + private _productService: ProductsService, private router: Router, + public dialog: MatDialog, private confirmBoxEvokeService: ConfirmBoxEvokeService, + private _authService: AuthService) { } + + ngOnInit(): void { + this._tesoMediaWatcherService.onMediaChange$ + .pipe(takeUntil(this._unsubscribeAll)) + .subscribe(({ matchingAliases }) => { + + // Check if the screen is small + this.isScreenSmall = !matchingAliases.includes('md'); + }); + + this._productService.categories$.pipe(takeUntil(this._unsubscribeAll)).subscribe((d) => { + this.categories = d; + this.changeDetector.markForCheck(); + // this.isLoading = false; }); + } + quillConfig = modules; + ngOnDestroy(): void { + // Unsubscribe from all subscriptions + this._unsubscribeAll.next(); + this._unsubscribeAll.complete(); + } - this._productService.categories$.pipe(takeUntil(this._unsubscribeAll)).subscribe((d) => { - this.categories = d; - this.changeDetector.markForCheck(); - // this.isLoading = false; - }); -} -quillConfig = modules; -items = [ - { id: 1, name: 'Python' }, - { id: 2, name: 'Node Js' }, - { id: 3, name: 'Java' }, - { id: 4, name: 'PHP', disabled: true }, - { id: 5, name: 'Django' }, - { id: 6, name: 'Angular' }, - { id: 7, name: 'Vue' }, - { id: 8, name: 'ReactJs' }, -]; -ngOnDestroy(): void { - // Unsubscribe from all subscriptions - this._unsubscribeAll.next(); - this._unsubscribeAll.complete(); -} - -// readURL(event: Event): void { -// if (event.target.files && event.target.files[0]) { -// const file = event.target.files[0]; -// const reader = new FileReader(); -// reader.onload = e => this.imageSrc = reader.result; + onFileSelected(event) { + + const file: File = event.target.files[0]; + if (file.type.includes("image")) { + console.log(file) + if (this.productImages.length == 0) { + var productImage: ProductUpload = { + file: file, + highlight: true, + imageSRC: "", + }; + let reader = new FileReader(); + reader.onload = (event: any) => { + productImage.imageSRC = event.target.result; + } + reader.readAsDataURL(file); + + this.productImages.push(productImage); + + } else { + var productImage: ProductUpload = { + file: file, + highlight: false, + imageSRC: "", + }; + + let reader = new FileReader(); + reader.onload = (event: any) => { + productImage.imageSRC = event.target.result; + } + reader.readAsDataURL(file); + + this.productImages.push(productImage); + } + } else { + this.confirmBoxEvokeService.danger("Incompatible File", "Only images can be added", "OK").subscribe(); + } + } -// reader.readAsDataURL(file); -// } -// } -onFileSelected(event) { + removeImage(item: ProductUpload) { + this.productImages = this.productImages.filter((e) => e != item); + } - const file: File = event.target.files[0]; - if (file.type.includes("image")) { - console.log(file) + submit() { if (this.productImages.length == 0) { - var productImage: ProductUpload = { - file: file, - highlight: true, - imageSRC: "", - }; - let reader = new FileReader(); - reader.onload = (event: any) => { - productImage.imageSRC = event.target.result; - } - reader.readAsDataURL(file); + this.confirmBoxEvokeService.warning("Add Product Images", "To add a new product, you must add at least one image to support it", "OK").subscribe(); + } + else if (this.newProduct.categoryID == '' || this.newProduct.productDesc == '' || this.newProduct.productName == '' || this.newProduct.unitPrice == 0) { + this.confirmBoxEvokeService.warning("Missing Fields", "To add a new product, you must fill up all fields", "OK").subscribe(); + } + else { + this.confirmBoxEvokeService.success("Confirm Action", `Are you sure would like to add ${this.newProduct.productName} to your inventory on TESO ?`, "YES", "NO").subscribe(response => { + if (response.clickedButtonID == "yes") { + this._productService.createProduct(this.newProduct, this.productImages).subscribe((response: ProductsModel) => { + if (response.productID != null) { + this.newProduct = response; + this.confirmBoxEvokeService.success("Product Added", "Product added successfully, would like to create a coupon to go with it ?", "YES", "NO").subscribe(response => { + if (response.clickedButtonID == "yes") { + this.generateCoupons(); + } + this.newProduct = { + productName: '', + businessID: '', + productDesc: '', + productID: '', + categoryID: '', + unitPrice: 0, + productImage: '', + images: [] + }; + }); + } + }); + } + }); + } + + } + generateCoupons() { + const tokenInfo = this.getDecodedAccessToken(this._authService.relevantToken); - this.productImages.push(productImage); + if (tokenInfo.businessID == "1TESBU00000000") { + const dialogRef = this.dialog.open(AdminCouponDialogComponent, { + disableClose: true, + hasBackdrop: true, + data: { product: this.newProduct }, + + }); } else { - var productImage: ProductUpload = { - file: file, - highlight: false, - imageSRC: "", - }; - - let reader = new FileReader(); - reader.onload = (event: any) => { - productImage.imageSRC = event.target.result; - } - reader.readAsDataURL(file); + const dialogRef = this.dialog.open(CouponDialogComponent, { + disableClose: true, + hasBackdrop: true, + data: { product: this.newProduct }, + }); - this.productImages.push(productImage); } - } else { - this.confirmBoxEvokeService.danger("Incompatible File", "Only images can be added", "OK").subscribe(); - } -} - -removeImage(item: ProductUpload) { - this.productImages = this.productImages.filter((e) => e != item); -} -submit() { - if (this.productImages.length == 0) { - this.confirmBoxEvokeService.warning("Add Product Images", "To add a new product, you must add at least one image to support it", "OK").subscribe(); - } else { + this.newProduct = { + productName: '', + businessID: '', + productDesc: '', + productID: '', + categoryID: '', + unitPrice: 0, + productImage: '', + images: [] + }; + } + getDecodedAccessToken(token: string): any { + try { + return jwt_decode(token); + } catch (Error) { + return null; + } } } -} diff --git a/src/app/pages/admin/Products/ProductList/product-list.component.html b/src/app/pages/admin/Products/ProductList/product-list.component.html index 94f827e..8e16ab2 100644 --- a/src/app/pages/admin/Products/ProductList/product-list.component.html +++ b/src/app/pages/admin/Products/ProductList/product-list.component.html @@ -81,7 +81,7 @@
- {{product.productName}} + {{product.productName | productNameShort}}

{{product.unitPrice | currency:"GHS "}}

{{product.productDesc | productDescShort}}

@@ -95,9 +95,9 @@
- + [pageSizeOptions]="[5, 10, 25, 100]" [pageSize]="10" [length]="data.length" (page)="pageEvent = $event" [showFirstLastButtons]="true"> -->
There are no products!
diff --git a/src/app/pages/admin/Products/ProductList/product-list.component.ts b/src/app/pages/admin/Products/ProductList/product-list.component.ts index 284b334..e4ebe91 100644 --- a/src/app/pages/admin/Products/ProductList/product-list.component.ts +++ b/src/app/pages/admin/Products/ProductList/product-list.component.ts @@ -1,7 +1,7 @@ import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { FormControl } from '@angular/forms'; import { MatDialog } from '@angular/material/dialog'; -import { MatPaginator } from '@angular/material/paginator'; +import { MatPaginator, PageEvent } from '@angular/material/paginator'; import { MatSort } from '@angular/material/sort'; import { Router } from '@angular/router'; import { ConfirmBoxEvokeService } from '@costlydeveloper/ngx-awesome-popup'; @@ -13,6 +13,8 @@ import { debounceTime, map, switchMap, takeUntil } from 'rxjs/operators'; import { AdminCouponDialogComponent } from '../AdminCouponDialog/admin-coupon-dialog.component'; import { CouponDialogComponent } from '../CouponDialog/coupon-dialog.component'; import { ProductsService } from '../products.service'; +import jwt_decode from 'jwt-decode'; +import { AuthService } from 'app/core/auth/auth.service'; @Component({ selector: 'app-product-list', @@ -27,7 +29,10 @@ export class ProductListComponent implements OnInit, OnDestroy { isLoading: boolean = true; searchInputControl: FormControl = new FormControl(); private _unsubscribeAll: Subject = new Subject(); + // MatPaginator Output + // pageEvent: PageEvent; constructor(private router: Router, private _tesoMediaWatcherService: tesoMediaWatcherService, + private _authService: AuthService, public dialog: MatDialog, private confirmBoxEvokeService: ConfirmBoxEvokeService, private _productService: ProductsService) { } @@ -74,7 +79,8 @@ export class ProductListComponent implements OnInit, OnDestroy { this.router.navigate(['products/' + productID]); } generateCoupon(product: any) { - if (!true) { + const tokenInfo = this.getDecodedAccessToken(this._authService.relevantToken); + if (tokenInfo.businessID != "1TESBU00000000") { const dialogRef = this.dialog.open(CouponDialogComponent, { disableClose: true, hasBackdrop: true, @@ -90,6 +96,14 @@ export class ProductListComponent implements OnInit, OnDestroy { }); } } + + getDecodedAccessToken(token: string): any { + try { + return jwt_decode(token); + } catch (Error) { + return null; + } + } deleteProduct(product: any) { this.confirmBoxEvokeService.danger("Delete Product", "Are you sure you would like to delete this product from your inventory ?", @@ -107,4 +121,5 @@ export class ProductListComponent implements OnInit, OnDestroy { details(productID: any) { this.router.navigate(['products/details/' + productID]); } + // this.pageEvent. } \ No newline at end of file diff --git a/src/app/pages/admin/Products/products.module.ts b/src/app/pages/admin/Products/products.module.ts index c4f170a..3419bbb 100644 --- a/src/app/pages/admin/Products/products.module.ts +++ b/src/app/pages/admin/Products/products.module.ts @@ -32,6 +32,9 @@ import { AdminCouponDialogComponent } from './AdminCouponDialog/admin-coupon-dia import { BusinessLookUpComponent } from './BusinessLookUp/business-look-up.component'; import { ProductDescriptionShort } from 'app/pipes/productDescriptionShort.pipe'; import { DetailsProductComponent } from './Details/details-product.component'; +import { CouponWorthPipe } from 'app/pipes/coupon-worth.pipe'; +import { ProductCouponWorthPipe } from 'app/pipes/product-coupon-worth.pipe'; +import { ProductNameShort } from 'app/pipes/productNameShort.pipe'; @NgModule({ declarations: [ @@ -44,7 +47,9 @@ import { DetailsProductComponent } from './Details/details-product.component'; AdminCouponDialogComponent, BusinessLookUpComponent, ProductDescriptionShort, - DetailsProductComponent + DetailsProductComponent, + ProductCouponWorthPipe, + ProductNameShort ], imports: [ RouterModule.forChild(productsRoutes), diff --git a/src/app/pages/admin/Products/products.service.ts b/src/app/pages/admin/Products/products.service.ts index f96a776..a7fdb1a 100644 --- a/src/app/pages/admin/Products/products.service.ts +++ b/src/app/pages/admin/Products/products.service.ts @@ -1,11 +1,12 @@ import { Injectable } from '@angular/core'; -import { HttpClient } from '@angular/common/http'; +import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; import { BehaviorSubject, Observable } from 'rxjs'; import { tap } from 'rxjs/operators'; import { analytics as analyticsData } from 'app/mock-api/dashboards/analytics/data'; import { environment } from 'environments/environment'; import { ProductsModule } from './products.module'; -import { ProductCategory, ProductsModel } from 'app/models/productsModel'; +import { ProductCategory, ProductImages, ProductsModel } from 'app/models/productsModel'; +import { ProductUpload } from 'app/models/generalModel'; @Injectable({ providedIn: 'root' @@ -55,12 +56,17 @@ export class ProductsService { }) ); } + filterProducts(productName: string): ProductsModel[] { return this._dataFilterable.getValue().filter(p => p.productName.toLowerCase().includes(productName)); - // this.products = response; + // this.products = response; } + getProduct(id: string): ProductsModel { - const found = this.products.find(item => item.productID === id); + var found = this.products.find(item => item.productID === id); + this._httpClient.get(environment.apiURL + `allimages/${id}`).subscribe((results: ProductImages[]) => { + found.images = results; + }); return found; } @@ -70,4 +76,22 @@ export class ProductsService { this._productsCategory.next(response); })); } + + createProduct(product: ProductsModel, productImage: ProductUpload[]): Observable { + const formData = new FormData(); + const images = productImage.map((e) => e.file); + formData.set("productName", product.productName); + formData.set("productDesc", product.productDesc); + formData.set("categoryId", product.categoryID); + formData.set("unitPrice", product.unitPrice.toString()); + + for (var i = 0; i < images.length; i++) { + formData.append("file[]", images[i]); + } + return this._httpClient.post(environment.apiURL + "products/newproduct", formData) + .pipe( + tap((response: ProductsModel) => { + this.getData(); + })); + } } diff --git a/src/app/pages/admin/Profile/Information/information.component.html b/src/app/pages/admin/Profile/Information/information.component.html index 40d620a..cf2908d 100644 --- a/src/app/pages/admin/Profile/Information/information.component.html +++ b/src/app/pages/admin/Profile/Information/information.component.html @@ -1,5 +1,5 @@
-
+

Business diff --git a/src/app/pages/admin/TempShop/temp-shop.component.html b/src/app/pages/admin/TempShop/temp-shop.component.html new file mode 100644 index 0000000..9d86caa --- /dev/null +++ b/src/app/pages/admin/TempShop/temp-shop.component.html @@ -0,0 +1,83 @@ +
+
+

Business Information

+
+ +
+ +
+
+ + Shop Name + + + + +
+
+ + Shop Category + + + + + {{category.categoryName}} + + + +
+
+ + Physical Address + + + + +
+ +
+ + Digital Address + + + + + +   + + + + +
+
+ + Date Of Establishment + + + + +
+
+ + Telephone + + + + + +
+
+ + Email Address + + + + +
+ +
+ +
+
+
\ No newline at end of file diff --git a/src/app/pages/admin/TempShop/temp-shop.component.scss b/src/app/pages/admin/TempShop/temp-shop.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/pages/admin/Coupons/coupons.component.spec.ts b/src/app/pages/admin/TempShop/temp-shop.component.spec.ts similarity index 55% rename from src/app/pages/admin/Coupons/coupons.component.spec.ts rename to src/app/pages/admin/TempShop/temp-shop.component.spec.ts index e45fd9d..d0a7628 100644 --- a/src/app/pages/admin/Coupons/coupons.component.spec.ts +++ b/src/app/pages/admin/TempShop/temp-shop.component.spec.ts @@ -1,20 +1,20 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { CouponsComponent } from './coupons.component'; +import { TempShopComponent } from './temp-shop.component'; -describe('CouponsComponent', () => { - let component: CouponsComponent; - let fixture: ComponentFixture; +describe('TempShopComponent', () => { + let component: TempShopComponent; + let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ CouponsComponent ] + declarations: [ TempShopComponent ] }) .compileComponents(); }); beforeEach(() => { - fixture = TestBed.createComponent(CouponsComponent); + fixture = TestBed.createComponent(TempShopComponent); component = fixture.componentInstance; fixture.detectChanges(); }); diff --git a/src/app/pages/admin/TempShop/temp-shop.component.ts b/src/app/pages/admin/TempShop/temp-shop.component.ts new file mode 100644 index 0000000..df0b2ce --- /dev/null +++ b/src/app/pages/admin/TempShop/temp-shop.component.ts @@ -0,0 +1,59 @@ +import { Component, Inject, OnInit } from '@angular/core'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { ConfirmBoxEvokeService } from '@costlydeveloper/ngx-awesome-popup'; +import { UserService } from 'app/core/user/user.service'; +import { BusinessCategory, TesoBusinessDetail } from 'app/models/businessModel'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; +import { ProfileService } from '../Profile/profile.service'; + +@Component({ + selector: 'app-temp-shop', + templateUrl: './temp-shop.component.html', + styleUrls: ['./temp-shop.component.scss'] +}) +export class TempShopComponent implements OnInit { + businessCategories: BusinessCategory[] = []; + selectedCategory: BusinessCategory; + newBusiness: TesoBusinessDetail = { + businessId: "", + Handle: "", + businessName: "", + businessEmail: "", + businessTin: "", + businessDescription: "", + businessCategory: "", + businessAddress: "", + businessContact: "", + businessLogo: "", + dateOfEst: new Date(), + businessDigitalAddress: "", + businessLatitude: "", + businessLongitude: "", + }; + private _unsubscribeAll: Subject = new Subject(); + constructor(public dialogRef: MatDialogRef, private _profileService: ProfileService, private confirmBoxEvokeService: ConfirmBoxEvokeService, + private _userService: UserService) { } + + ngOnInit(): void { + this._profileService.categories$.pipe(takeUntil(this._unsubscribeAll)).subscribe((d) => { + this.businessCategories = d; + }); + } + + closeDialog() { + if (this.newBusiness.businessName != '' && this.newBusiness.businessContact != '' && this.newBusiness.businessDigitalAddress) { + this.confirmBoxEvokeService.info("Confirm Action", `Are you sure you would to add this business as a target for yor coupon ?`, "YES", "NO").subscribe(response => { + if (response.clickedButtonID == "yes") { + this.dialogRef.close(this.newBusiness); + } + }); + }else{ + this.confirmBoxEvokeService.info("Action Required", `To continue you should have the Business Name, Business Telephone and the digital address of the busines entered `, "OK") + .subscribe(response => { + + }); + } + } + +} diff --git a/src/app/pages/admin/TempShop/temp-shop.module.ts b/src/app/pages/admin/TempShop/temp-shop.module.ts new file mode 100644 index 0000000..979a84d --- /dev/null +++ b/src/app/pages/admin/TempShop/temp-shop.module.ts @@ -0,0 +1,53 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { MatButtonModule } from '@angular/material/button'; +import { MatButtonToggleModule } from '@angular/material/button-toggle'; +import { MatRippleModule } from '@angular/material/core'; +import { MatDialogModule } from '@angular/material/dialog'; +import { MatDividerModule } from '@angular/material/divider'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatIconModule } from '@angular/material/icon'; +import { MatInputModule } from '@angular/material/input'; +import { MatMenuModule } from '@angular/material/menu'; +import { MatPaginatorModule } from '@angular/material/paginator'; +import { MatProgressBarModule } from '@angular/material/progress-bar'; +import { MatSelectModule } from '@angular/material/select'; +import { MatSidenavModule } from '@angular/material/sidenav'; +import { MatSortModule } from '@angular/material/sort'; +import { MatTableModule } from '@angular/material/table'; +import { MatTabsModule } from '@angular/material/tabs'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { TranslocoModule } from '@ngneat/transloco'; +import { SharedModule } from 'app/shared/shared.module'; +import { TempShopComponent } from './temp-shop.component'; + + + +@NgModule({ + declarations: [ + TempShopComponent + ], + imports: [ + CommonModule, + MatButtonModule, + MatButtonToggleModule, + MatDividerModule, + MatIconModule, + MatFormFieldModule, + MatInputModule, + MatMenuModule, + MatProgressBarModule, + MatRippleModule, + MatSidenavModule, + MatSortModule, + MatTableModule, + MatTabsModule, + MatDialogModule, + TranslocoModule, + SharedModule, + MatPaginatorModule, + MatSelectModule, + MatTooltipModule, + ] +}) +export class TempShopModule { } diff --git a/src/app/pages/auth/sign-in/sign-in.component.css b/src/app/pages/auth/sign-in/sign-in.component.css index c3c89e4..231b50e 100644 --- a/src/app/pages/auth/sign-in/sign-in.component.css +++ b/src/app/pages/auth/sign-in/sign-in.component.css @@ -1,8 +1,14 @@ -#welcomeBoard { +#welcomeBoard{ background-image: url('assets/images/bg.jpg'); background-position: center; background-size: cover; } +.mobileAuth{ + background-image: url('assets/images/bg.jpg'); + background-position: center; + background-size: cover; + height: 100vh; +} .welcomeText { text-shadow: 2px 2px 20px #C3C76C; diff --git a/src/app/pages/auth/sign-in/sign-in.component.html b/src/app/pages/auth/sign-in/sign-in.component.html index 910c121..3684d30 100644 --- a/src/app/pages/auth/sign-in/sign-in.component.html +++ b/src/app/pages/auth/sign-in/sign-in.component.html @@ -1,7 +1,7 @@
-
+ class="mobileAuth md:flex md:items-center md:justify-end w-full sm:w-auto md:h-full md:w-1/2 py-8 px-4 sm:p-12 md:p-16 sm:rounded-2xl md:rounded-none sm:shadow md:shadow-none sm:bg-card"> +
@@ -27,7 +27,7 @@ - Standard call, message, or data rates may apply. + Standard call, message, or data rates may apply.
diff --git a/src/app/pages/auth/sign-in/signOld.txt b/src/app/pages/auth/sign-in/signOld.txt new file mode 100644 index 0000000..c11e423 --- /dev/null +++ b/src/app/pages/auth/sign-in/signOld.txt @@ -0,0 +1,106 @@ + + +
+ +
+ + +
Sign in
+
+
Enter phone number of business
+
+ + + + {{alert.message}} + + + + + + + + + Standard call, message, or data rates may apply. + + +
+ + Remember me + +
+ + + +
+ +
+
+ +
--> \ No newline at end of file diff --git a/src/app/pipes/couponProductShort.pipe.ts b/src/app/pipes/couponProductShort.pipe.ts new file mode 100644 index 0000000..b3ecd58 --- /dev/null +++ b/src/app/pipes/couponProductShort.pipe.ts @@ -0,0 +1,20 @@ +import { Pipe, PipeTransform } from "@angular/core"; + +@Pipe({ + name: 'couponProductShort' +}) +export class CouponProductShort implements PipeTransform { + + constructor( + ) { } + + transform(value: string): string { + + if (value.length > 70) { + return value.substring(0, 66) + "...."; + } else { + return value; + } + } + +} \ No newline at end of file diff --git a/src/app/pipes/personalized-coupon-worth.pipe.ts b/src/app/pipes/personalized-coupon-worth.pipe.ts new file mode 100644 index 0000000..b3d9f81 --- /dev/null +++ b/src/app/pipes/personalized-coupon-worth.pipe.ts @@ -0,0 +1,13 @@ +import { Pipe, PipeTransform } from '@angular/core'; + +@Pipe({ + name: 'personalizedcouponWorth' +}) +export class PersonalizedCouponWorthPipe implements PipeTransform { + + transform(value: number,rate:number): string { + var worth = (rate * value)/100; + return `GH¢ ${worth}`; + } + +} diff --git a/src/app/pipes/product-coupon-worth.pipe.ts b/src/app/pipes/product-coupon-worth.pipe.ts new file mode 100644 index 0000000..59285ef --- /dev/null +++ b/src/app/pipes/product-coupon-worth.pipe.ts @@ -0,0 +1,13 @@ +import { Pipe, PipeTransform } from '@angular/core'; + +@Pipe({ + name: 'productcouponWorth' +}) +export class ProductCouponWorthPipe implements PipeTransform { + + transform(value: number,rate:number): string { + var worth = (rate * value)/100; + return `GH¢ ${worth}`; + } + +} diff --git a/src/app/pipes/productDescriptionShort.pipe.ts b/src/app/pipes/productDescriptionShort.pipe.ts index d3887b2..82c7a78 100644 --- a/src/app/pipes/productDescriptionShort.pipe.ts +++ b/src/app/pipes/productDescriptionShort.pipe.ts @@ -9,10 +9,19 @@ export class ProductDescriptionShort implements PipeTransform { ) { } transform(value: string): string { - if(value.length > 57){ - return value.substring(0, 58) + "...."; - }else{ - return value; + var content = this.stripHtml(value) + if (content.length > 57) { + return content.substring(0, 58) + "...."; + } else { + return content; } } + stripHtml(html) { + // Create a new div element + var temporalDivElement = document.createElement("div"); + // Set the HTML content with the providen + temporalDivElement.innerHTML = html; + // Retrieve the text property of the element (cross-browser support) + return temporalDivElement.textContent || temporalDivElement.innerText || ""; + } } \ No newline at end of file diff --git a/src/app/pipes/productNameShort.pipe.ts b/src/app/pipes/productNameShort.pipe.ts new file mode 100644 index 0000000..4b69e05 --- /dev/null +++ b/src/app/pipes/productNameShort.pipe.ts @@ -0,0 +1,20 @@ +import { Pipe, PipeTransform } from "@angular/core"; + +@Pipe({ + name: 'productNameShort' +}) +export class ProductNameShort implements PipeTransform { + + constructor( + ) { } + + transform(value: string): string { + + if (value.length > 70) { + return value.substring(0, 66) + "...."; + } else { + return value; + } + } + +} \ No newline at end of file diff --git a/src/assets/icons/icon-128_x_128.png b/src/assets/icons/icon-128_x_128.png new file mode 100644 index 0000000..2deaf39 Binary files /dev/null and b/src/assets/icons/icon-128_x_128.png differ diff --git a/src/assets/icons/icon-144_x_144.png b/src/assets/icons/icon-144_x_144.png new file mode 100644 index 0000000..448e78b Binary files /dev/null and b/src/assets/icons/icon-144_x_144.png differ diff --git a/src/assets/icons/icon-152_x_152.png b/src/assets/icons/icon-152_x_152.png new file mode 100644 index 0000000..0794602 Binary files /dev/null and b/src/assets/icons/icon-152_x_152.png differ diff --git a/src/assets/icons/icon-192_x_192.png b/src/assets/icons/icon-192_x_192.png new file mode 100644 index 0000000..9e558f9 Binary files /dev/null and b/src/assets/icons/icon-192_x_192.png differ diff --git a/src/assets/icons/icon-384_x_384.png b/src/assets/icons/icon-384_x_384.png new file mode 100644 index 0000000..2ef3d92 Binary files /dev/null and b/src/assets/icons/icon-384_x_384.png differ diff --git a/src/assets/icons/icon-512_x_512.png b/src/assets/icons/icon-512_x_512.png new file mode 100644 index 0000000..509fbdc Binary files /dev/null and b/src/assets/icons/icon-512_x_512.png differ diff --git a/src/assets/icons/icon-72_x_72.png b/src/assets/icons/icon-72_x_72.png new file mode 100644 index 0000000..03a1837 Binary files /dev/null and b/src/assets/icons/icon-72_x_72.png differ diff --git a/src/assets/icons/icon-96_x_96.png b/src/assets/icons/icon-96_x_96.png new file mode 100644 index 0000000..013cb45 Binary files /dev/null and b/src/assets/icons/icon-96_x_96.png differ diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts index 5d08331..502b550 100644 --- a/src/environments/environment.prod.ts +++ b/src/environments/environment.prod.ts @@ -1,3 +1,5 @@ export const environment = { - production: true + production: true, + apiURL:"https://test.tesoapp.com/v2/" + // apiURL:"https://localhost:7076/" }; diff --git a/src/environments/environment.ts b/src/environments/environment.ts index 31df6c3..6e495f5 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -4,7 +4,8 @@ export const environment = { production: false, - apiURL:"https://test.tesoapp.com/" + apiURL:"https://test.tesoapp.com/v2/" + // apiURL:"https://localhost:7076/" }; /* @@ -14,4 +15,4 @@ export const environment = { * This import should be commented out in production mode because it will have a negative impact * on performance if an error is thrown. */ -// import 'zone.js/plugins/zone-error'; // Included with Angular CLI. +// import 'zone.js/plugins/zone-error'; // Included with Angular CLI. \ No newline at end of file diff --git a/src/favicon.ico b/src/favicon.ico new file mode 100644 index 0000000..3bdeca1 Binary files /dev/null and b/src/favicon.ico differ diff --git a/src/index.html b/src/index.html index 4d73de1..6b213a0 100644 --- a/src/index.html +++ b/src/index.html @@ -18,10 +18,12 @@ - + + + @@ -29,6 +31,7 @@ + diff --git a/src/manifest.webmanifest b/src/manifest.webmanifest new file mode 100644 index 0000000..352fef8 --- /dev/null +++ b/src/manifest.webmanifest @@ -0,0 +1,51 @@ +{ + "name": "Teso Business", + "short_name": "Teso Business", + "theme_color": "#1976d2", + "background_color": "#fafafa", + "display": "standalone", + "scope": "./", + "start_url": "./", + "icons": [ + { + "src": "assets/icons/icon-72_x_72.png", + "sizes": "72x72", + "type": "image/png" + }, + { + "src": "assets/icons/icon-96_x_96.png", + "sizes": "96x96", + "type": "image/png" + }, + { + "src": "assets/icons/icon-128_x_128.png", + "sizes": "128x128", + "type": "image/png" + }, + { + "src": "assets/icons/icon-144_x_144.png", + "sizes": "144x144", + "type": "image/png" + }, + { + "src": "assets/icons/icon-152_x_152.png", + "sizes": "152x152", + "type": "image/png" + }, + { + "src": "assets/icons/icon-192_x_192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "assets/icons/icon-384_x_384.png", + "sizes": "384x384", + "type": "image/png" + }, + { + "src": "assets/icons/icon-512_x_512.png", + "sizes": "512x512", + "type": "image/png" + } + ] +}