Browse Source

21st March 2022 commit

devBranch
Benjamin Arhen 3 years ago
parent
commit
7b7702c14f
  1. 10
      angular.json
  2. 30
      ngsw-config.json
  3. 188
      package-lock.json
  4. 3
      package.json
  5. 37
      src/app/app.component.ts
  6. 15
      src/app/app.module.ts
  7. 8
      src/app/core/auth/auth.interceptor.ts
  8. 46
      src/app/core/auth/auth.service.ts
  9. 1
      src/app/models/generalModel.ts
  10. 80
      src/app/pages/admin/Coupons/Active/active-coupons.component.html
  11. 56
      src/app/pages/admin/Coupons/Active/active-coupons.component.ts
  12. 83
      src/app/pages/admin/Coupons/AdminNewCoupons/admin-new-coupons.component.html
  13. 42
      src/app/pages/admin/Coupons/AdminNewCoupons/admin-new-coupons.component.ts
  14. 142
      src/app/pages/admin/Coupons/DetailsDialog/details-dialog.component.html
  15. 11
      src/app/pages/admin/Coupons/DetailsDialog/details-dialog.component.scss
  16. 12
      src/app/pages/admin/Coupons/DetailsDialog/details-dialog.component.spec.ts
  17. 69
      src/app/pages/admin/Coupons/DetailsDialog/details-dialog.component.ts
  18. 9
      src/app/pages/admin/Coupons/Inactive/inactive-coupons.component.html
  19. 9
      src/app/pages/admin/Coupons/Inactive/inactive-coupons.component.ts
  20. 58
      src/app/pages/admin/Coupons/NewCoupons/new-coupons.component.html
  21. 64
      src/app/pages/admin/Coupons/NewCoupons/new-coupons.component.ts
  22. 5
      src/app/pages/admin/Coupons/coupons.component.ts
  23. 6
      src/app/pages/admin/Coupons/coupons.module.ts
  24. 22
      src/app/pages/admin/Coupons/coupons.service.ts
  25. 18
      src/app/pages/admin/Followers/PersonalizedCoupons/personalized-coupons.component.html
  26. 25
      src/app/pages/admin/Followers/PersonalizedCoupons/personalized-coupons.component.spec.ts
  27. 70
      src/app/pages/admin/Followers/PersonalizedCoupons/personalized-coupons.component.ts
  28. 2
      src/app/pages/admin/Followers/followers.component.html
  29. 4
      src/app/pages/admin/Followers/followers.module.ts
  30. 1
      src/app/pages/admin/GoldPurchase/gold-transaction.component.html
  31. 0
      src/app/pages/admin/GoldPurchase/gold-transaction.component.scss
  32. 25
      src/app/pages/admin/GoldPurchase/gold-transaction.component.spec.ts
  33. 15
      src/app/pages/admin/GoldPurchase/gold-transaction.component.ts
  34. 36
      src/app/pages/admin/Monthly_Desires/ListDesires/list-desires.component.html
  35. 14
      src/app/pages/admin/Monthly_Desires/ListDesires/list-desires.component.ts
  36. 42
      src/app/pages/admin/Products/AdminCouponDialog/admin-coupon-dialog.component.html
  37. 63
      src/app/pages/admin/Products/AdminCouponDialog/admin-coupon-dialog.component.ts
  38. 2
      src/app/pages/admin/Products/BusinessLookUp/business-look-up.component.html
  39. 17
      src/app/pages/admin/Products/BusinessLookUp/business-look-up.component.ts
  40. 42
      src/app/pages/admin/Products/CouponDialog/coupon-dialog.component.html
  41. 54
      src/app/pages/admin/Products/CouponDialog/coupon-dialog.component.ts
  42. 20
      src/app/pages/admin/Products/Details/details-product.component.html
  43. 18
      src/app/pages/admin/Products/Details/details-product.component.ts
  44. 40
      src/app/pages/admin/Products/EditProduct/edit-product.component.html
  45. 60
      src/app/pages/admin/Products/EditProduct/edit-product.component.ts
  46. 16
      src/app/pages/admin/Products/NewProduct/new-product.component.html
  47. 228
      src/app/pages/admin/Products/NewProduct/new-product.component.ts
  48. 6
      src/app/pages/admin/Products/ProductList/product-list.component.html
  49. 19
      src/app/pages/admin/Products/ProductList/product-list.component.ts
  50. 7
      src/app/pages/admin/Products/products.module.ts
  51. 32
      src/app/pages/admin/Products/products.service.ts
  52. 2
      src/app/pages/admin/Profile/Information/information.component.html
  53. 83
      src/app/pages/admin/TempShop/temp-shop.component.html
  54. 0
      src/app/pages/admin/TempShop/temp-shop.component.scss
  55. 12
      src/app/pages/admin/TempShop/temp-shop.component.spec.ts
  56. 59
      src/app/pages/admin/TempShop/temp-shop.component.ts
  57. 53
      src/app/pages/admin/TempShop/temp-shop.module.ts
  58. 8
      src/app/pages/auth/sign-in/sign-in.component.css
  59. 6
      src/app/pages/auth/sign-in/sign-in.component.html
  60. 106
      src/app/pages/auth/sign-in/signOld.txt
  61. 20
      src/app/pipes/couponProductShort.pipe.ts
  62. 13
      src/app/pipes/personalized-coupon-worth.pipe.ts
  63. 13
      src/app/pipes/product-coupon-worth.pipe.ts
  64. 17
      src/app/pipes/productDescriptionShort.pipe.ts
  65. 20
      src/app/pipes/productNameShort.pipe.ts
  66. BIN
      src/assets/icons/icon-128_x_128.png
  67. BIN
      src/assets/icons/icon-144_x_144.png
  68. BIN
      src/assets/icons/icon-152_x_152.png
  69. BIN
      src/assets/icons/icon-192_x_192.png
  70. BIN
      src/assets/icons/icon-384_x_384.png
  71. BIN
      src/assets/icons/icon-512_x_512.png
  72. BIN
      src/assets/icons/icon-72_x_72.png
  73. BIN
      src/assets/icons/icon-96_x_96.png
  74. 4
      src/environments/environment.prod.ts
  75. 5
      src/environments/environment.ts
  76. BIN
      src/favicon.ico
  77. 5
      src/index.html
  78. 51
      src/manifest.webmanifest

10
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"

30
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)"
]
}
}
]
}

188
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",

3
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",

37
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();
}
}

15
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 },

8
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")
});
}

46
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<any>
{
forgotPassword(email: string): Observable<any> {
return this._httpClient.post('api/auth/forgot-password', email);
}
@ -66,8 +59,7 @@ export class AuthService
*
* @param password
*/
resetPassword(password: string): Observable<any>
{
resetPassword(password: string): Observable<any> {
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<any>
{
signIn(credentials: { email: string; password: string }): Observable<any> {
// 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<any>
{
signInUsingToken(): Observable<any> {
// 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<any>
{
signOut(): Observable<any> {
// 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<any>
{
signUp(user: { name: string; email: string; password: string; company: string }): Observable<any> {
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<any>
{
unlockSession(credentials: { email: string; password: string }): Observable<any> {
return this._httpClient.post('api/auth/unlock-session', credentials);
}
/**
* Check the authentication status
*/
check(): Observable<boolean> {
check(): Observable<boolean> {
// Check if the user is logged in
if (this._authenticated) {
return of(true);

1
src/app/models/generalModel.ts

@ -2,4 +2,5 @@ export interface ProductUpload {
highlight: boolean;
file: File;
imageSRC:string;
id?:string;
}

80
src/app/pages/admin/Coupons/Active/active-coupons.component.html

@ -1,7 +1,7 @@
<div class="xl:col-span-2 flex flex-col flex-auto bg-card shadow rounded-2xl overflow-hidden">
<div class="overflow-x-auto mx-6 py-10">
<table class="w-full bg-transparent" mat-table matSort [dataSource]="couponsDataSource" [trackBy]="trackByFn"
#couponsTable>
#couponsTable *ngIf="!isScreenSmall">
<!-- Transaction ID -->
<ng-container matColumnDef="product">
<th mat-header-cell mat-sort-header *matHeaderCellDef>
@ -9,7 +9,7 @@
</th>
<td mat-cell *matCellDef="let transaction">
<span class="pr-6 whitespace-wrap">
{{transaction.targetProduct}}
{{transaction.targetProduct |couponProductShort}}
</span>
</td>
</ng-container>
@ -51,10 +51,12 @@
Range
</th>
<td mat-cell *matCellDef="let transaction">
<span class="pr-6 whitespace-wrap" style="display: flex;justify-content: center;font-weight: bold;" *ngIf="!transaction.type.includes('FREEBIES')">
{{transaction.lower}} - {{transaction.upper}}
<span class="pr-6 whitespace-wrap" style="display: flex;justify-content: center;font-weight: bold;"
*ngIf="!transaction.type.includes('FREEBIES')">
{{transaction.lower}} % - {{transaction.upper}} %
</span>
<span class="pr-6 whitespace-wrap" style="display: flex;justify-content: center;font-weight: bold;" *ngIf="transaction.type.includes('FREEBIES')">
<span class="pr-6 whitespace-wrap" style="display: flex;justify-content: center;font-weight: bold;"
*ngIf="transaction.type.includes('FREEBIES')">
100% OFF
</span>
</td>
@ -96,15 +98,15 @@
</th>
<td mat-cell *matCellDef="let transaction">
<div style="display: flex;">
<button class=" sm:inline-flex" mat-flat-button>
<a href="#" style="color: #0152cc;margin-right:10px;">Show Details</a>
<button class=" sm:inline-flex" mat-flat-button (click)="showDetails(transaction)">
<a href="javascript:void(0)" style="color: #0152cc;margin-right:10px;">Show Details</a>
</button>
<button class=" sm:inline-flex" mat-flat-button [matMenuTriggerFor]="actionsMenu">
<mat-icon class="icon-size-5" matPrefix [svgIcon]="'heroicons_solid:dots-horizontal'">
</mat-icon>
</button>
<mat-menu #actionsMenu="matMenu">
<button mat-menu-item>Cancel</button>
<button mat-menu-item (click)="cancel(transaction)">Cancel</button>
</mat-menu>
</div>
</td>
@ -113,7 +115,7 @@
<ng-container matColumnDef="recentOrdersTableFooter">
<td class="py-6 px-0 border-0" mat-footer-cell *matFooterCellDef colspan="6">
<mat-paginator style="position:sticky;bottom: 0;width: 100%;" [pageSize]="20"
[pageSizeOptions]="[10, 20, 25,50, 100]" [showFirstLastButtons]="true"></mat-paginator>
[pageSizeOptions]="[10, 20, 25,50, 100]" [length]="couponsDataSource.data.length" [showFirstLastButtons]="true"></mat-paginator>
</td>
</ng-container>
@ -121,5 +123,65 @@
<tr class="coupons-row h-14" mat-row *matRowDef="let row; columns: couponsTableColumns;"></tr>
<tr class="h-16 border-0" mat-footer-row *matFooterRowDef="['recentOrdersTableFooter']"></tr>
</table>
<table class="w-full bg-transparent" mat-table matSort [dataSource]="couponsDataSource" [trackBy]="trackByFn"
#couponsTable *ngIf="isScreenSmall">
<!-- Transaction ID -->
<ng-container matColumnDef="product">
<th mat-header-cell mat-sort-header *matHeaderCellDef>
Coupons
</th>
<td mat-cell *matCellDef="let transaction">
<div style="display: grid;">
<span class="pr-6 whitespace-nowrap">
{{transaction.targetProduct |couponProductShort}}
</span>
<span class="pr-6 whitespace-nowrap">
{{transaction.quantity}} left
</span>
<span class="pr-6 whitespace-wrap"
style="display: flex;justify-content: center;font-weight: bold;"
*ngIf="!transaction.type.includes('FREEBIES')">
{{transaction.lower}} - {{transaction.upper}}
</span>
<span class="pr-6 whitespace-wrap"
style="display: flex;justify-content: center;font-weight: bold;"
*ngIf="transaction.type.includes('FREEBIES')">
{{transaction.type}}
</span>
</div>
</td>
</ng-container>
<ng-container matColumnDef="status">
<th mat-header-cell mat-sort-header *matHeaderCellDef>
</th>
<td mat-cell *matCellDef="let transaction">
<div style="display: grid;">
<span class="whitespace-wrap">
Active
</span>
<span class="whitespace-wrap">
{{transaction.expiration | date:"dd-MM-yyyy"}}
</span>
</div>
</td>
</ng-container>
<!-- Amount -->
<!-- Footer -->
<ng-container matColumnDef="recentOrdersTableFooter">
<td class="py-6 px-0 border-0" mat-footer-cell *matFooterCellDef colspan="6">
<mat-paginator style="position:sticky;bottom: 0;width: 100%;" [pageSize]="20"
[pageSizeOptions]="[10, 20, 25,50, 100]" [showFirstLastButtons]="true"></mat-paginator>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="couponsTableColumnsMobile"></tr>
<tr class="coupons-row h-14" mat-row *matRowDef="let row; columns: couponsTableColumnsMobile;"></tr>
<tr class="h-16 border-0" mat-footer-row *matFooterRowDef="['recentOrdersTableFooter']"></tr>
</table>
</div>
</div>

56
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<any>;
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<any> = new Subject<any>();
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 },
});
}
}

83
src/app/pages/admin/Coupons/AdminNewCoupons/admin-new-coupons.component.html

@ -59,8 +59,8 @@
</div>
<div class="column">
<mat-form-field appearance="fill" style="min-width: 200px;">
<mat-select [(ngModel)]="selectedType">
<mat-option *ngFor="let type of couponTypes" [value]="type">
<mat-select [(ngModel)]="newCoupon.type">
<mat-option *ngFor="let type of couponTypes" [value]="type.typeCode">
{{type.typeName}}
</mat-option>
</mat-select>
@ -76,7 +76,8 @@
</mat-label>
</div>
<div class="column">
<textarea name="" id="" cols="30" rows="5" style="border: 1px solid #cbd5e1;"></textarea>
<textarea name="" id="" cols="30" rows="5" style="border: 1px solid #cbd5e1;"
[(ngModel)]="newCoupon.condition"></textarea>
</div>
</div>
<div class="columns" style="display: flex;align-items: center">
@ -87,51 +88,76 @@
</div>
<div class="column">
<mat-form-field appearance="fill" style="min-width: 200px;">
<input matInput style="margin-left:10px;" type="number">
<input matInput style="margin-left:10px;" type="number" [(ngModel)]="newCoupon.quantity">
</mat-form-field>
</div>
</div>
<div style="display: flex;align-items: baseline" *ngIf="!isScreenSmall">
<div style="display: flex;align-items: baseline"
*ngIf="!isScreenSmall && (newCoupon.type != 'TESCP001' && newCoupon.type != 'TESCP004')">
<mat-label style="margin-right: 48px;">
<strong class="tileHead">Percentage Off </strong>
</mat-label>
<mat-form-field appearance="fill" style="margin-right:10px;">
<span matPrefix>From &nbsp;</span>
<input matInput type="number">
<input matInput type="number" [(ngModel)]="newCoupon.lower">
<span matSuffix>% &nbsp;</span>
</mat-form-field>
<mat-form-field appearance="fill" style="margin-left:10px;">
<span matPrefix>To &nbsp;</span>
<input matInput type="number">
<input matInput type="number" [(ngModel)]="newCoupon.upper">
<span matSuffix>% &nbsp;</span>
</mat-form-field>
</div>
<div *ngIf="isScreenSmall">
<div *ngIf="!isScreenSmall && (newCoupon.type == 'TESCP001' || newCoupon.type == 'TESCP004')">
<div class="columns" style="display: flex;align-items: center">
<div class="column" style="display: flex;align-items: baseline">
<mat-label style="margin-right: 48px;">
<strong class="tileHead">Percentage Off</strong>
</mat-label>
</div>
<div class="column">
<mat-label style="margin-right: 48px;">
Free
</mat-label>
</div>
</div>
</div>
<div *ngIf="isScreenSmall && (newCoupon.type != 'TESCP001' && newCoupon.type != 'TESCP004')">
<mat-label style="margin-right: 48px;">
<strong class="tileHead">Percentage Off </strong>
</mat-label>
<div style="display: flex;align-items: baseline">
<mat-form-field appearance="outline" style="margin-right:10px;">
<span matPrefix>From &nbsp;</span>
<input matInput type="number" [(ngModel)]="fromWorth">
<input matInput type="number" [(ngModel)]="newCoupon.lower">
<span matSuffix>% &nbsp;</span>
</mat-form-field>
</div>
<div style="display: flex;align-items: baseline">
<mat-form-field appearance="outline" style="margin-left:10px;">
<span matPrefix>To &nbsp;</span>
<input matInput type="number" [(ngModel)]="toWorth">
<input matInput type="number" [(ngModel)]="newCoupon.upper">
<span matSuffix>% &nbsp;</span>
</mat-form-field>
</div>
</div>
<div *ngIf="isScreenSmall && (newCoupon.type != 'TESCP001' && newCoupon.type != 'TESCP004')">
<mat-label style="margin-right: 48px;">
<strong class="tileHead">Percentage Off </strong>
</mat-label>
<div style="display: flex;align-items: baseline">
FREE
</div>
</div>
<div style="display: flex;align-items: baseline">
<mat-label style="margin-right: 48px;">
<strong class="tileHead">Coupon Worth </strong>
</mat-label>
<mat-label>
From {{selectedProduct.unitPrice | couponWorth:fromWorth}} To {{selectedProduct.unitPrice | couponWorth:toWorth}}
From {{selectedProduct.unitPrice | couponWorth:newCoupon.lower}} To {{selectedProduct.unitPrice |
couponWorth:newCoupon.upper}}
</mat-label>
</div>
<div style="display: flex;align-items: baseline;margin-top: 20px;" *ngIf="!isScreenSmall">
@ -139,31 +165,32 @@
<strong class="tileHead">Date Of Expiration </strong>
</mat-label>
<mat-form-field appearance="outline">
<input matInput style="margin-left:10px;" type="date">
<input matInput style="margin-left:10px;" type="date" [(ngModel)]="expiryDate">
</mat-form-field>
<mat-form-field appearance="fill" style="width: 200px;">
<span matPrefix>Time &nbsp;</span>
<input matInput type="time">
<input matInput type="time" [(ngModel)]="expiryTime">
</mat-form-field>
</div>
<div *ngIf="isScreenSmall">
<div style="display: flex;align-items: baseline;margin-top: 20px;">
<mat-form-field appearance="outline">
<mat-label style="margin-right: 48px;">
<strong class="tileHead">Date Of Expiration </strong>
</mat-label>
<input matInput style="margin-left:10px;" type="date">
</mat-form-field>
</div>
<div style="display: flex;align-items: baseline;">
<mat-form-field appearance="outline">
<span matPrefix>Time &nbsp;</span>
<input matInput type="time">
</mat-form-field>
<div style="display: flex;align-items: baseline;margin-top: 20px;">
<mat-form-field appearance="outline">
<mat-label style="margin-right: 48px;">
<strong class="tileHead">Date Of Expiration </strong>
</mat-label>
<input matInput style="margin-left:10px;" type="date" [(ngModel)]="expiryDate">
</mat-form-field>
</div>
<div style="display: flex;align-items: baseline;">
<mat-form-field appearance="outline">
<span matPrefix>Time &nbsp;</span>
<input matInput type="time" [(ngModel)]="expiryTime">
</mat-form-field>
</div>
</div>
</div>
<div class="row" style="display: flex;justify-content:space-around;width:100%;">
<button style="background-color: blue;color:white;margin-right:20px;" mat-button>Generate Coupons</button>
<button style="background-color: blue;color:white;margin-right:20px;" mat-button
(click)="generateCoupon()">Generate Coupons</button>
</div>
</div>

42
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<any> = new Subject<any>();
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<AdminNewCouponsComponent>,
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();
});
});
}
}

142
src/app/pages/admin/Coupons/DetailsDialog/details-dialog.component.html

@ -0,0 +1,142 @@
<div class="row" style="display: flex;justify-content:space-between">
<div class="row" style="display: flex;justify-content:center;width: 100%;">
<h2 mat-dialog-title style="text-align: center;">Coupons For {{newCoupon.targetProduct}} </h2>
</div>
<!-- <button mat-button mat-dialog-close>Cancel</button> -->
<button mat-button [mat-dialog-close]="true" cdkFocusInitial>x</button>
</div>
<mat-dialog-content class="mat-typography">
<div class=" py-2 pl-3 pr-3 sm:py-4 md:pl-4 md:pr-6">
<div class="columns" style="display: flex;align-items: center">
<div class="column" style="display: flex;align-items: baseline">
<mat-label style="margin-right: 48px;">
<strong class="tileHead">Product Name </strong>
</mat-label>
</div>
<div class="column">
<mat-label style="margin-right: 48px;">
{{newCoupon.targetProduct}}
</mat-label>
</div>
</div>
<div class="columns" style="display: flex;align-items: center;margin-top: 15px;">
<div class="column" style="display: flex;align-items: baseline">
<mat-label style="margin-right: 48px;">
<strong class="tileHead">Original Price </strong>
</mat-label>
</div>
<div class="column">
<mat-label style="margin-right: 48px;">
{{selectedProduct.unitPrice | currency:"GHS "}}
</mat-label>
</div>
</div>
<div class="columns" style="display: flex;align-items: center;margin-top: 15px;">
<div class="column" style="display: flex;align-items: baseline">
<mat-label style="margin-right: 20px;">
<strong class="tileHead">Coupon Type</strong>
</mat-label>
</div>
<div class="column">
<mat-label style="margin-right: 48px;">
{{newCoupon.type}}
</mat-label>
</div>
</div>
<div class="columns" style="display: flex;align-items: center">
<div class="column">
<mat-label style="margin-right: 48px;">
<strong class="tileHead">Coupon Condition</strong>
</mat-label>
</div>
<div class="column">
<textarea name="" id="" cols="30" rows="5" style="border: 1px solid #cbd5e1;"
[(ngModel)]="newCoupon.condition" disabled></textarea>
</div>
</div>
<div class="columns" style="display: flex;align-items: center">
<div class="column" style="display: flex;align-items: baseline">
<mat-label style="margin-right: 48px;">
<strong class="tileHead">Number Of Coupons</strong>
</mat-label>
</div>
<div class="column">
<mat-form-field appearance="fill" style="min-width: 200px;">
<input matInput disabled style="margin-left:10px;" type="number" [(ngModel)]="newCoupon.quantity">
</mat-form-field>
</div>
</div>
<div style="display: flex;margin-top: 20px;align-items: baseline" *ngIf="!isScreenSmall">
<div class="columns" style="display: flex;align-items: center">
<div class="column" style="display: flex;align-items: baseline">
<mat-label style="margin-right: 48px;">
<strong class="tileHead">Percentage Off </strong>
</mat-label>
</div>
<div class="column">
<mat-label>
From {{newCoupon.lower}}% To {{newCoupon.upper}}%
</mat-label>
</div>
</div>
</div>
<div *ngIf="isScreenSmall">
<mat-label style="margin-right: 48px;">
<strong class="tileHead">Percentage Off </strong>
</mat-label>
<div style="display: flex;align-items: baseline">
<mat-form-field appearance="outline" style="margin-right:10px;">
<span matPrefix>From &nbsp;</span>
<input matInput disabled type="number" [(ngModel)]="newCoupon.lower" min="0" max="100">
<span matSuffix>% &nbsp;</span>
</mat-form-field>
</div>
<div style="display: flex;align-items: baseline">
<mat-form-field appearance="outline" style="margin-left:10px;">
<span matPrefix>To &nbsp;</span>
<input matInput disabled type="number" [(ngModel)]="newCoupon.upper" min="0" max="100">
<span matSuffix>% &nbsp;</span>
</mat-form-field>
</div>
</div>
<div class="columns" style="display: flex;margin-top: 20px;align-items: center">
<div class="column" style="display: flex;align-items: baseline">
<mat-label style="margin-right: 48px;">
<strong class="tileHead">Coupon Worth </strong>
</mat-label>
</div>
<div class="column">
<mat-label>
From {{selectedProduct.unitPrice | couponWorth:newCoupon.lower}} To {{selectedProduct.unitPrice |
couponWorth:newCoupon.upper}}
</mat-label>
</div>
</div>
<div class="columns" style="display: flex;align-items: center;margin-top: 20px;" *ngIf="!isScreenSmall">
<div class="column" style="display: flex;align-items: baseline">
<mat-label style="margin-right: 48px;">
<strong class="tileHead">Date Of Expiration</strong>
</mat-label>
</div>
<div class="column">
<mat-label style="margin-right: 30px;">
{{newCoupon.expiration | date:"medium"}}
</mat-label>
</div>
</div>
<div *ngIf="isScreenSmall">
<div style="display: flex;align-items: baseline;margin-top: 20px;">
<mat-label style="margin-right: 48px;">
<strong class="tileHead">Date Of Expiration </strong>
</mat-label>
<mat-label style="margin-right: 30px;">
{{newCoupon.expiration | date:"medium"}}
</mat-label>
</div>
</div>
</div>
</mat-dialog-content>

11
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;
}

12
src/app/pages/admin/Monthly_Desires/desires.component.spec.ts → 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<DesiresComponent>;
describe('DetailsDialogComponent', () => {
let component: DetailsDialogComponent;
let fixture: ComponentFixture<DetailsDialogComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ DesiresComponent ]
declarations: [ DetailsDialogComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(DesiresComponent);
fixture = TestBed.createComponent(DetailsDialogComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

69
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<any> = new Subject<any>();
constructor(
public dialog: MatDialog, private _productService: ProductsService,
private _couponService: CouponsService, private _tesoMediaWatcherService: tesoMediaWatcherService,
public dialogRef: MatDialogRef<DetailsDialogComponent>,@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 {
}
}

9
src/app/pages/admin/Coupons/Inactive/inactive-coupons.component.html

@ -9,7 +9,7 @@
</th>
<td mat-cell *matCellDef="let transaction">
<span class="pr-6 whitespace-wrap">
{{transaction.targetProduct}}
{{transaction.targetProduct | couponProductShort}}
</span>
</td>
</ng-container>
@ -52,7 +52,7 @@
</th>
<td mat-cell *matCellDef="let transaction">
<span class="pr-6 whitespace-wrap" style="display: flex;justify-content: center;font-weight: bold;" *ngIf="!transaction.type.includes('FREEBIES')">
{{transaction.lower}} - {{transaction.upper}}
{{transaction.lower}}% - {{transaction.upper}}%
</span>
<span class="pr-6 whitespace-wrap" style="display: flex;justify-content: center;font-weight: bold;" *ngIf="transaction.type.includes('FREEBIES')">
100% OFF
@ -74,9 +74,12 @@
Status
</th>
<td mat-cell *matCellDef="let transaction">
<span class="pr-6 whitespace-wrap">
<span class="pr-6 whitespace-wrap" *ngIf="transaction.status != 'cancelled'">
Expired
</span>
<span class="pr-6 whitespace-wrap" *ngIf="transaction.status == 'cancelled'">
Cancelled
</span>
</td>
</ng-container>
<ng-container matColumnDef="expiration">

9
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<any>;
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 {

58
src/app/pages/admin/Coupons/NewCoupons/new-coupons.component.html

@ -44,8 +44,8 @@
</div>
<div class="column">
<mat-form-field appearance="fill" style="min-width: 200px;">
<mat-select [(ngModel)]="selectedType">
<mat-option *ngFor="let type of couponTypes" [value]="type">
<mat-select [(ngModel)]="newCoupon.type">
<mat-option *ngFor="let type of couponTypes" [value]="type.typeCode">
{{type.typeName}}
</mat-option>
</mat-select>
@ -61,7 +61,7 @@
</mat-label>
</div>
<div class="column">
<textarea name="" id="" cols="30" rows="5" style="border: 1px solid #cbd5e1;"></textarea>
<textarea name="" id="" cols="30" rows="5" style="border: 1px solid #cbd5e1;" [(ngModel)]="newCoupon.condition"></textarea>
</div>
</div>
<div class="columns" style="display: flex;align-items: center">
@ -72,52 +72,76 @@
</div>
<div class="column">
<mat-form-field appearance="fill" style="min-width: 200px;">
<input matInput style="margin-left:10px;" type="number">
<input matInput style="margin-left:10px;" type="number" [(ngModel)]="newCoupon.quantity">
</mat-form-field>
</div>
</div>
<div style="display: flex;align-items: baseline" *ngIf="!isScreenSmall">
<div style="display: flex;align-items: baseline"
*ngIf="!isScreenSmall && (newCoupon.type != 'TESCP001' && newCoupon.type != 'TESCP004')">
<mat-label style="margin-right: 48px;">
<strong class="tileHead">Percentage Off </strong>
</mat-label>
<mat-form-field appearance="fill" style="margin-right:10px;">
<span matPrefix>From &nbsp;</span>
<input matInput type="number">
<input matInput type="number" [(ngModel)]="newCoupon.lower">
<span matSuffix>% &nbsp;</span>
</mat-form-field>
<mat-form-field appearance="fill" style="margin-left:10px;">
<span matPrefix>To &nbsp;</span>
<input matInput type="number">
<input matInput type="number" [(ngModel)]="newCoupon.upper">
<span matSuffix>% &nbsp;</span>
</mat-form-field>
</div>
<div *ngIf="isScreenSmall">
<div *ngIf="!isScreenSmall && (newCoupon.type == 'TESCP001' || newCoupon.type == 'TESCP004')">
<div class="columns" style="display: flex;align-items: center">
<div class="column" style="display: flex;align-items: baseline">
<mat-label style="margin-right: 48px;">
<strong class="tileHead">Percentage Off</strong>
</mat-label>
</div>
<div class="column">
<mat-label style="margin-right: 48px;">
Free
</mat-label>
</div>
</div>
</div>
<div *ngIf="isScreenSmall && (newCoupon.type != 'TESCP001' && newCoupon.type != 'TESCP004')">
<mat-label style="margin-right: 48px;">
<strong class="tileHead">Percentage Off </strong>
</mat-label>
<div style="display: flex;align-items: baseline">
<mat-form-field appearance="outline" style="margin-right:10px;">
<span matPrefix>From &nbsp;</span>
<input matInput type="number" [(ngModel)]="fromWorth" min="0" max="100">
<input matInput type="number" [(ngModel)]="newCoupon.lower">
<span matSuffix>% &nbsp;</span>
</mat-form-field>
</div>
<div style="display: flex;align-items: baseline">
<mat-form-field appearance="outline" style="margin-left:10px;">
<span matPrefix>To &nbsp;</span>
<input matInput type="number" [(ngModel)]="toWorth" min="0" max="100">
<input matInput type="number" [(ngModel)]="newCoupon.upper">
<span matSuffix>% &nbsp;</span>
</mat-form-field>
</div>
</div>
<div *ngIf="isScreenSmall && (newCoupon.type != 'TESCP001' && newCoupon.type != 'TESCP004')">
<mat-label style="margin-right: 48px;">
<strong class="tileHead">Percentage Off </strong>
</mat-label>
<div style="display: flex;align-items: baseline">
FREE
</div>
</div>
<div style="display: flex;align-items: baseline">
<mat-label style="margin-right: 48px;">
<strong class="tileHead">Coupon Worth </strong>
</mat-label>
<mat-label>
From {{selectedProduct.unitPrice | couponWorth:fromWorth}} To {{selectedProduct.unitPrice |
couponWorth:toWorth}}
From {{selectedProduct.unitPrice | couponWorth:newCoupon.lower}} To {{selectedProduct.unitPrice |
couponWorth:newCoupon.upper}}
</mat-label>
</div>
<div style="display: flex;align-items: baseline;margin-top: 20px;" *ngIf="!isScreenSmall">
@ -125,11 +149,11 @@
<strong class="tileHead">Date Of Expiration </strong>
</mat-label>
<mat-form-field appearance="outline">
<input matInput style="margin-left:10px;" type="date">
<input matInput style="margin-left:10px;" type="date" [(neModel)]="expiryDate">
</mat-form-field>
<mat-form-field appearance="fill" style="width: 200px;">
<span matPrefix>Time &nbsp;</span>
<input matInput type="time">
<input matInput type="time" [(ngModel)]="expiryTime">
</mat-form-field>
</div>
<div *ngIf="isScreenSmall">
@ -138,18 +162,18 @@
<mat-label style="margin-right: 48px;">
<strong class="tileHead">Date Of Expiration </strong>
</mat-label>
<input matInput style="margin-left:10px;" type="date">
<input matInput style="margin-left:10px;" type="date" [(ngModel)]="expiryDate">
</mat-form-field>
</div>
<div style="display: flex;align-items: baseline;">
<mat-form-field appearance="outline">
<span matPrefix>Time &nbsp;</span>
<input matInput type="time">
<input matInput type="time" [(ngModel)]="expiryTime">
</mat-form-field>
</div>
</div>
<div class="row" style="display: flex;justify-content:space-around;width:100%;">
<button style="background-color: blue;color:white;margin-right:20px;" mat-button>Generate Coupons</button>
<button style="background-color: blue;color:white;margin-right:20px;" mat-button (click)="generateCoupon()">Generate Coupons</button>
</div>
</div>

64
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<any> = new Subject<any>();
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<NewCouponsComponent>,
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();
});
});
}
}

5
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,

6
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),

22
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<any> {
return this._httpClient.post(environment.apiURL + "newcoupon", coupon)
.pipe(
tap((response: CouponsModel) => {
}));
}
generatePersonalizedCoupon(coupon: CouponsModel): Observable<any> {
return this._httpClient.post(environment.apiURL + "coupon-personalized", coupon)
.pipe(
tap((response: CouponsModel) => {
}));
}
cancelCoupon(item: CouponsModel): Observable<any> {
return this._httpClient.post(environment.apiURL + `cancelcoupon`, item).pipe(
tap((response: any) => {
console.log(response)
})
);
}
}

18
src/app/pages/admin/Followers/PersonalizedCoupons/personalized-coupons.component.html

@ -65,7 +65,7 @@
</div>
<div class="column">
<mat-form-field appearance="fill" style="min-width: 200px;">
<mat-select [(ngModel)]="selectedType">
<mat-select [(ngModel)]="newCoupon.type">
<mat-option value="TESCP005">DISCOUNT </mat-option>
<mat-option value="TESCP006">FREEBIE </mat-option>
</mat-select>
@ -80,7 +80,7 @@
</mat-label>
</div>
<div class="column">
<textarea name="" id="" cols="30" rows="5" style="border: 1px solid #cbd5e1;"></textarea>
<textarea name="" id="" cols="30" rows="5" style="border: 1px solid #cbd5e1;" [(ngModel)]="newCoupon.condition"></textarea>
</div>
</div>
<div class="columns" style="display: flex;align-items: center">
@ -91,7 +91,7 @@
</div>
<div class="column">
<mat-form-field appearance="fill" style="min-width: 200px;">
<input matInput style="margin-left:10px;" type="number">
<input matInput style="margin-left:10px;" type="number" [(ngModel)]="newCoupon.quantity">
</mat-form-field>
</div>
@ -102,12 +102,12 @@
</mat-label>
<mat-form-field appearance="fill" style="margin-right:10px;">
<span matPrefix>From &nbsp;</span>
<input matInput type="number">
<input matInput type="number" [(ngModel)]="newCoupon.lower">
<span matSuffix>% &nbsp;</span>
</mat-form-field>
<mat-form-field appearance="fill" style="margin-left:10px;">
<span matPrefix>To &nbsp;</span>
<input matInput type="number">
<input matInput type="number" [(ngModel)]="newCoupon.upper">
<span matSuffix>% &nbsp;</span>
</mat-form-field>
</div>
@ -116,7 +116,7 @@
<strong class="tileHead">Coupon Worth </strong>
</mat-label>
<mat-label>
From GH¢200 To GH¢800
From {{selectedProduct.unitPrice | personalizedcouponWorth:newCoupon.lower}} To {{selectedProduct.unitPrice | personalizedcouponWorth:newCoupon.upper}}
</mat-label>
</div>
<div style="display: flex;align-items: baseline;margin-top: 20px;">
@ -124,15 +124,15 @@
<strong class="tileHead">Date Of Expiration </strong>
</mat-label>
<mat-form-field appearance="outline">
<input matInput style="margin-left:10px;" type="date">
<input matInput style="margin-left:10px;" type="date" [(ngModel)]="expiryDate">
</mat-form-field>
<mat-form-field appearance="fill">
<span matPrefix>Time &nbsp;</span>
<input matInput type="time">
<input matInput type="time" [(ngModel)]="expiryTime">
</mat-form-field>
</div>
<div class="row" style="display: flex;justify-content:space-around;width:100%;">
<button style="background-color: blue;color:white;margin-right:20px;" mat-button>Generate Coupons</button>
<button style="background-color: blue;color:white;margin-right:20px;" mat-button (click)="generateCoupon()">Generate Coupons</button>
</div>
</div>

25
src/app/pages/admin/Followers/PersonalizedCoupons/personalized-coupons.component.spec.ts

@ -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<PersonalizedCouponsComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ PersonalizedCouponsComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(PersonalizedCouponsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

70
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<any> = new Subject<any>();
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<any> = new Subject<any>();
constructor(private _productService: ProductsService,
@Inject(MAT_DIALOG_DATA) public data: { subscriber: TesoUserDetails },
private _couponService: CouponsService, private _tesoMediaWatcherService: tesoMediaWatcherService,
public dialogRef: MatDialogRef<PersonalizedCouponsComponent>,
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();
});
});
}
}

2
src/app/pages/admin/Followers/followers.component.html

@ -6,7 +6,7 @@
<div class="flex items-center justify-between w-full" style="margin-bottom: 20px;">
<div>
<div class="text-3xl font-semibold tracking-tight leading-8">Subscribers</div>
<div class="font-medium tracking-tight text-secondary">Here is a list of all your subscribers on Teso (850)
<div class="font-medium tracking-tight text-secondary">Here is a list of all your subscribers on Teso ({{data.length}})
</div>
</div>
<div class="flex flex-shrink-0 items-center mt-6 sm:mt-0 sm:ml-4">

4
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),

1
src/app/pages/admin/GoldPurchase/gold-transaction.component.html

@ -0,0 +1 @@
<p>gold-transaction works!</p>

0
src/app/pages/admin/GoldPurchase/gold-transaction.component.scss

25
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<GoldTransactionComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ GoldTransactionComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(GoldTransactionComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

15
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 {
}
}

36
src/app/pages/admin/Monthly_Desires/ListDesires/list-desires.component.html

@ -1,12 +1,13 @@
<div class="flex flex-col flex-auto w-full">
<div class="flex flex-wrap w-full max-w-screen-xl mx-auto p-6 md:p-8">
<div class="flex flex-wrap w-full max-w-screen-xl mx-auto p-6 md:p-8" *ngIf="data.mostWishedProduct.length != 0">
<!-- Title and action buttons -->
<div class="flex items-center justify-between w-full" style="margin-bottom: 20px;" *ngIf="!isScreenSmall">
<div>
<div class="text-3xl font-semibold tracking-tight leading-8">Monthly Desires</div>
<div class="font-medium tracking-tight text-secondary">Here is a list of all products in your Teso (850)
<div class="font-medium tracking-tight text-secondary">({{data.mostWishedProduct.length}}) wanted
products
</div>
</div>
@ -22,7 +23,7 @@
<div class="flex items-center justify-between w-full" style="margin-bottom: 20px;" *ngIf="isScreenSmall">
<div>
<div class="text-3xl font-semibold tracking-tight leading-8">Monthly Desires</div>
<div class="font-medium tracking-tight text-secondary">(850) products
<div class="font-medium tracking-tight text-secondary">({{data.mostWishedProduct.length}}) products
</div>
</div>
@ -45,14 +46,17 @@
<div class="product">
<div class="product-content">
<div class="product-img">
<img [src]="imageLoader(product.productImage)" alt="product image" style="max-height:200px;" *ngIf="product.productImage != null">
<img [src]="imageLoader(product.productImage)" alt="product image" style="max-height:200px;"
*ngIf="product.productImage != null">
<!-- <img [src]="imageLoader(product.productImage)" alt="product image" style="max-height:200px;" *ngIf="productImage == null"> -->
</div>
<div class="product-btns">
<button type="button" class="btn-buy" (click)="generateCoupon(product.productID)" *ngIf="availableP.includes(product.productID)">
<button type="button" class="btn-buy" (click)="generateCoupon(product.productID)"
*ngIf="availableP.includes(product.productID)">
Generate Coupons
</button>
<button type="button" class="btn-cart" (click)="createProduct()" *ngIf="!availableP.includes(product.productID)">
<button type="button" class="btn-cart" (click)="createProduct()"
*ngIf="!availableP.includes(product.productID)">
Add Product
</button>
</div>
@ -63,9 +67,14 @@
<div class="product-info-top">
<h2 class="sm-title">{{product.category}}</h2>
</div>
<a href="javascript:void(0)" class="product-name" *ngIf="product.productName === null">{{product.productID}}</a>
<a href="javascript:void(0)" class="product-name" *ngIf="product.productName != null">{{product.productName}}</a>
<p class="product-price"><mat-icon style="color: red;" class="icon-size-5" [svgIcon]="'heroicons_solid:heart'"></mat-icon> {{product.desires}}</p>
<a href="javascript:void(0)" class="product-name"
*ngIf="product.productName === null">{{product.productID}}</a>
<a href="javascript:void(0)" class="product-name"
*ngIf="product.productName != null">{{product.productName}}</a>
<p class="product-price">
<mat-icon style="color: red;" class="icon-size-5" [svgIcon]="'heroicons_solid:heart'">
</mat-icon> {{product.desires}}
</p>
</div>
<!-- </div> -->
</div>
@ -74,9 +83,10 @@
<mat-paginator
class="sticky sm:absolute sm:inset-x-0 sm:bottom-0 border-b sm:border-t sm:border-b-0 z-10 bg-gray-50 dark:bg-transparent"
[pageSizeOptions]="[5, 10, 25, 100]" [showFirstLastButtons]="true"></mat-paginator>
<ng-template #noProducts>
<div class="p-8 sm:p-16 border-t text-4xl font-semibold tracking-tight text-center">There are no monthly desires yet!
</div>
</ng-template>
</div>
<div class="flex flex-wrap w-full max-w-screen-xl mx-auto p-6 md:p-8" *ngIf="data.mostWishedProduct.length == 0">
<div class="p-8 sm:p-16 border-t text-4xl font-semibold tracking-tight text-center">There are no monthly desires
yet!
</div>
</div>
</div>

14
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<any> = new Subject<any>();
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;
}
}
}

42
src/app/pages/admin/Products/AdminCouponDialog/admin-coupon-dialog.component.html

@ -53,8 +53,8 @@
</div>
<div class="column">
<mat-form-field appearance="fill" style="min-width: 200px;">
<mat-select [(ngModel)]="selectedType">
<mat-option *ngFor="let type of couponTypes" [value]="type">
<mat-select [(ngModel)]="newCoupon.type">
<mat-option *ngFor="let type of couponTypes" [value]="type.typeCode">
{{type.typeName}}
</mat-option>
</mat-select>
@ -70,7 +70,7 @@
</mat-label>
</div>
<div class="column">
<textarea name="" id="" cols="30" rows="5" style="border: 1px solid #cbd5e1;"></textarea>
<textarea name="" id="" cols="30" rows="5" style="border: 1px solid #cbd5e1;" [(ngModel)]="newCoupon.condition"></textarea>
</div>
</div>
<div class="columns" style="display: flex;align-items: center">
@ -81,7 +81,7 @@
</div>
<div class="column">
<mat-form-field appearance="fill" style="min-width: 200px;">
<input matInput style="margin-left:10px;" type="number">
<input matInput style="margin-left:10px;" type="number" [(ngModel)]="newCoupon.quantity">
</mat-form-field>
</div>
@ -92,12 +92,12 @@
</mat-label>
<mat-form-field appearance="fill" style="margin-right:10px;">
<span matPrefix>From &nbsp;</span>
<input matInput type="number">
<input matInput type="number" [(ngModel)]="newCoupon.lower">
<span matSuffix>% &nbsp;</span>
</mat-form-field>
<mat-form-field appearance="fill" style="margin-left:10px;">
<span matPrefix>To &nbsp;</span>
<input matInput type="number">
<input matInput type="number" [(ngModel)]="newCoupon.upper">
<span matSuffix>% &nbsp;</span>
</mat-form-field>
</div>
@ -106,7 +106,8 @@
<strong class="tileHead">Coupon Worth </strong>
</mat-label>
<mat-label>
From GH¢200 To GH¢800
From {{data.product.unitPrice | productcouponWorth:newCoupon.lower}} To {{data.product.unitPrice |
productcouponWorth:newCoupon.upper}}
</mat-label>
</div>
<div style="display: flex;align-items: baseline;margin-top: 20px;">
@ -114,15 +115,15 @@
<strong class="tileHead">Date Of Expiration </strong>
</mat-label>
<mat-form-field appearance="outline">
<input matInput style="margin-left:10px;" type="date">
<input matInput style="margin-left:10px;" type="date" [(ngModel)]="expiryDate">
</mat-form-field>
<mat-form-field appearance="fill">
<span matPrefix>Time &nbsp;</span>
<input matInput type="time">
<input matInput type="time" [(ngModel)]="expiryTime">
</mat-form-field>
</div>
<div class="row" style="display: flex;justify-content:space-around;width:100%;">
<button style="background-color: blue;color:white;margin-right:20px;" mat-button>Generate Coupons</button>
<button style="background-color: blue;color:white;margin-right:20px;" mat-button (click)="generateCoupon()">Generate Coupons</button>
</div>
</div>
@ -172,8 +173,8 @@
<mat-label style="margin-right: 20px;">
<strong class="tileHead">Coupon Type</strong>
</mat-label>
<mat-select [(ngModel)]="selectedType">
<mat-option *ngFor="let type of couponTypes" [value]="type">
<mat-select [(ngModel)]="newCoupon.type">
<mat-option *ngFor="let type of couponTypes" [value]="type.typeCode">
{{type.typeName}}
</mat-option>
</mat-select>
@ -189,7 +190,7 @@
</mat-label>
</div>
<div class="column">
<textarea name="" id="" cols="30" rows="5" style="border: 1px solid #cbd5e1;"></textarea>
<textarea name="" id="" cols="30" rows="5" style="border: 1px solid #cbd5e1;" [(ngModel)]="newCoupon.condition"></textarea>
</div>
</div>
<div class="columns" style="display: flex;align-items: center">
@ -199,7 +200,7 @@
<mat-label style="margin-right: 48px;">
<strong class="tileHead">Number Of Coupons</strong>
</mat-label>
<input matInput style="margin-left:10px;" type="number">
<input matInput style="margin-left:10px;" type="number" [(ngModel)]="newCoupon.quantity">
</mat-form-field>
</div>
@ -210,14 +211,14 @@
<div style="display: flex;align-items: baseline">
<mat-form-field appearance="outline" style="margin-right:10px;">
<span matPrefix>From &nbsp;</span>
<input matInput type="number">
<input matInput type="number" [(ngModel)]="newCoupon.lower">
<span matSuffix>% &nbsp;</span>
</mat-form-field>
</div>
<div style="display: flex;align-items: baseline">
<mat-form-field appearance="outline" style="margin-left:10px;">
<span matPrefix>To &nbsp;</span>
<input matInput type="number">
<input matInput type="number" [(ngModel)]="newCoupon.upper">
<span matSuffix>% &nbsp;</span>
</mat-form-field>
</div>
@ -226,7 +227,8 @@
<strong class="tileHead">Coupon Worth </strong>
</mat-label>
<mat-label>
From GH¢200 To GH¢800
From {{data.product.unitPrice | productcouponWorth:newCoupon.lower}} To {{data.product.unitPrice |
productcouponWorth:newCoupon.upper}}
</mat-label>
</div>
<div style="display: flex;align-items: baseline;margin-top: 20px;">
@ -234,17 +236,17 @@
<mat-label style="margin-right: 48px;">
<strong class="tileHead">Date Of Expiration </strong>
</mat-label>
<input matInput style="margin-left:10px;" type="date">
<input matInput style="margin-left:10px;" type="date" [(ngModel)]="expiryDate">
</mat-form-field>
</div>
<div style="display: flex;align-items: baseline;">
<mat-form-field appearance="outline">
<span matPrefix>Time &nbsp;</span>
<input matInput type="time">
<input matInput type="time" [(ngModel)]="expiryTime">
</mat-form-field>
</div>
<div class="row" style="display: flex;justify-content:space-around;width:100%;">
<button style="background-color: blue;color:white;margin-right:20px;" mat-button>Generate Coupons</button>
<button style="background-color: blue;color:white;margin-right:20px;" mat-button (click)="generateCoupon()">Generate Coupons</button>
</div>
</div>

63
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<any> = new Subject<any>();
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<AdminCouponDialogComponent>,
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();
}
}
}

2
src/app/pages/admin/Products/BusinessLookUp/business-look-up.component.html

@ -15,7 +15,7 @@
</div>
<div class="flex flex-shrink-0 items-center justify-center" style="margin-top: 10px !important;">
<a href="javascript:void(0)"
style="color: #1890ff;text-decoration: none;background-color: transparent;outline: 0;cursor: pointer;">Target
style="color: #1890ff;text-decoration: none;background-color: transparent;outline: 0;cursor: pointer;" (click)="temporaryShop()">Target
business not registered on teso ?</a>
</div>
<table class="w-full bg-transparent" mat-table matSort [dataSource]="typesDataSource" [trackBy]="trackByFn"

17
src/app/pages/admin/Products/BusinessLookUp/business-look-up.component.ts

@ -1,11 +1,12 @@
import { Component, Inject, OnInit, ViewChild } from '@angular/core';
import { FormControl } from '@angular/forms';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { MatSort } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table';
import { TesoBusinessDetail } from 'app/models/businessModel';
import { Subject } from 'rxjs';
import { ProfileService } from '../../Profile/profile.service';
import { TempShopComponent } from '../../TempShop/temp-shop.component';
@Component({
selector: 'app-business-look-up',
@ -21,7 +22,7 @@ export class BusinessLookUpComponent implements OnInit {
htmlRequest: string;
transferType: string;
private _unsubscribeAll: Subject<any> = new Subject<any>();
constructor(private _businessService: ProfileService, public dialogRef: MatDialogRef<BusinessLookUpComponent>,
constructor(private _businessService: ProfileService, public dialogRef: MatDialogRef<BusinessLookUpComponent>,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);
});
}
}

42
src/app/pages/admin/Products/CouponDialog/coupon-dialog.component.html

@ -39,8 +39,8 @@
</div>
<div class="column">
<mat-form-field appearance="fill" style="min-width: 200px;">
<mat-select [(ngModel)]="selectedType">
<mat-option *ngFor="let type of couponTypes" [value]="type">
<mat-select [(ngModel)]="newCoupon.type">
<mat-option *ngFor="let type of couponTypes" [value]="type.typeCode">
{{type.typeName}}
</mat-option>
</mat-select>
@ -56,7 +56,7 @@
</mat-label>
</div>
<div class="column">
<textarea name="" id="" cols="30" rows="5" style="border: 1px solid #cbd5e1;"></textarea>
<textarea name="" id="" cols="30" rows="5" style="border: 1px solid #cbd5e1;" [(ngModel)]="newCoupon.condition"></textarea>
</div>
</div>
<div class="columns" style="display: flex;align-items: center">
@ -67,7 +67,7 @@
</div>
<div class="column">
<mat-form-field appearance="fill" style="min-width: 200px;">
<input matInput style="margin-left:10px;" type="number">
<input matInput style="margin-left:10px;" type="number" [(ngModel)]="newCoupon.quantity">
</mat-form-field>
</div>
@ -78,12 +78,12 @@
</mat-label>
<mat-form-field appearance="fill" style="margin-right:10px;">
<span matPrefix>From &nbsp;</span>
<input matInput type="number">
<input matInput type="number" [(ngModel)]="newCoupon.lower">
<span matSuffix>% &nbsp;</span>
</mat-form-field>
<mat-form-field appearance="fill" style="margin-left:10px;">
<span matPrefix>To &nbsp;</span>
<input matInput type="number">
<input matInput type="number" [(ngModel)]="newCoupon.upper">
<span matSuffix>% &nbsp;</span>
</mat-form-field>
</div>
@ -92,7 +92,8 @@
<strong class="tileHead">Coupon Worth </strong>
</mat-label>
<mat-label>
From GH¢200 To GH¢800
From {{data.product.unitPrice | productcouponWorth:newCoupon.lower}} To {{data.product.unitPrice |
productcouponWorth:newCoupon.upper}}
</mat-label>
</div>
<div style="display: flex;align-items: baseline;margin-top: 20px;">
@ -100,15 +101,15 @@
<strong class="tileHead">Date Of Expiration </strong>
</mat-label>
<mat-form-field appearance="outline">
<input matInput style="margin-left:10px;" type="date">
<input matInput style="margin-left:10px;" type="date" [(ngModel)]="expiryDate">
</mat-form-field>
<mat-form-field appearance="fill">
<span matPrefix>Time &nbsp;</span>
<input matInput type="time">
<input matInput type="time" [(ngModel)]="expiryTime">
</mat-form-field>
</div>
<div class="row" style="display: flex;justify-content:space-around;width:100%;">
<button style="background-color: blue;color:white;margin-right:20px;" mat-button>Generate Coupons</button>
<button style="background-color: blue;color:white;margin-right:20px;" mat-button (click)="generateCoupon()">Generate Coupons</button>
</div>
</div>
@ -144,8 +145,8 @@
<mat-label style="margin-right: 20px;">
<strong class="tileHead">Coupon Type</strong>
</mat-label>
<mat-select [(ngModel)]="selectedType">
<mat-option *ngFor="let type of couponTypes" [value]="type">
<mat-select [(ngModel)]="newCoupon.type">
<mat-option *ngFor="let type of couponTypes" [value]="type.typeCode">
{{type.typeName}}
</mat-option>
</mat-select>
@ -161,7 +162,7 @@
</mat-label>
</div>
<div class="column">
<textarea name="" id="" cols="30" rows="5" style="border: 1px solid #cbd5e1;"></textarea>
<textarea name="" id="" cols="30" rows="5" style="border: 1px solid #cbd5e1;" [(ngModel)]="newCoupon.condition"></textarea>
</div>
</div>
<div class="columns" style="display: flex;align-items: center">
@ -171,7 +172,7 @@
<mat-label style="margin-right: 48px;">
<strong class="tileHead">Number Of Coupons</strong>
</mat-label>
<input matInput style="margin-left:10px;" type="number">
<input matInput style="margin-left:10px;" type="number" [(ngModel)]="newCoupon.quantity">
</mat-form-field>
</div>
@ -182,14 +183,14 @@
<div style="display: flex;align-items: baseline">
<mat-form-field appearance="outline" style="margin-right:10px;">
<span matPrefix>From &nbsp;</span>
<input matInput type="number">
<input matInput type="number" [(ngModel)]="newCoupon.lower">
<span matSuffix>% &nbsp;</span>
</mat-form-field>
</div>
<div style="display: flex;align-items: baseline">
<mat-form-field appearance="outline" style="margin-left:10px;">
<span matPrefix>To &nbsp;</span>
<input matInput type="number">
<input matInput type="number" [(ngModel)]="newCoupon.upper">
<span matSuffix>% &nbsp;</span>
</mat-form-field>
</div>
@ -198,7 +199,8 @@
<strong class="tileHead">Coupon Worth </strong>
</mat-label>
<mat-label>
From GH¢200 To GH¢800
From {{data.product.unitPrice | productcouponWorth:newCoupon.lower}} To {{data.product.unitPrice |
productcouponWorth:newCoupon.upper}}
</mat-label>
</div>
<div style="display: flex;align-items: baseline;margin-top: 20px;">
@ -206,17 +208,17 @@
<mat-label style="margin-right: 48px;">
<strong class="tileHead">Date Of Expiration </strong>
</mat-label>
<input matInput style="margin-left:10px;" type="date">
<input matInput style="margin-left:10px;" type="date" [(ngModel)]="expiryDate">
</mat-form-field>
</div>
<div style="display: flex;align-items: baseline;">
<mat-form-field appearance="outline">
<span matPrefix>Time &nbsp;</span>
<input matInput type="time">
<input matInput type="time" [(ngModel)]="expiryTime">
</mat-form-field>
</div>
<div class="row" style="display: flex;justify-content:space-around;width:100%;">
<button style="background-color: blue;color:white;margin-right:20px;" mat-button>Generate Coupons</button>
<button style="background-color: blue;color:white;margin-right:20px;" mat-button (click)="generateCoupon()">Generate Coupons</button>
</div>
</div>

54
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<any> = new Subject<any>();
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<CouponDialogComponent>,
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();
});
});
}
}

20
src/app/pages/admin/Products/Details/details-product.component.html

@ -11,7 +11,7 @@
<mat-icon [svgIcon]="'heroicons_outline:arrow-sm-left'"></mat-icon>
</button>
<h2 class="ml-2.5 text-md sm:text-xl font-medium tracking-tight truncate">
Product # {{product.productID}}
Product : {{product.productName}}
</h2>
</div>
<div
@ -75,8 +75,8 @@
<div class="font-medium tracking-tight text-secondary">Add up to 10 high
quality images of the product
</div>
<div class="font-medium tracking-tight text-secondary">
{{productImages.length}} out of
<div class="font-medium tracking-tight text-secondary" *ngIf="product.images != null">
{{product.images.length}} out of
<strong>10</strong>
</div>
</div>
@ -84,12 +84,12 @@
</div>
<div class="imageList">
<div class="imageHolder" *ngFor="let images of product.images">
<img src="{{images.imageSRC}}" class="productImage">
<button class="deleteelement" style="border:none;"
<img [src]="imageLoader(images.path)" alt="product image" class="productImage">
<!-- <button class="deleteelement" style="border:none;"
(click)="removeImage(images)">
<mat-icon [svgIcon]="'heroicons_outline:trash'" style="color: red;">
</mat-icon>
</button>
</button> -->
</div>
</div>
</div>
@ -175,19 +175,19 @@
<div class="font-medium tracking-tight text-secondary">Add up to 10 high
quality images of the product
</div>
<div class="font-medium tracking-tight text-secondary">0 out of
<div class="font-medium tracking-tight text-secondary" *ngIf="product.images != null">{{product.images.length}} out of
<strong>10</strong>
</div>
</div>
</div>
<div class="imageList">
<div class="imageHolder" *ngFor="let images of productImages">
<img src="{{images.imageSRC}}" class="productImage">
<button class="deleteelement" style="border:none;"
<img [src]="imageLoader(images.path)" alt="product image" class="productImage">
<!-- <button class="deleteelement" style="border:none;"
(click)="removeImage(images)">
<mat-icon [svgIcon]="'heroicons_outline:trash'" style="color: red;">
</mat-icon>
</button>
</button> -->
</div>
</div>
</div>

18
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}`;
}
}

40
src/app/pages/admin/Products/EditProduct/edit-product.component.html

@ -11,7 +11,7 @@
<mat-icon [svgIcon]="'heroicons_outline:arrow-sm-left'"></mat-icon>
</button>
<h2 class="ml-2.5 text-md sm:text-xl font-medium tracking-tight truncate">
Product # {{product.productID}}
Product : {{product.productName}}
</h2>
</div>
<div
@ -28,18 +28,19 @@
<strong class="tileHead">Product Name </strong>
</mat-label>
<mat-form-field appearance="fill" style="width: 350px;">
<input matInput style="margin-left:10px;" type="text">
<input matInput style="margin-left:10px;" type="text" [(ngModel)]="product.productName">
</mat-form-field>
</div>
<div style="display:flex;">
<mat-label style="margin-right: 20px;">
<strong class="tileHead">Product Category</strong>
<strong>Product Category</strong>
</mat-label>
<mat-form-field appearance="fill" style="width: 350px;">
<mat-select required>
<mat-option value="low">Lowdsadsadsadsadsadsa</mat-option>
<mat-option value="standard">Standardasdsadasdas</mat-option>
<mat-option value="high">Highsadsadsadas</mat-option>
<mat-select [(ngModel)]="product.categoryID">
<mat-option *ngFor="let category of categories"
[value]="category.catCode">
{{category.catName}}
</mat-option>
</mat-select>
</mat-form-field>
</div>
@ -49,7 +50,7 @@
</mat-label>
<mat-form-field appearance="fill" style="width: 350px;">
<span matPrefix>GH¢ &nbsp;</span>
<input matInput style="margin-left:10px;" type="number">
<input matInput style="margin-left:10px;" type="number" [(ngModel)]="product.unitPrice">
</mat-form-field>
</div>
@ -61,7 +62,7 @@
</div>
</div>
<div class="column">
<quill-editor [modules]="quillConfig" [(ngModel)]="htmlRequest"
<quill-editor [modules]="quillConfig" [(ngModel)]="product.productDesc"
placeholder="Further details">
</quill-editor>
@ -78,18 +79,18 @@
quality images of the product
</div>
<div class="font-medium tracking-tight text-secondary">
{{productImages.length}} out of
{{product.images.length}} out of
<strong>10</strong>
</div>
</div>
<input style="display: none" type="file" (change)="onFileSelected($event)"
#fileInput accept="image/*" capture="user">
<button style="background-color: blue;color:white;margin-right:20px;" mat-button
(click)="fileInput.click()" [disabled]="productImages.length > 9">Add Image
(click)="fileInput.click()" [disabled]="product.images.length > 9">Add Image
</button>
</div>
<div class="imageList">
<div class="imageHolder" *ngFor="let images of productImages">
<div class="imageHolder" *ngFor="let images of product.images">
<img src="{{images.imageSRC}}" class="productImage">
<button class="deleteelement" style="border:none;"
(click)="removeImage(images)">
@ -103,8 +104,7 @@
</mat-tab-group>
<div class="row" style="display: flex;justify-content:space-around;width:100%;">
<button style="background-color: green;color:white;margin-right:20px;" mat-button
(click)="submit()">Add
Product</button>
(click)="submit()">Save Changes</button>
<button style="background-color: firebrick;color:white;margin-left:10px;" mat-button
cdkFocusInitial>Cancel</button>
</div>
@ -152,10 +152,11 @@
<strong>Product Category</strong>
</mat-label>
<mat-form-field appearance="fill">
<mat-select required>
<mat-option value="low">Lowdsadsadsadsadsadsa</mat-option>
<mat-option value="standard">Standardasdsadasdas</mat-option>
<mat-option value="high">Highsadsadsadas</mat-option>
<mat-select [(ngModel)]="product.categoryID">
<mat-option *ngFor="let category of categories"
[value]="category.catCode">
{{category.catName}}
</mat-option>
</mat-select>
</mat-form-field>
</div>
@ -218,8 +219,7 @@
</mat-tab>
</mat-tab-group>
<div class="row" style="display: flex;justify-content:space-around;width:100%;">
<button style="background-color: green;color:white;margin-right:20px;" mat-button (click)="submit()">Add
Product</button>
<button style="background-color: green;color:white;margin-right:20px;" mat-button (click)="submit()">Save Changes</button>
<button style="background-color: firebrick;color:white;margin-left:10px;" mat-button
cdkFocusInitial>Cancel</button>
</div>

60
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<any> = new Subject<any>();
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 {

16
src/app/pages/admin/Products/NewProduct/new-product.component.html

@ -28,7 +28,7 @@
<strong class="tileHead">Product Name </strong>
</mat-label>
<mat-form-field appearance="fill" style="width: 350px;">
<input matInput style="margin-left:10px;" type="text">
<input matInput style="margin-left:10px;" type="text" [(ngModel)]="newProduct.productName">
</mat-form-field>
</div>
<div style="display:flex;">
@ -36,7 +36,7 @@
<strong class="tileHead">Product Category</strong>
</mat-label>
<mat-form-field appearance="fill" style="width: 350px;">
<mat-select [(ngModel)]="selected">
<mat-select [(ngModel)]="newProduct.categoryID">
<mat-option *ngFor="let category of categories"
[value]="category.catCode">
{{category.catName}}
@ -50,7 +50,7 @@
</mat-label>
<mat-form-field appearance="fill" style="width: 350px;">
<span matPrefix>GH¢ &nbsp;</span>
<input matInput style="margin-left:10px;" type="number">
<input matInput style="margin-left:10px;" type="number" [(ngModel)]="newProduct.unitPrice">
</mat-form-field>
</div>
@ -146,7 +146,7 @@
<mat-label style="margin-right: 20px;">
<strong>Product Name </strong>
</mat-label>
<input matInput style="margin-left:10px;" type="text">
<input matInput style="margin-left:10px;" type="text" [(ngModel)]="newProduct.productName">
</mat-form-field>
</div>
<div style="display: flex;justify-content: space-between;">
@ -154,7 +154,7 @@
<mat-label style="margin-right: 20px;">
<strong>Product Category</strong>
</mat-label>
<mat-select [(ngModel)]="selected">
<mat-select [(ngModel)]="newProduct.categoryID">
<mat-option *ngFor="let category of categories"
[value]="category.catCode">
{{category.catName}}
@ -168,7 +168,7 @@
<strong> Price </strong>
</mat-label>
<span matSuffix>GH¢ &nbsp;</span>
<input matInput style="margin-left:10px;" type="number">
<input matInput style="margin-left:10px;" type="number" [(ngModel)]="newProduct.unitPrice">
</mat-form-field>
</div>
@ -180,7 +180,7 @@
</div>
</div>
<div class="column">
<quill-editor [modules]="quillConfig" [(ngModel)]="htmlRequest"
<quill-editor [modules]="quillConfig" [(ngModel)]="newProduct.productDesc"
placeholder="Further details">
</quill-editor>
@ -211,7 +211,7 @@
</button>
<mat-menu #actionsMenu="matMenu">
<button (click)="takePhoto.click()" mat-menu-item>Take Photo</button>
<button (click)="takePhoto.click()" mat-menu-item>Add From Library</button>
<button (click)="fileInput.click()" mat-menu-item>Add From Library</button>
</mat-menu>
</div>
<div class="imageList">

228
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<any>();
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<any> = new Subject<any>();
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;
}
}
}
}

6
src/app/pages/admin/Products/ProductList/product-list.component.html

@ -81,7 +81,7 @@
</mat-icon>
</button>
</div>
<a href="javascript:void(0)" class="product-name">{{product.productName}}</a>
<a href="javascript:void(0)" class="product-name">{{product.productName | productNameShort}}</a>
<p class="product-price"><span>{{product.unitPrice | currency:"GHS "}}</span></p>
<p>{{product.productDesc | productDescShort}}</p>
<div style="width: 100%;display: flex;justify-content: center;margin-top:30px;">
@ -95,9 +95,9 @@
</div>
</ng-container>
</div>
<mat-paginator
<!-- <mat-paginator
class="absolute sm:absolute sm:inset-x-0 sm:bottom-0 border-b sm:border-t sm:border-b-0 z-10 bg-gray-50 dark:bg-transparent"
[pageSizeOptions]="[5, 10, 25, 100]" [showFirstLastButtons]="true"></mat-paginator>
[pageSizeOptions]="[5, 10, 25, 100]" [pageSize]="10" [length]="data.length" (page)="pageEvent = $event" [showFirstLastButtons]="true"></mat-paginator> -->
<ng-template *ngIf="data.length == 0">
<div class="p-8 sm:p-16 border-t text-4xl font-semibold tracking-tight text-center">There are no products!
</div>

19
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<any> = new Subject<any>();
// 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.
}

7
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),

32
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<any> {
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();
}));
}
}

2
src/app/pages/admin/Profile/Information/information.component.html

@ -1,5 +1,5 @@
<div class="InfoHold">
<div style="display: flex;justify-content: space-between;max-height: 46px;align-items: center;">
<div style="display: flex;justify-content: space-between;max-height: 46px;align-items: center;margin-bottom: 20px;">
<div>
<h4>
<mat-icon class="icon-size-5" [svgIcon]="'heroicons_solid:information-circle'"></mat-icon> Business

83
src/app/pages/admin/TempShop/temp-shop.component.html

@ -0,0 +1,83 @@
<div class="row" style="display: flex;justify-content:space-between;">
<div class="row" style="display: flex;justify-content:center;width: 100%;">
<h3 mat-dialog-title style="text-align: center;">Business Information</h3>
</div>
<button mat-button [mat-dialog-close]="null" cdkFocusInitial>x</button>
</div>
<mat-dialog-content class="mat-typography">
<div class=" py-2 pl-3 pr-3 sm:py-4 md:pl-4 md:pr-6" style="overflow-y: hidden !important;">
<div style="display: flex;justify-content:space-between;">
<mat-label style="margin-right: 48px;">
<strong class="tileHead">Shop Name </strong>
</mat-label>
<mat-form-field appearance="fill" style="width: 350px;">
<input matInput style="margin-left:10px;" type="text" [(ngModel)]="newBusiness.businessName">
</mat-form-field>
</div>
<div style="display: flex;justify-content:space-between;">
<mat-label style="margin-right: 20px;">
<strong class="tileHead">Shop Category</strong>
</mat-label>
<mat-form-field appearance="fill" style="width: 350px;">
<mat-select [(ngModel)]="selectedCategory">
<mat-option *ngFor="let category of businessCategories" [value]="category">
{{category.categoryName}}
</mat-option>
</mat-select>
</mat-form-field>
</div>
<div style="display: flex;justify-content:space-between;">
<mat-label style="margin-right: 48px;">
<strong class="tileHead">Physical Address </strong>
</mat-label>
<mat-form-field appearance="fill" style="width: 350px;">
<input matInput style="margin-left:10px;" type="text" [(ngModel)]="newBusiness.businessAddress">
</mat-form-field>
</div>
<div style="display: flex;justify-content:space-between;">
<mat-label style="margin-right: 48px;">
<strong class="tileHead">Digital Address </strong>
</mat-label>
<mat-form-field appearance="fill" style="width: 350px;">
<span matPrefix>
<a href="https://ghanapostgps.com/map/" target="blank">
<mat-icon class="icon-size-5" [svgIcon]="'heroicons_solid:information-circle'"
style="color: #0152cc;" matTooltip="visit https://ghanapostgps.com/map/">
</mat-icon> &nbsp;
</a>
</span>
<input matInput style="margin-left:10px;" type="text" [(ngModel)]="newBusiness.businessDigitalAddress">
</mat-form-field>
</div>
<div style="display: flex;justify-content:space-between;">
<mat-label style="margin-right: 48px;">
<strong class="tileHead">Date Of Establishment </strong>
</mat-label>
<mat-form-field appearance="fill" style="width: 350px;">
<input matInput style="margin-left:10px;" type="date" [(ngModel)]="newBusiness.dateOfEst">
</mat-form-field>
</div>
<div style="display: flex;justify-content:space-between;">
<mat-label style="margin-right: 48px;">
<strong class="tileHead">Telephone </strong>
</mat-label>
<mat-form-field appearance="fill" style="width: 350px;">
<input matInput style="margin-left:10px;" type="text" [(ngModel)]="newBusiness.businessContact">
</mat-form-field>
</div>
<div style="display: flex;justify-content:space-between;">
<mat-label style="margin-right: 48px;">
<strong class="tileHead">Email Address </strong>
</mat-label>
<mat-form-field appearance="fill" style="width: 350px;">
<input matInput style="margin-left:10px;" type="text" [(ngModel)]="newBusiness.businessEmail" >
</mat-form-field>
</div>
<div style="display: flex;justify-content:center;width: 100%;">
<button mat-button style="background-color: green;color: white;" (click)="closeDialog()" >Confirm Add</button>
</div>
</div>
</mat-dialog-content>

0
src/app/pages/admin/TempShop/temp-shop.component.scss

12
src/app/pages/admin/Coupons/coupons.component.spec.ts → 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<CouponsComponent>;
describe('TempShopComponent', () => {
let component: TempShopComponent;
let fixture: ComponentFixture<TempShopComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ CouponsComponent ]
declarations: [ TempShopComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(CouponsComponent);
fixture = TestBed.createComponent(TempShopComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

59
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<any> = new Subject<any>();
constructor(public dialogRef: MatDialogRef<TempShopComponent>, 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 => {
});
}
}
}

53
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 { }

8
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;

6
src/app/pages/auth/sign-in/sign-in.component.html

@ -1,7 +1,7 @@
<div class="flex flex-col sm:flex-row items-center md:items-start sm:justify-center md:justify-start flex-auto min-w-0">
<div *ngIf="isScreenSmall"
class="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">
<div class="w-full max-w-80 sm:w-80 mx-auto sm:mx-0">
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">
<div class="w-full max-w-80 sm:w-80 mx-auto sm:mx-0" style="background-color: rgb(6 6 6 / 44%);color: white;">
<div class="w-full sm:w-auto py-8 px-4 sm:p-12 sm:rounded-2xl sm:shadow sm:bg-card">
<!-- Logo -->
<div class="w-20">
@ -27,7 +27,7 @@
<ngx-mat-intl-tel-input [preferredCountries]="['gh', 'ng']" [onlyCountries]="['gh', 'ng']"
[enablePlaceholder]="true" [enableSearch]="false" name="phone">
</ngx-mat-intl-tel-input>
<mat-hint>Standard call, message, or data rates may apply.</mat-hint>
<mat-hint style="color: wheat;">Standard call, message, or data rates may apply.</mat-hint>
</mat-form-field>
<!-- Actions -->
<div class="inline-flex items-end justify-between w-full mt-3.5">

106
src/app/pages/auth/sign-in/signOld.txt

@ -0,0 +1,106 @@
<!-- <div class="flex flex-col sm:flex-row items-center md:items-start sm:justify-center md:justify-start flex-auto min-w-0">
<div *ngIf="isScreenSmall"
class="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">
<div class="w-full max-w-80 sm:w-80 mx-auto sm:mx-0">
<div class="w-full sm:w-auto py-8 px-4 sm:p-12 sm:rounded-2xl sm:shadow sm:bg-card">
<!-- Logo -->
<div class="w-20">
<img src="assets/images/logo.png">
</div>
<!-- Title -->
<div class="mt-8 text-4xl font-extrabold tracking-tight leading-tight">Sign in</div>
<div class="flex items-baseline mt-0.5 font-medium">
<div>Enter phone number of business</div>
</div>
<!-- Alert -->
<teso-alert class="mt-8 -mb-4" *ngIf="showAlert" [appearance]="'outline'" [showIcon]="false"
[type]="alert.type" [@shake]="alert.type === 'error'">
{{alert.message}}
</teso-alert>
<!-- Sign in form -->
<mat-form-field appearance="outline">
<ngx-mat-intl-tel-input [preferredCountries]="['gh', 'ng']" [onlyCountries]="['gh', 'ng']"
[enablePlaceholder]="true" [enableSearch]="false" name="phone">
</ngx-mat-intl-tel-input>
<mat-hint>Standard call, message, or data rates may apply.</mat-hint>
</mat-form-field>
<!-- Actions -->
<div class="inline-flex items-end justify-between w-full mt-3.5">
<mat-checkbox [color]="'primary'">
Remember me
</mat-checkbox>
</div>
<!-- Submit button -->
<button class="teso-mat-button-large w-full mt-6" mat-flat-button [color]="'primary'"
(click)="signIn()">
<span>
Sign in
</span>
<!-- <mat-progress-spinner
*ngIf="signInForm.disabled"
[diameter]="24"
[mode]="'indeterminate'"></mat-progress-spinner> -->
</button>
</div>
</div>
</div>
<div id="welcomeBoard"
class="relative hidden md:flex flex-auto items-center justify-center w-1/2 h-full p-5 lg:px-20 overflow-hidden bg-gray-800 dark:border-l">
<div class="w-full max-w-80 sm:w-80 mx-auto sm:mx-0">
<div class="w-full sm:w-auto py-5 px-4 sm:p-8 sm:rounded-2xl sm:shadow sm:bg-card" style="background-color: #484444c4 !important;color: white !important;">
<!-- Logo -->
<div class="w-30" style="width: 100%;justify-content: center;display: flex;">
<img src="assets/images/logo.png">
</div>
<!-- Title -->
<div class="mt-8 text-4xl font-extrabold tracking-tight leading-tight">Sign in</div>
<div class="flex items-baseline mt-0.5 font-medium">
<div>Enter phone number of business</div>
</div>
<!-- Alert -->
<teso-alert class="mt-8 -mb-4" *ngIf="showAlert" [appearance]="'outline'" [showIcon]="false"
[type]="alert.type" [@shake]="alert.type === 'error'">
{{alert.message}}
</teso-alert>
<!-- Sign in form -->
<mat-form-field appearance="outline">
<ngx-mat-intl-tel-input [preferredCountries]="['gh', 'ng']" [onlyCountries]="['gh', 'ng']"
[enablePlaceholder]="true" [enableSearch]="false" name="phone">
</ngx-mat-intl-tel-input>
<mat-hint style="color: white !important;">Standard call, message, or data rates may apply.</mat-hint>
</mat-form-field>
<!-- Actions -->
<div class="inline-flex items-end justify-between w-full mt-3.5">
<mat-checkbox [color]="'primary'">
Remember me
</mat-checkbox>
</div>
<!-- Submit button -->
<button class="teso-mat-button-large w-full mt-6" mat-flat-button [color]="'primary'"
(click)="signIn()">
<span>
Sign in
</span>
<!-- <mat-progress-spinner
*ngIf="signInForm.disabled"
[diameter]="24"
[mode]="'indeterminate'"></mat-progress-spinner> -->
</button>
</div>
</div>
</div>
</div> -->

20
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;
}
}
}

13
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}`;
}
}

13
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}`;
}
}

17
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 || "";
}
}

20
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;
}
}
}

BIN
src/assets/icons/icon-128_x_128.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
src/assets/icons/icon-144_x_144.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
src/assets/icons/icon-152_x_152.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
src/assets/icons/icon-192_x_192.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
src/assets/icons/icon-384_x_384.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

BIN
src/assets/icons/icon-512_x_512.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

BIN
src/assets/icons/icon-72_x_72.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

BIN
src/assets/icons/icon-96_x_96.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

4
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/"
};

5
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.

BIN
src/favicon.ico

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

5
src/index.html

@ -18,10 +18,12 @@
<link href="assets/fonts/inter/inter.css" rel="stylesheet">
<link href="https://fonts.gstatic.com" rel="preconnect">
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,400;0,500;0,600;1,400&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,400;0,500;0,600;1,400&amp;display=swap" rel="stylesheet">
<!-- Icon fonts -->
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link rel="manifest" href="manifest.webmanifest">
<meta name="theme-color" content="#1976d2">
</head>
<body>
@ -29,6 +31,7 @@
<!-- App root -->
<app-root></app-root>
<noscript>Please enable JavaScript to continue using this application.</noscript>
</body>
<script src="assets/js/jquery.min.js"></script>
<script src="assets/js/slick.min.js"></script>

51
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"
}
]
}
Loading…
Cancel
Save