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", "glob": "_redirects",
"input": "src", "input": "src",
"output": "/" "output": "/"
} },
"src/manifest.webmanifest"
], ],
"stylePreprocessorOptions": { "stylePreprocessorOptions": {
"includePaths": [ "includePaths": [
@ -53,7 +54,9 @@
"src/styles/styles.scss", "src/styles/styles.scss",
"src/styles/tailwind.scss" "src/styles/tailwind.scss"
], ],
"scripts": [] "scripts": [],
"serviceWorker": true,
"ngswConfigPath": "ngsw-config.json"
}, },
"configurations": { "configurations": {
"production": { "production": {
@ -114,7 +117,8 @@
"assets": [ "assets": [
"src/favicon-16x16.png", "src/favicon-16x16.png",
"src/favicon-32x32.png", "src/favicon-32x32.png",
"src/assets" "src/assets",
"src/manifest.webmanifest"
], ],
"styles": [ "styles": [
"src/styles/styles.scss" "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": "13.0.2",
"@angular/platform-browser-dynamic": "13.0.2", "@angular/platform-browser-dynamic": "13.0.2",
"@angular/router": "13.0.2", "@angular/router": "13.0.2",
"@angular/service-worker": "^13.0.2",
"@costlydeveloper/ngx-awesome-popup": "^3.1.3", "@costlydeveloper/ngx-awesome-popup": "^3.1.3",
"@fullcalendar/angular": "4.4.5-beta", "@fullcalendar/angular": "4.4.5-beta",
"@fullcalendar/core": "4.4.2", "@fullcalendar/core": "4.4.2",
@ -77,7 +78,9 @@
"@types/node": "12.20.21", "@types/node": "12.20.21",
"@typescript-eslint/eslint-plugin": "4.30.0", "@typescript-eslint/eslint-plugin": "4.30.0",
"@typescript-eslint/parser": "4.30.0", "@typescript-eslint/parser": "4.30.0",
"@webpixels/css": "^1.1.92",
"autoprefixer": "10.3.3", "autoprefixer": "10.3.3",
"bootstrap": "^5.1.3",
"chroma-js": "2.1.2", "chroma-js": "2.1.2",
"eslint": "7.32.0", "eslint": "7.32.0",
"eslint-plugin-import": "2.24.2", "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": { "node_modules/@angular-devkit/build-angular/node_modules/autoprefixer": {
"version": "10.4.2", "version": "10.4.2",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.2.tgz", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.2.tgz",
@ -1408,6 +1397,24 @@
"rxjs": "^6.5.3 || ^7.4.0" "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": { "node_modules/@assemblyscript/loader": {
"version": "0.10.1", "version": "0.10.1",
"resolved": "https://registry.npmjs.org/@assemblyscript/loader/-/loader-0.10.1.tgz", "resolved": "https://registry.npmjs.org/@assemblyscript/loader/-/loader-0.10.1.tgz",
@ -4488,6 +4495,16 @@
"@xtuc/long": "4.2.2" "@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": { "node_modules/@xtuc/ieee754": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
@ -5178,6 +5195,19 @@
"integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=",
"dev": true "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": { "node_modules/brace-expansion": {
"version": "1.1.11", "version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@ -12269,9 +12299,12 @@
} }
}, },
"node_modules/prismjs": { "node_modules/prismjs": {
"version": "1.25.0", "version": "1.27.0",
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.25.0.tgz", "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.27.0.tgz",
"integrity": "sha512-WCjJHl1KEWbnkQom1+SzftbtXMKQoezOCYs5rECqMN+jP+apI7ftoflyqigqzopSO3hMhTEb0mFClA8lkolgEg==" "integrity": "sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==",
"engines": {
"node": ">=6"
}
}, },
"node_modules/process-nextick-args": { "node_modules/process-nextick-args": {
"version": "2.0.1", "version": "2.0.1",
@ -15571,14 +15604,6 @@
"webpack-subresource-integrity": "5.1.0" "webpack-subresource-integrity": "5.1.0"
}, },
"dependencies": { "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": { "autoprefixer": {
"version": "10.4.2", "version": "10.4.2",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.2.tgz", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.2.tgz",
@ -15621,8 +15646,7 @@
"version": "6.0.3", "version": "6.0.3",
"resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz", "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz",
"integrity": "sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==", "integrity": "sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==",
"dev": true, "dev": true
"requires": {}
}, },
"postcss": { "postcss": {
"version": "8.4.5", "version": "8.4.5",
@ -15711,8 +15735,7 @@
"version": "3.0.3", "version": "3.0.3",
"resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-3.0.3.tgz", "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-3.0.3.tgz",
"integrity": "sha512-rPPZRLPmEKgLk/KlXMqRaNkYTUpE7YC+bOIQFN5xcu1Vp11Y4faIXv6/Jpft6FMnl6YRxZqDZG0qQOW80stzxQ==", "integrity": "sha512-rPPZRLPmEKgLk/KlXMqRaNkYTUpE7YC+bOIQFN5xcu1Vp11Y4faIXv6/Jpft6FMnl6YRxZqDZG0qQOW80stzxQ==",
"dev": true, "dev": true
"requires": {}
}, },
"postcss-image-set-function": { "postcss-image-set-function": {
"version": "4.0.6", "version": "4.0.6",
@ -15736,15 +15759,13 @@
"version": "5.0.4", "version": "5.0.4",
"resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-5.0.4.tgz", "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-5.0.4.tgz",
"integrity": "sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==", "integrity": "sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==",
"dev": true, "dev": true
"requires": {}
}, },
"postcss-overflow-shorthand": { "postcss-overflow-shorthand": {
"version": "3.0.3", "version": "3.0.3",
"resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-3.0.3.tgz", "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-3.0.3.tgz",
"integrity": "sha512-CxZwoWup9KXzQeeIxtgOciQ00tDtnylYIlJBBODqkgS/PU2jISuWOL/mYLHmZb9ZhZiCaNKsCRiLp22dZUtNsg==", "integrity": "sha512-CxZwoWup9KXzQeeIxtgOciQ00tDtnylYIlJBBODqkgS/PU2jISuWOL/mYLHmZb9ZhZiCaNKsCRiLp22dZUtNsg==",
"dev": true, "dev": true
"requires": {}
}, },
"postcss-place": { "postcss-place": {
"version": "7.0.4", "version": "7.0.4",
@ -16291,6 +16312,14 @@
"tslib": "^2.3.0" "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": { "@assemblyscript/loader": {
"version": "0.10.1", "version": "0.10.1",
"resolved": "https://registry.npmjs.org/@assemblyscript/loader/-/loader-0.10.1.tgz", "resolved": "https://registry.npmjs.org/@assemblyscript/loader/-/loader-0.10.1.tgz",
@ -17580,32 +17609,27 @@
"@fullcalendar/daygrid": { "@fullcalendar/daygrid": {
"version": "4.4.2", "version": "4.4.2",
"resolved": "https://registry.npmjs.org/@fullcalendar/daygrid/-/daygrid-4.4.2.tgz", "resolved": "https://registry.npmjs.org/@fullcalendar/daygrid/-/daygrid-4.4.2.tgz",
"integrity": "sha512-axjfMhxEXHShV3r2TZjf+2niJ1C6LdAxkHKmg7mVq4jXtUQHOldU5XsjV0v2lUAt1urJBFi2zajfK8798ukL3Q==", "integrity": "sha512-axjfMhxEXHShV3r2TZjf+2niJ1C6LdAxkHKmg7mVq4jXtUQHOldU5XsjV0v2lUAt1urJBFi2zajfK8798ukL3Q=="
"requires": {}
}, },
"@fullcalendar/interaction": { "@fullcalendar/interaction": {
"version": "4.4.2", "version": "4.4.2",
"resolved": "https://registry.npmjs.org/@fullcalendar/interaction/-/interaction-4.4.2.tgz", "resolved": "https://registry.npmjs.org/@fullcalendar/interaction/-/interaction-4.4.2.tgz",
"integrity": "sha512-3ItpGFnxcYQT4NClqhq93QTQwOI8x3mlMf5M4DgK5avVaSzpv9g8p+opqeotK2yzpFeINps06cuQyB1h7vcv1Q==", "integrity": "sha512-3ItpGFnxcYQT4NClqhq93QTQwOI8x3mlMf5M4DgK5avVaSzpv9g8p+opqeotK2yzpFeINps06cuQyB1h7vcv1Q=="
"requires": {}
}, },
"@fullcalendar/list": { "@fullcalendar/list": {
"version": "4.4.2", "version": "4.4.2",
"resolved": "https://registry.npmjs.org/@fullcalendar/list/-/list-4.4.2.tgz", "resolved": "https://registry.npmjs.org/@fullcalendar/list/-/list-4.4.2.tgz",
"integrity": "sha512-buhfd0w0PavH3EVZ6DR6kvjb+wPDe16XEpNcPkTpvIxnAziwGBvcUeHUBd9KvtEhOcvs9sAKoYKbU4xwHFK0Wg==", "integrity": "sha512-buhfd0w0PavH3EVZ6DR6kvjb+wPDe16XEpNcPkTpvIxnAziwGBvcUeHUBd9KvtEhOcvs9sAKoYKbU4xwHFK0Wg=="
"requires": {}
}, },
"@fullcalendar/moment": { "@fullcalendar/moment": {
"version": "4.4.2", "version": "4.4.2",
"resolved": "https://registry.npmjs.org/@fullcalendar/moment/-/moment-4.4.2.tgz", "resolved": "https://registry.npmjs.org/@fullcalendar/moment/-/moment-4.4.2.tgz",
"integrity": "sha512-PBrjxxDEG3RO+8SOA3a1YA7yoGI3bgnltiGY3ehOtJwFIMsUQDSSr5aMoWyRpz7MXgp2YOQY5rzMEIp2A8eK9w==", "integrity": "sha512-PBrjxxDEG3RO+8SOA3a1YA7yoGI3bgnltiGY3ehOtJwFIMsUQDSSr5aMoWyRpz7MXgp2YOQY5rzMEIp2A8eK9w=="
"requires": {}
}, },
"@fullcalendar/rrule": { "@fullcalendar/rrule": {
"version": "4.4.2", "version": "4.4.2",
"resolved": "https://registry.npmjs.org/@fullcalendar/rrule/-/rrule-4.4.2.tgz", "resolved": "https://registry.npmjs.org/@fullcalendar/rrule/-/rrule-4.4.2.tgz",
"integrity": "sha512-pUKHFp62SZbW9X3vvxc8IMnoWpQ6Nt2IBwwPFPAWmebCnUhyDfMf3tpKaV9slUYvW0Cch4Y58tv0EySP27Q2jg==", "integrity": "sha512-pUKHFp62SZbW9X3vvxc8IMnoWpQ6Nt2IBwwPFPAWmebCnUhyDfMf3tpKaV9slUYvW0Cch4Y58tv0EySP27Q2jg=="
"requires": {}
}, },
"@fullcalendar/timegrid": { "@fullcalendar/timegrid": {
"version": "4.4.2", "version": "4.4.2",
@ -17703,8 +17727,7 @@
"version": "13.2.2", "version": "13.2.2",
"resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-13.2.2.tgz", "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-13.2.2.tgz",
"integrity": "sha512-BkykqIIJgSllNT6f+8IoZtJaI8uKdq62LteoWMMtvAxXErqRLN6CcDxPWjP8tfeEKeCYZ41ueZwNK5V05SsCIg==", "integrity": "sha512-BkykqIIJgSllNT6f+8IoZtJaI8uKdq62LteoWMMtvAxXErqRLN6CcDxPWjP8tfeEKeCYZ41ueZwNK5V05SsCIg==",
"dev": true, "dev": true
"requires": {}
}, },
"@nodelib/fs.scandir": { "@nodelib/fs.scandir": {
"version": "2.1.5", "version": "2.1.5",
@ -17957,15 +17980,13 @@
"version": "0.2.1", "version": "0.2.1",
"resolved": "https://registry.npmjs.org/@tailwindcss/aspect-ratio/-/aspect-ratio-0.2.1.tgz", "resolved": "https://registry.npmjs.org/@tailwindcss/aspect-ratio/-/aspect-ratio-0.2.1.tgz",
"integrity": "sha512-aDFi80aHQ3JM3symJ5iKU70lm151ugIGFCI0yRZGpyjgQSDS+Fbe93QwypC1tCEllQE8p0S7TUu20ih1b9IKLA==", "integrity": "sha512-aDFi80aHQ3JM3symJ5iKU70lm151ugIGFCI0yRZGpyjgQSDS+Fbe93QwypC1tCEllQE8p0S7TUu20ih1b9IKLA==",
"dev": true, "dev": true
"requires": {}
}, },
"@tailwindcss/line-clamp": { "@tailwindcss/line-clamp": {
"version": "0.2.1", "version": "0.2.1",
"resolved": "https://registry.npmjs.org/@tailwindcss/line-clamp/-/line-clamp-0.2.1.tgz", "resolved": "https://registry.npmjs.org/@tailwindcss/line-clamp/-/line-clamp-0.2.1.tgz",
"integrity": "sha512-Hq2KJY1+T2v7jw/mnT3mnC7CKbp5kj1XTqzSb2xbEt1j+JkxIR6N3ijsN/WevZtsKJfVE1KOejA/3IRKuhZEsQ==", "integrity": "sha512-Hq2KJY1+T2v7jw/mnT3mnC7CKbp5kj1XTqzSb2xbEt1j+JkxIR6N3ijsN/WevZtsKJfVE1KOejA/3IRKuhZEsQ==",
"dev": true, "dev": true
"requires": {}
}, },
"@tailwindcss/typography": { "@tailwindcss/typography": {
"version": "0.4.1", "version": "0.4.1",
@ -18568,6 +18589,12 @@
"@xtuc/long": "4.2.2" "@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": { "@xtuc/ieee754": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
@ -18618,15 +18645,13 @@
"version": "1.8.0", "version": "1.8.0",
"resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz",
"integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==",
"dev": true, "dev": true
"requires": {}
}, },
"acorn-jsx": { "acorn-jsx": {
"version": "5.3.2", "version": "5.3.2",
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
"integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
"dev": true, "dev": true
"requires": {}
}, },
"acorn-node": { "acorn-node": {
"version": "1.8.2", "version": "1.8.2",
@ -18723,8 +18748,7 @@
"version": "3.5.2", "version": "3.5.2",
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
"dev": true, "dev": true
"requires": {}
}, },
"ansi-colors": { "ansi-colors": {
"version": "4.1.1", "version": "4.1.1",
@ -19097,6 +19121,12 @@
"integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=",
"dev": true "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": { "brace-expansion": {
"version": "1.1.11", "version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@ -19281,8 +19311,7 @@
"version": "5.2.2", "version": "5.2.2",
"resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-5.2.2.tgz", "resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-5.2.2.tgz",
"integrity": "sha512-g38K9Cm5WRwlaH6g03B9OEz/0qRizI+2I7n+Gz+L5DxXJAPAiWQvwlYNm1V1jkdpUv95bOe/ASm2vfi/G560jQ==", "integrity": "sha512-g38K9Cm5WRwlaH6g03B9OEz/0qRizI+2I7n+Gz+L5DxXJAPAiWQvwlYNm1V1jkdpUv95bOe/ASm2vfi/G560jQ==",
"dev": true, "dev": true
"requires": {}
}, },
"clean-stack": { "clean-stack": {
"version": "2.2.0", "version": "2.2.0",
@ -20910,8 +20939,7 @@
"version": "1.2.3", "version": "1.2.3",
"resolved": "https://registry.npmjs.org/eslint-plugin-prefer-arrow/-/eslint-plugin-prefer-arrow-1.2.3.tgz", "resolved": "https://registry.npmjs.org/eslint-plugin-prefer-arrow/-/eslint-plugin-prefer-arrow-1.2.3.tgz",
"integrity": "sha512-J9I5PKCOJretVuiZRGvPQxCbllxGAV/viI20JO3LYblAodofBxyMnZAJ+WGeClHgANnSJberTNoFWWjrWKBuXQ==", "integrity": "sha512-J9I5PKCOJretVuiZRGvPQxCbllxGAV/viI20JO3LYblAodofBxyMnZAJ+WGeClHgANnSJberTNoFWWjrWKBuXQ==",
"dev": true, "dev": true
"requires": {}
}, },
"eslint-scope": { "eslint-scope": {
"version": "5.1.1", "version": "5.1.1",
@ -21750,8 +21778,7 @@
"version": "5.1.0", "version": "5.1.0",
"resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz",
"integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==",
"dev": true, "dev": true
"requires": {}
}, },
"ieee754": { "ieee754": {
"version": "1.2.1", "version": "1.2.1",
@ -22564,8 +22591,7 @@
"version": "1.7.0", "version": "1.7.0",
"resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-1.7.0.tgz", "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-1.7.0.tgz",
"integrity": "sha512-pzum1TL7j90DTE86eFt48/s12hqwQuiD+e5aXx2Dc9wDEn2LfGq6RoAxEZZjFiN0RDSCOnosEKRZWxbQ+iMpQQ==", "integrity": "sha512-pzum1TL7j90DTE86eFt48/s12hqwQuiD+e5aXx2Dc9wDEn2LfGq6RoAxEZZjFiN0RDSCOnosEKRZWxbQ+iMpQQ==",
"dev": true, "dev": true
"requires": {}
}, },
"karma-source-map-support": { "karma-source-map-support": {
"version": "1.4.0", "version": "1.4.0",
@ -24181,8 +24207,7 @@
"version": "8.0.0", "version": "8.0.0",
"resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-8.0.0.tgz", "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-8.0.0.tgz",
"integrity": "sha512-FvO2GzMUaTN0t1fBULDeIvxr5IvbDXcIatt6pnJghc736nqNgsGao5NT+5+WVLAQiTt6Cb3YUms0jiPaXhL//g==", "integrity": "sha512-FvO2GzMUaTN0t1fBULDeIvxr5IvbDXcIatt6pnJghc736nqNgsGao5NT+5+WVLAQiTt6Cb3YUms0jiPaXhL//g==",
"dev": true, "dev": true
"requires": {}
}, },
"postcss-custom-selectors": { "postcss-custom-selectors": {
"version": "6.0.0", "version": "6.0.0",
@ -24197,8 +24222,7 @@
"version": "5.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz", "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz",
"integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==", "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==",
"dev": true, "dev": true
"requires": {}
}, },
"postcss-import": { "postcss-import": {
"version": "14.0.2", "version": "14.0.2",
@ -24215,8 +24239,7 @@
"version": "4.0.1", "version": "4.0.1",
"resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-4.0.1.tgz", "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-4.0.1.tgz",
"integrity": "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==", "integrity": "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==",
"dev": true, "dev": true
"requires": {}
}, },
"postcss-js": { "postcss-js": {
"version": "3.0.3", "version": "3.0.3",
@ -24269,15 +24292,13 @@
"version": "5.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-5.0.0.tgz", "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-5.0.0.tgz",
"integrity": "sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==", "integrity": "sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==",
"dev": true, "dev": true
"requires": {}
}, },
"postcss-modules-extract-imports": { "postcss-modules-extract-imports": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz",
"integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==",
"dev": true, "dev": true
"requires": {}
}, },
"postcss-modules-local-by-default": { "postcss-modules-local-by-default": {
"version": "4.0.0", "version": "4.0.0",
@ -24330,15 +24351,13 @@
"version": "3.0.4", "version": "3.0.4",
"resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz", "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz",
"integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==", "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==",
"dev": true, "dev": true
"requires": {}
}, },
"postcss-replace-overflow-wrap": { "postcss-replace-overflow-wrap": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz", "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz",
"integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==", "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==",
"dev": true, "dev": true
"requires": {}
}, },
"postcss-selector-not": { "postcss-selector-not": {
"version": "5.0.0", "version": "5.0.0",
@ -24384,9 +24403,9 @@
"dev": true "dev": true
}, },
"prismjs": { "prismjs": {
"version": "1.25.0", "version": "1.27.0",
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.25.0.tgz", "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.27.0.tgz",
"integrity": "sha512-WCjJHl1KEWbnkQom1+SzftbtXMKQoezOCYs5rECqMN+jP+apI7ftoflyqigqzopSO3hMhTEb0mFClA8lkolgEg==" "integrity": "sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA=="
}, },
"process-nextick-args": { "process-nextick-args": {
"version": "2.0.1", "version": "2.0.1",
@ -25051,8 +25070,7 @@
"version": "0.0.2", "version": "0.0.2",
"resolved": "https://registry.npmjs.org/rxjs-for-await/-/rxjs-for-await-0.0.2.tgz", "resolved": "https://registry.npmjs.org/rxjs-for-await/-/rxjs-for-await-0.0.2.tgz",
"integrity": "sha512-IJ8R/ZCFMHOcDIqoABs82jal00VrZx8Xkgfe7TOKoaRPAW5nH/VFlG23bXpeGdrmtqI9UobFPgUKgCuFc7Lncw==", "integrity": "sha512-IJ8R/ZCFMHOcDIqoABs82jal00VrZx8Xkgfe7TOKoaRPAW5nH/VFlG23bXpeGdrmtqI9UobFPgUKgCuFc7Lncw==",
"dev": true, "dev": true
"requires": {}
}, },
"safe-buffer": { "safe-buffer": {
"version": "5.1.2", "version": "5.1.2",
@ -26601,8 +26619,7 @@
"version": "8.4.2", "version": "8.4.2",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.4.2.tgz", "resolved": "https://registry.npmjs.org/ws/-/ws-8.4.2.tgz",
"integrity": "sha512-Kbk4Nxyq7/ZWqr/tarI9yIt/+iNNFOjBXEWgTb4ydaNHBNGgvf2QHbS9fdfsndfjFlFwEd4Al+mw83YkaD10ZA==", "integrity": "sha512-Kbk4Nxyq7/ZWqr/tarI9yIt/+iNNFOjBXEWgTb4ydaNHBNGgvf2QHbS9fdfsndfjFlFwEd4Al+mw83YkaD10ZA==",
"dev": true, "dev": true
"requires": {}
} }
} }
}, },
@ -26742,8 +26759,7 @@
"version": "8.2.3", "version": "8.2.3",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz",
"integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==",
"dev": true, "dev": true
"requires": {}
}, },
"xtend": { "xtend": {
"version": "4.0.2", "version": "4.0.2",

3
package.json

@ -24,6 +24,7 @@
"@angular/platform-browser": "13.0.2", "@angular/platform-browser": "13.0.2",
"@angular/platform-browser-dynamic": "13.0.2", "@angular/platform-browser-dynamic": "13.0.2",
"@angular/router": "13.0.2", "@angular/router": "13.0.2",
"@angular/service-worker": "^13.0.2",
"@costlydeveloper/ngx-awesome-popup": "^3.1.3", "@costlydeveloper/ngx-awesome-popup": "^3.1.3",
"@fullcalendar/angular": "4.4.5-beta", "@fullcalendar/angular": "4.4.5-beta",
"@fullcalendar/core": "4.4.2", "@fullcalendar/core": "4.4.2",
@ -81,7 +82,9 @@
"@types/node": "12.20.21", "@types/node": "12.20.21",
"@typescript-eslint/eslint-plugin": "4.30.0", "@typescript-eslint/eslint-plugin": "4.30.0",
"@typescript-eslint/parser": "4.30.0", "@typescript-eslint/parser": "4.30.0",
"@webpixels/css": "^1.1.92",
"autoprefixer": "10.3.3", "autoprefixer": "10.3.3",
"bootstrap": "^5.1.3",
"chroma-js": "2.1.2", "chroma-js": "2.1.2",
"eslint": "7.32.0", "eslint": "7.32.0",
"eslint-plugin-import": "2.24.2", "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({ @Component({
selector : 'app-root', selector: 'app-root',
templateUrl: './app.component.html', templateUrl: './app.component.html',
styleUrls : ['./app.component.scss'] styleUrls: ['./app.component.scss'],
encapsulation: ViewEncapsulation.None
}) })
export class AppComponent export class AppComponent implements OnInit {
{ hasUpdate = false;
/**
* Constructor constructor(
*/ private swUpdate: SwUpdate,
constructor() ) {
{
} }
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 { NgxAwesomePopupModule,ConfirmBoxConfigModule} from '@costlydeveloper/ngx-awesome-popup';
import { ProductDescriptionShort } from './pipes/productDescriptionShort.pipe'; import { ProductDescriptionShort } from './pipes/productDescriptionShort.pipe';
import { CouponWorthPipe } from './pipes/coupon-worth.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 = { const routerConfig: ExtraOptions = {
preloadingStrategy : PreloadAllModules, preloadingStrategy : PreloadAllModules,
@ -25,6 +29,7 @@ const routerConfig: ExtraOptions = {
@NgModule({ @NgModule({
declarations: [ declarations: [
AppComponent, AppComponent,
GoldTransactionComponent,
], ],
imports : [ imports : [
@ -37,7 +42,7 @@ const routerConfig: ExtraOptions = {
tesoModule, tesoModule,
tesoConfigModule.forRoot(appConfig), tesoConfigModule.forRoot(appConfig),
tesoMockApiModule.forRoot(mockApiServices), tesoMockApiModule.forRoot(mockApiServices),
TempShopModule,
// Core module of your application // Core module of your application
CoreModule, CoreModule,
@ -45,7 +50,13 @@ const routerConfig: ExtraOptions = {
LayoutModule, LayoutModule,
// 3rd party modules that require global configuration via forRoot // 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: [ providers: [
{ provide: LocationStrategy, useClass: PathLocationStrategy }, { provide: LocationStrategy, useClass: PathLocationStrategy },

8
src/app/core/auth/auth.interceptor.ts

@ -36,10 +36,10 @@ export class AuthInterceptor implements HttpInterceptor {
newReq = req.clone({ newReq = req.clone({
headers: req.headers.set('Authorization', 'Bearer ' + this._authService.relevantToken) headers: req.headers.set('Authorization', 'Bearer ' + this._authService.relevantToken)
.append('Accept', '*/*') .append('Accept', '*/*')
.append('Content-Type', 'application/json; charset=utf-8;') // .append('Content-Type', 'application/json; charset=utf-8;')
.append("Access-Control-Allow-Origin", "*") // .append("Access-Control-Allow-Origin", "*")
.append("Access-Control-Allow-Methods", "DELETE, POST, GET, OPTIONS") // .append("Access-Control-Allow-Methods", "DELETE, POST, GET, OPTIONS")
.append("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Requested-With") // .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'; import { UserService } from 'app/core/user/user.service';
@Injectable() @Injectable()
export class AuthService export class AuthService {
{
private _authenticated: boolean = false; private _authenticated: boolean = false;
/** /**
@ -16,8 +15,7 @@ export class AuthService
constructor( constructor(
private _httpClient: HttpClient, private _httpClient: HttpClient,
private _userService: UserService private _userService: UserService
) ) {
{
} }
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
@ -27,23 +25,19 @@ export class AuthService
/** /**
* Setter & getter for access token * Setter & getter for access token
*/ */
set accessToken(token: string) set accessToken(token: string) {
{
localStorage.setItem('accessToken', token); localStorage.setItem('accessToken', token);
} }
get accessToken(): string get accessToken(): string {
{
return localStorage.getItem('accessToken') ?? ''; return localStorage.getItem('accessToken') ?? '';
} }
set relevantToken(token: string) set relevantToken(token: string) {
{
localStorage.setItem('relevantToken', token); localStorage.setItem('relevantToken', token);
} }
get relevantToken(): string get relevantToken(): string {
{
return localStorage.getItem('relevantToken') ?? ''; return localStorage.getItem('relevantToken') ?? '';
} }
@ -56,8 +50,7 @@ export class AuthService
* *
* @param email * @param email
*/ */
forgotPassword(email: string): Observable<any> forgotPassword(email: string): Observable<any> {
{
return this._httpClient.post('api/auth/forgot-password', email); return this._httpClient.post('api/auth/forgot-password', email);
} }
@ -66,8 +59,7 @@ export class AuthService
* *
* @param password * @param password
*/ */
resetPassword(password: string): Observable<any> resetPassword(password: string): Observable<any> {
{
return this._httpClient.post('api/auth/reset-password', password); return this._httpClient.post('api/auth/reset-password', password);
} }
@ -76,11 +68,9 @@ export class AuthService
* *
* @param credentials * @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 // Throw error, if the user is already logged in
if ( this._authenticated ) if (this._authenticated) {
{
return throwError('User is already logged in.'); return throwError('User is already logged in.');
} }
@ -89,7 +79,7 @@ export class AuthService
// Store the access token in the local storage // Store the access token in the local storage
this.accessToken = response.accessToken; 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 // Set the authenticated flag to true
this._authenticated = true; this._authenticated = true;
@ -105,8 +95,7 @@ this.relevantToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOiIyMjY0ODQyN
/** /**
* Sign in using the access token * Sign in using the access token
*/ */
signInUsingToken(): Observable<any> signInUsingToken(): Observable<any> {
{
// Renew token // Renew token
return this._httpClient.post('api/auth/refresh-access-token', { return this._httpClient.post('api/auth/refresh-access-token', {
accessToken: this.accessToken accessToken: this.accessToken
@ -136,8 +125,7 @@ this.relevantToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOiIyMjY0ODQyN
/** /**
* Sign out * Sign out
*/ */
signOut(): Observable<any> signOut(): Observable<any> {
{
// Remove the access token from the local storage // Remove the access token from the local storage
localStorage.removeItem('accessToken'); localStorage.removeItem('accessToken');
@ -153,8 +141,7 @@ this.relevantToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOiIyMjY0ODQyN
* *
* @param user * @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); return this._httpClient.post('api/auth/sign-up', user);
} }
@ -163,15 +150,14 @@ this.relevantToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOiIyMjY0ODQyN
* *
* @param credentials * @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); return this._httpClient.post('api/auth/unlock-session', credentials);
} }
/** /**
* Check the authentication status * Check the authentication status
*/ */
check(): Observable<boolean> { check(): Observable<boolean> {
// Check if the user is logged in // Check if the user is logged in
if (this._authenticated) { if (this._authenticated) {
return of(true); return of(true);

1
src/app/models/generalModel.ts

@ -2,4 +2,5 @@ export interface ProductUpload {
highlight: boolean; highlight: boolean;
file: File; file: File;
imageSRC:string; 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="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"> <div class="overflow-x-auto mx-6 py-10">
<table class="w-full bg-transparent" mat-table matSort [dataSource]="couponsDataSource" [trackBy]="trackByFn" <table class="w-full bg-transparent" mat-table matSort [dataSource]="couponsDataSource" [trackBy]="trackByFn"
#couponsTable> #couponsTable *ngIf="!isScreenSmall">
<!-- Transaction ID --> <!-- Transaction ID -->
<ng-container matColumnDef="product"> <ng-container matColumnDef="product">
<th mat-header-cell mat-sort-header *matHeaderCellDef> <th mat-header-cell mat-sort-header *matHeaderCellDef>
@ -9,7 +9,7 @@
</th> </th>
<td mat-cell *matCellDef="let transaction"> <td mat-cell *matCellDef="let transaction">
<span class="pr-6 whitespace-wrap"> <span class="pr-6 whitespace-wrap">
{{transaction.targetProduct}} {{transaction.targetProduct |couponProductShort}}
</span> </span>
</td> </td>
</ng-container> </ng-container>
@ -51,10 +51,12 @@
Range Range
</th> </th>
<td mat-cell *matCellDef="let transaction"> <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')"> <span class="pr-6 whitespace-wrap" style="display: flex;justify-content: center;font-weight: bold;"
{{transaction.lower}} - {{transaction.upper}} *ngIf="!transaction.type.includes('FREEBIES')">
{{transaction.lower}} % - {{transaction.upper}} %
</span> </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 100% OFF
</span> </span>
</td> </td>
@ -96,15 +98,15 @@
</th> </th>
<td mat-cell *matCellDef="let transaction"> <td mat-cell *matCellDef="let transaction">
<div style="display: flex;"> <div style="display: flex;">
<button class=" sm:inline-flex" mat-flat-button> <button class=" sm:inline-flex" mat-flat-button (click)="showDetails(transaction)">
<a href="#" style="color: #0152cc;margin-right:10px;">Show Details</a> <a href="javascript:void(0)" style="color: #0152cc;margin-right:10px;">Show Details</a>
</button> </button>
<button class=" sm:inline-flex" mat-flat-button [matMenuTriggerFor]="actionsMenu"> <button class=" sm:inline-flex" mat-flat-button [matMenuTriggerFor]="actionsMenu">
<mat-icon class="icon-size-5" matPrefix [svgIcon]="'heroicons_solid:dots-horizontal'"> <mat-icon class="icon-size-5" matPrefix [svgIcon]="'heroicons_solid:dots-horizontal'">
</mat-icon> </mat-icon>
</button> </button>
<mat-menu #actionsMenu="matMenu"> <mat-menu #actionsMenu="matMenu">
<button mat-menu-item>Cancel</button> <button mat-menu-item (click)="cancel(transaction)">Cancel</button>
</mat-menu> </mat-menu>
</div> </div>
</td> </td>
@ -113,7 +115,7 @@
<ng-container matColumnDef="recentOrdersTableFooter"> <ng-container matColumnDef="recentOrdersTableFooter">
<td class="py-6 px-0 border-0" mat-footer-cell *matFooterCellDef colspan="6"> <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" <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> </td>
</ng-container> </ng-container>
@ -121,5 +123,65 @@
<tr class="coupons-row h-14" mat-row *matRowDef="let row; columns: couponsTableColumns;"></tr> <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> <tr class="h-16 border-0" mat-footer-row *matFooterRowDef="['recentOrdersTableFooter']"></tr>
</table> </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>
</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 { MatSort } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table'; 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({ @Component({
selector: 'activecoupons', selector: 'activecoupons',
@ -9,15 +18,56 @@ import { MatTableDataSource } from '@angular/material/table';
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
exportAs: 'activecoupons' exportAs: 'activecoupons'
}) })
export class ActiveCouponsComponent implements OnInit { export class ActiveCouponsComponent implements OnInit, AfterViewInit {
@Input() couponsDataSource: MatTableDataSource<any>; @Input() couponsDataSource: MatTableDataSource<any>;
couponsTableColumns: string[] = ['product', 'quantity', 'type', 'condition', 'range', 'claimed', 'status', 'expiration', 'actions']; couponsTableColumns: string[] = ['product', 'quantity', 'type', 'condition', 'range', 'claimed', 'status', 'expiration', 'actions'];
couponsTableColumnsMobile: string[] = ['product', 'status'];
@ViewChild('couponsTable', { read: MatSort }) couponsTableMatSort: MatSort; @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 { 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 { trackByFn(index: number, item: any): any {
return item.id || index; 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>
<div class="column"> <div class="column">
<mat-form-field appearance="fill" style="min-width: 200px;"> <mat-form-field appearance="fill" style="min-width: 200px;">
<mat-select [(ngModel)]="selectedType"> <mat-select [(ngModel)]="newCoupon.type">
<mat-option *ngFor="let type of couponTypes" [value]="type"> <mat-option *ngFor="let type of couponTypes" [value]="type.typeCode">
{{type.typeName}} {{type.typeName}}
</mat-option> </mat-option>
</mat-select> </mat-select>
@ -76,7 +76,8 @@
</mat-label> </mat-label>
</div> </div>
<div class="column"> <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> </div>
<div class="columns" style="display: flex;align-items: center"> <div class="columns" style="display: flex;align-items: center">
@ -87,51 +88,76 @@
</div> </div>
<div class="column"> <div class="column">
<mat-form-field appearance="fill" style="min-width: 200px;"> <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> </mat-form-field>
</div> </div>
</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;"> <mat-label style="margin-right: 48px;">
<strong class="tileHead">Percentage Off </strong> <strong class="tileHead">Percentage Off </strong>
</mat-label> </mat-label>
<mat-form-field appearance="fill" style="margin-right:10px;"> <mat-form-field appearance="fill" style="margin-right:10px;">
<span matPrefix>From &nbsp;</span> <span matPrefix>From &nbsp;</span>
<input matInput type="number"> <input matInput type="number" [(ngModel)]="newCoupon.lower">
<span matSuffix>% &nbsp;</span> <span matSuffix>% &nbsp;</span>
</mat-form-field> </mat-form-field>
<mat-form-field appearance="fill" style="margin-left:10px;"> <mat-form-field appearance="fill" style="margin-left:10px;">
<span matPrefix>To &nbsp;</span> <span matPrefix>To &nbsp;</span>
<input matInput type="number"> <input matInput type="number" [(ngModel)]="newCoupon.upper">
<span matSuffix>% &nbsp;</span> <span matSuffix>% &nbsp;</span>
</mat-form-field> </mat-form-field>
</div> </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;"> <mat-label style="margin-right: 48px;">
<strong class="tileHead">Percentage Off </strong> <strong class="tileHead">Percentage Off </strong>
</mat-label> </mat-label>
<div style="display: flex;align-items: baseline"> <div style="display: flex;align-items: baseline">
<mat-form-field appearance="outline" style="margin-right:10px;"> <mat-form-field appearance="outline" style="margin-right:10px;">
<span matPrefix>From &nbsp;</span> <span matPrefix>From &nbsp;</span>
<input matInput type="number" [(ngModel)]="fromWorth"> <input matInput type="number" [(ngModel)]="newCoupon.lower">
<span matSuffix>% &nbsp;</span> <span matSuffix>% &nbsp;</span>
</mat-form-field> </mat-form-field>
</div> </div>
<div style="display: flex;align-items: baseline"> <div style="display: flex;align-items: baseline">
<mat-form-field appearance="outline" style="margin-left:10px;"> <mat-form-field appearance="outline" style="margin-left:10px;">
<span matPrefix>To &nbsp;</span> <span matPrefix>To &nbsp;</span>
<input matInput type="number" [(ngModel)]="toWorth"> <input matInput type="number" [(ngModel)]="newCoupon.upper">
<span matSuffix>% &nbsp;</span> <span matSuffix>% &nbsp;</span>
</mat-form-field> </mat-form-field>
</div> </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">
FREE
</div>
</div>
<div style="display: flex;align-items: baseline"> <div style="display: flex;align-items: baseline">
<mat-label style="margin-right: 48px;"> <mat-label style="margin-right: 48px;">
<strong class="tileHead">Coupon Worth </strong> <strong class="tileHead">Coupon Worth </strong>
</mat-label> </mat-label>
<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> </mat-label>
</div> </div>
<div style="display: flex;align-items: baseline;margin-top: 20px;" *ngIf="!isScreenSmall"> <div style="display: flex;align-items: baseline;margin-top: 20px;" *ngIf="!isScreenSmall">
@ -139,31 +165,32 @@
<strong class="tileHead">Date Of Expiration </strong> <strong class="tileHead">Date Of Expiration </strong>
</mat-label> </mat-label>
<mat-form-field appearance="outline"> <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>
<mat-form-field appearance="fill" style="width: 200px;"> <mat-form-field appearance="fill" style="width: 200px;">
<span matPrefix>Time &nbsp;</span> <span matPrefix>Time &nbsp;</span>
<input matInput type="time"> <input matInput type="time" [(ngModel)]="expiryTime">
</mat-form-field> </mat-form-field>
</div> </div>
<div *ngIf="isScreenSmall"> <div *ngIf="isScreenSmall">
<div style="display: flex;align-items: baseline;margin-top: 20px;"> <div style="display: flex;align-items: baseline;margin-top: 20px;">
<mat-form-field appearance="outline"> <mat-form-field appearance="outline">
<mat-label style="margin-right: 48px;"> <mat-label style="margin-right: 48px;">
<strong class="tileHead">Date Of Expiration </strong> <strong class="tileHead">Date Of Expiration </strong>
</mat-label> </mat-label>
<input matInput style="margin-left:10px;" type="date"> <input matInput style="margin-left:10px;" type="date" [(ngModel)]="expiryDate">
</mat-form-field> </mat-form-field>
</div> </div>
<div style="display: flex;align-items: baseline;"> <div style="display: flex;align-items: baseline;">
<mat-form-field appearance="outline"> <mat-form-field appearance="outline">
<span matPrefix>Time &nbsp;</span> <span matPrefix>Time &nbsp;</span>
<input matInput type="time"> <input matInput type="time" [(ngModel)]="expiryTime">
</mat-form-field> </mat-form-field>
</div>
</div> </div>
</div>
<div class="row" style="display: flex;justify-content:space-around;width:100%;"> <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>
</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 { 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 { tesoMediaWatcherService } from '@teso/services/media-watcher';
import { TesoBusinessDetail } from 'app/models/businessModel'; 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 { ProductsModel } from 'app/models/productsModel';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators'; import { takeUntil } from 'rxjs/operators';
@ -18,21 +19,38 @@ import { CouponsService } from '../coupons.service';
export class AdminNewCouponsComponent implements OnInit, OnDestroy { export class AdminNewCouponsComponent implements OnInit, OnDestroy {
businessTargetted: TesoBusinessDetail = {}; businessTargetted: TesoBusinessDetail = {};
products: ProductsModel[] = []; products: ProductsModel[] = [];
fromWorth:number;
toWorth:number;
selectedProduct: any = {}; selectedProduct: any = {};
selectedType: any = {}; selectedType: any = {};
isLoading: boolean = false; isLoading: boolean = false;
isScreenSmall: boolean; isScreenSmall: boolean;
couponTypes: CouponsType[] = []; 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>(); private _unsubscribeAll: Subject<any> = new Subject<any>();
constructor(@Inject(MAT_DIALOG_DATA) public data: { product: any }, constructor(@Inject(MAT_DIALOG_DATA) public data: { product: any },
public dialog: MatDialog, private _productService: ProductsService, 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 { ngOnInit(): void {
this._couponService.categories$.pipe(takeUntil(this._unsubscribeAll)).subscribe((d) => { this._couponService.categories$.pipe(takeUntil(this._unsubscribeAll)).subscribe((d) => {
this.couponTypes = d; this.couponTypes = d;
console.log(d)
}); });
this._productService.data$.pipe(takeUntil(this._unsubscribeAll)).subscribe((d) => { this._productService.data$.pipe(takeUntil(this._unsubscribeAll)).subscribe((d) => {
this.products = d; this.products = d;
@ -55,9 +73,23 @@ export class AdminNewCouponsComponent implements OnInit, OnDestroy {
this.businessTargetted = result; this.businessTargetted = result;
}); });
} }
ngOnDestroy(): void { ngOnDestroy(): void {
// Unsubscribe from all subscriptions // Unsubscribe from all subscriptions
this._unsubscribeAll.next(); this._unsubscribeAll.next();
this._unsubscribeAll.complete(); 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 { ComponentFixture, TestBed } from '@angular/core/testing';
import { DesiresComponent } from './desires.component'; import { DetailsDialogComponent } from './details-dialog.component';
describe('DesiresComponent', () => { describe('DetailsDialogComponent', () => {
let component: DesiresComponent; let component: DetailsDialogComponent;
let fixture: ComponentFixture<DesiresComponent>; let fixture: ComponentFixture<DetailsDialogComponent>;
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ DesiresComponent ] declarations: [ DetailsDialogComponent ]
}) })
.compileComponents(); .compileComponents();
}); });
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(DesiresComponent); fixture = TestBed.createComponent(DetailsDialogComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); 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> </th>
<td mat-cell *matCellDef="let transaction"> <td mat-cell *matCellDef="let transaction">
<span class="pr-6 whitespace-wrap"> <span class="pr-6 whitespace-wrap">
{{transaction.targetProduct}} {{transaction.targetProduct | couponProductShort}}
</span> </span>
</td> </td>
</ng-container> </ng-container>
@ -52,7 +52,7 @@
</th> </th>
<td mat-cell *matCellDef="let transaction"> <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')"> <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>
<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 100% OFF
@ -74,9 +74,12 @@
Status Status
</th> </th>
<td mat-cell *matCellDef="let transaction"> <td mat-cell *matCellDef="let transaction">
<span class="pr-6 whitespace-wrap"> <span class="pr-6 whitespace-wrap" *ngIf="transaction.status != 'cancelled'">
Expired Expired
</span> </span>
<span class="pr-6 whitespace-wrap" *ngIf="transaction.status == 'cancelled'">
Cancelled
</span>
</td> </td>
</ng-container> </ng-container>
<ng-container matColumnDef="expiration"> <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 { MatSort } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table'; import { MatTableDataSource } from '@angular/material/table';
@ -9,12 +10,16 @@ import { MatTableDataSource } from '@angular/material/table';
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
exportAs: 'inactivecoupons' exportAs: 'inactivecoupons'
}) })
export class InactiveCouponsComponent implements OnInit { export class InactiveCouponsComponent implements OnInit,AfterViewInit {
@Input() couponsDataSource: MatTableDataSource<any>; @Input() couponsDataSource: MatTableDataSource<any>;
couponsTableColumns: string[] = ['product', 'quantity', 'type', 'condition', 'range', 'claimed', 'status', 'expiration', 'actions']; couponsTableColumns: string[] = ['product', 'quantity', 'type', 'condition', 'range', 'claimed', 'status', 'expiration', 'actions'];
@ViewChild('couponsTable', { read: MatSort }) couponsTableMatSort: MatSort; @ViewChild('couponsTable', { read: MatSort }) couponsTableMatSort: MatSort;
@ViewChild(MatPaginator) paginator: MatPaginator;
constructor() { } constructor() { }
ngAfterViewInit(): void {
this.couponsDataSource.paginator = this.paginator;
}
ngOnInit(): void { ngOnInit(): void {
} }
trackByFn(index: number, item: any): any { trackByFn(index: number, item: any): any {

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

@ -44,8 +44,8 @@
</div> </div>
<div class="column"> <div class="column">
<mat-form-field appearance="fill" style="min-width: 200px;"> <mat-form-field appearance="fill" style="min-width: 200px;">
<mat-select [(ngModel)]="selectedType"> <mat-select [(ngModel)]="newCoupon.type">
<mat-option *ngFor="let type of couponTypes" [value]="type"> <mat-option *ngFor="let type of couponTypes" [value]="type.typeCode">
{{type.typeName}} {{type.typeName}}
</mat-option> </mat-option>
</mat-select> </mat-select>
@ -61,7 +61,7 @@
</mat-label> </mat-label>
</div> </div>
<div class="column"> <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> </div>
<div class="columns" style="display: flex;align-items: center"> <div class="columns" style="display: flex;align-items: center">
@ -72,52 +72,76 @@
</div> </div>
<div class="column"> <div class="column">
<mat-form-field appearance="fill" style="min-width: 200px;"> <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> </mat-form-field>
</div> </div>
</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;"> <mat-label style="margin-right: 48px;">
<strong class="tileHead">Percentage Off </strong> <strong class="tileHead">Percentage Off </strong>
</mat-label> </mat-label>
<mat-form-field appearance="fill" style="margin-right:10px;"> <mat-form-field appearance="fill" style="margin-right:10px;">
<span matPrefix>From &nbsp;</span> <span matPrefix>From &nbsp;</span>
<input matInput type="number"> <input matInput type="number" [(ngModel)]="newCoupon.lower">
<span matSuffix>% &nbsp;</span> <span matSuffix>% &nbsp;</span>
</mat-form-field> </mat-form-field>
<mat-form-field appearance="fill" style="margin-left:10px;"> <mat-form-field appearance="fill" style="margin-left:10px;">
<span matPrefix>To &nbsp;</span> <span matPrefix>To &nbsp;</span>
<input matInput type="number"> <input matInput type="number" [(ngModel)]="newCoupon.upper">
<span matSuffix>% &nbsp;</span> <span matSuffix>% &nbsp;</span>
</mat-form-field> </mat-form-field>
</div> </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;"> <mat-label style="margin-right: 48px;">
<strong class="tileHead">Percentage Off </strong> <strong class="tileHead">Percentage Off </strong>
</mat-label> </mat-label>
<div style="display: flex;align-items: baseline"> <div style="display: flex;align-items: baseline">
<mat-form-field appearance="outline" style="margin-right:10px;"> <mat-form-field appearance="outline" style="margin-right:10px;">
<span matPrefix>From &nbsp;</span> <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> <span matSuffix>% &nbsp;</span>
</mat-form-field> </mat-form-field>
</div> </div>
<div style="display: flex;align-items: baseline"> <div style="display: flex;align-items: baseline">
<mat-form-field appearance="outline" style="margin-left:10px;"> <mat-form-field appearance="outline" style="margin-left:10px;">
<span matPrefix>To &nbsp;</span> <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> <span matSuffix>% &nbsp;</span>
</mat-form-field> </mat-form-field>
</div> </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">
FREE
</div>
</div>
<div style="display: flex;align-items: baseline"> <div style="display: flex;align-items: baseline">
<mat-label style="margin-right: 48px;"> <mat-label style="margin-right: 48px;">
<strong class="tileHead">Coupon Worth </strong> <strong class="tileHead">Coupon Worth </strong>
</mat-label> </mat-label>
<mat-label> <mat-label>
From {{selectedProduct.unitPrice | couponWorth:fromWorth}} To {{selectedProduct.unitPrice | From {{selectedProduct.unitPrice | couponWorth:newCoupon.lower}} To {{selectedProduct.unitPrice |
couponWorth:toWorth}} couponWorth:newCoupon.upper}}
</mat-label> </mat-label>
</div> </div>
<div style="display: flex;align-items: baseline;margin-top: 20px;" *ngIf="!isScreenSmall"> <div style="display: flex;align-items: baseline;margin-top: 20px;" *ngIf="!isScreenSmall">
@ -125,11 +149,11 @@
<strong class="tileHead">Date Of Expiration </strong> <strong class="tileHead">Date Of Expiration </strong>
</mat-label> </mat-label>
<mat-form-field appearance="outline"> <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>
<mat-form-field appearance="fill" style="width: 200px;"> <mat-form-field appearance="fill" style="width: 200px;">
<span matPrefix>Time &nbsp;</span> <span matPrefix>Time &nbsp;</span>
<input matInput type="time"> <input matInput type="time" [(ngModel)]="expiryTime">
</mat-form-field> </mat-form-field>
</div> </div>
<div *ngIf="isScreenSmall"> <div *ngIf="isScreenSmall">
@ -138,18 +162,18 @@
<mat-label style="margin-right: 48px;"> <mat-label style="margin-right: 48px;">
<strong class="tileHead">Date Of Expiration </strong> <strong class="tileHead">Date Of Expiration </strong>
</mat-label> </mat-label>
<input matInput style="margin-left:10px;" type="date"> <input matInput style="margin-left:10px;" type="date" [(ngModel)]="expiryDate">
</mat-form-field> </mat-form-field>
</div> </div>
<div style="display: flex;align-items: baseline;"> <div style="display: flex;align-items: baseline;">
<mat-form-field appearance="outline"> <mat-form-field appearance="outline">
<span matPrefix>Time &nbsp;</span> <span matPrefix>Time &nbsp;</span>
<input matInput type="time"> <input matInput type="time" [(ngModel)]="expiryTime">
</mat-form-field> </mat-form-field>
</div> </div>
</div> </div>
<div class="row" style="display: flex;justify-content:space-around;width:100%;"> <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>
</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 { 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 { 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 { ProductsModel } from 'app/models/productsModel';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators'; import { takeUntil } from 'rxjs/operators';
@ -13,23 +14,51 @@ import { CouponsService } from '../coupons.service';
templateUrl: './new-coupons.component.html', templateUrl: './new-coupons.component.html',
styleUrls: ['./new-coupons.component.scss'] styleUrls: ['./new-coupons.component.scss']
}) })
export class NewCouponsComponent implements OnInit,OnDestroy { export class NewCouponsComponent implements OnInit, OnDestroy {
products: ProductsModel[] = []; products: ProductsModel[] = [];
selectedProduct:any={}; selectedProduct: ProductsModel = {
selectedType:any={}; productName: '',
businessID: '',
productDesc: '',
productID: '',
categoryID: '',
unitPrice: 0,
productImage: '',
images: []
};
selectedType: any = {};
isLoading: boolean = false; isLoading: boolean = false;
couponTypes:CouponsType[]=[]; couponTypes: CouponsType[] = [];
fromWorth:number; fromWorth: number;
toWorth:number; toWorth: number;
isScreenSmall:boolean; 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>(); private _unsubscribeAll: Subject<any> = new Subject<any>();
constructor(@Inject(MAT_DIALOG_DATA) public data: { product: any },
public dialog: MatDialog, private _productService: ProductsService, constructor(
private _couponService: CouponsService, private _tesoMediaWatcherService: tesoMediaWatcherService,) { } public dialog: MatDialog, private _productService: ProductsService,
private _couponService: CouponsService, private _tesoMediaWatcherService: tesoMediaWatcherService,
public dialogRef: MatDialogRef<NewCouponsComponent>,
private confirmBoxEvokeService: ConfirmBoxEvokeService,) { }
ngOnInit(): void { ngOnInit(): void {
this._couponService.categories$.pipe(takeUntil(this._unsubscribeAll)).subscribe((d) => { this._couponService.categories$.pipe(takeUntil(this._unsubscribeAll)).subscribe((d) => {
this.couponTypes = d; this.couponTypes = d;
console.log(d)
}); });
this._productService.data$.pipe(takeUntil(this._unsubscribeAll)).subscribe((d) => { this._productService.data$.pipe(takeUntil(this._unsubscribeAll)).subscribe((d) => {
this.products = d; this.products = d;
@ -47,4 +76,15 @@ export class NewCouponsComponent implements OnInit,OnDestroy {
this._unsubscribeAll.next(); this._unsubscribeAll.next();
this._unsubscribeAll.complete(); 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 { MatDialog } from '@angular/material/dialog';
import { MatTableDataSource } from '@angular/material/table'; import { MatTableDataSource } from '@angular/material/table';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
@ -11,6 +11,7 @@ import { CouponsService } from './coupons.service';
import { NewCouponsComponent } from './NewCoupons/new-coupons.component'; import { NewCouponsComponent } from './NewCoupons/new-coupons.component';
import jwt_decode from 'jwt-decode'; import jwt_decode from 'jwt-decode';
import { AuthService } from 'app/core/auth/auth.service'; import { AuthService } from 'app/core/auth/auth.service';
import { MatPaginator } from '@angular/material/paginator';
@Component({ @Component({
selector: 'app-coupons', selector: 'app-coupons',
@ -41,7 +42,7 @@ export class CouponsComponent implements OnInit {
const tokenInfo = this.getDecodedAccessToken(this._authService.relevantToken); const tokenInfo = this.getDecodedAccessToken(this._authService.relevantToken);
if (tokenInfo.businessID != "1TESBU00000000") { if (tokenInfo.businessID == "1TESBU00000000") {
const dialogRef = this.dialog.open(AdminNewCouponsComponent, { const dialogRef = this.dialog.open(AdminNewCouponsComponent, {
disableClose: true, disableClose: true,
hasBackdrop: 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 { NewCouponsComponent } from './NewCoupons/new-coupons.component';
import { AdminNewCouponsComponent } from './AdminNewCoupons/admin-new-coupons.component'; import { AdminNewCouponsComponent } from './AdminNewCoupons/admin-new-coupons.component';
import { CouponWorthPipe } from 'app/pipes/coupon-worth.pipe'; import { CouponWorthPipe } from 'app/pipes/coupon-worth.pipe';
import { CouponProductShort } from 'app/pipes/couponProductShort.pipe';
import { DetailsDialogComponent } from './DetailsDialog/details-dialog.component';
@NgModule({ @NgModule({
@ -36,7 +38,9 @@ import { CouponWorthPipe } from 'app/pipes/coupon-worth.pipe';
InactiveCouponsComponent, InactiveCouponsComponent,
NewCouponsComponent, NewCouponsComponent,
AdminNewCouponsComponent, AdminNewCouponsComponent,
CouponWorthPipe CouponWorthPipe,
CouponProductShort,
DetailsDialogComponent
], ],
imports: [ imports: [
RouterModule.forChild(couponsRoutes), 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 { HttpClient } from '@angular/common/http';
import { BehaviorSubject, Observable } from 'rxjs'; import { BehaviorSubject, Observable } from 'rxjs';
import { tap } from 'rxjs/operators'; import { tap } from 'rxjs/operators';
import { analytics as analyticsData } from 'app/mock-api/dashboards/analytics/data';
import { environment } from 'environments/environment'; import { environment } from 'environments/environment';
import { CouponsModel, CouponsType } from 'app/models/couponsModel'; 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>
<div class="column"> <div class="column">
<mat-form-field appearance="fill" style="min-width: 200px;"> <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="TESCP005">DISCOUNT </mat-option>
<mat-option value="TESCP006">FREEBIE </mat-option> <mat-option value="TESCP006">FREEBIE </mat-option>
</mat-select> </mat-select>
@ -80,7 +80,7 @@
</mat-label> </mat-label>
</div> </div>
<div class="column"> <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> </div>
<div class="columns" style="display: flex;align-items: center"> <div class="columns" style="display: flex;align-items: center">
@ -91,7 +91,7 @@
</div> </div>
<div class="column"> <div class="column">
<mat-form-field appearance="fill" style="min-width: 200px;"> <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> </mat-form-field>
</div> </div>
@ -102,12 +102,12 @@
</mat-label> </mat-label>
<mat-form-field appearance="fill" style="margin-right:10px;"> <mat-form-field appearance="fill" style="margin-right:10px;">
<span matPrefix>From &nbsp;</span> <span matPrefix>From &nbsp;</span>
<input matInput type="number"> <input matInput type="number" [(ngModel)]="newCoupon.lower">
<span matSuffix>% &nbsp;</span> <span matSuffix>% &nbsp;</span>
</mat-form-field> </mat-form-field>
<mat-form-field appearance="fill" style="margin-left:10px;"> <mat-form-field appearance="fill" style="margin-left:10px;">
<span matPrefix>To &nbsp;</span> <span matPrefix>To &nbsp;</span>
<input matInput type="number"> <input matInput type="number" [(ngModel)]="newCoupon.upper">
<span matSuffix>% &nbsp;</span> <span matSuffix>% &nbsp;</span>
</mat-form-field> </mat-form-field>
</div> </div>
@ -116,7 +116,7 @@
<strong class="tileHead">Coupon Worth </strong> <strong class="tileHead">Coupon Worth </strong>
</mat-label> </mat-label>
<mat-label> <mat-label>
From GH¢200 To GH¢800 From {{selectedProduct.unitPrice | personalizedcouponWorth:newCoupon.lower}} To {{selectedProduct.unitPrice | personalizedcouponWorth:newCoupon.upper}}
</mat-label> </mat-label>
</div> </div>
<div style="display: flex;align-items: baseline;margin-top: 20px;"> <div style="display: flex;align-items: baseline;margin-top: 20px;">
@ -124,15 +124,15 @@
<strong class="tileHead">Date Of Expiration </strong> <strong class="tileHead">Date Of Expiration </strong>
</mat-label> </mat-label>
<mat-form-field appearance="outline"> <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>
<mat-form-field appearance="fill"> <mat-form-field appearance="fill">
<span matPrefix>Time &nbsp;</span> <span matPrefix>Time &nbsp;</span>
<input matInput type="time"> <input matInput type="time" [(ngModel)]="expiryTime">
</mat-form-field> </mat-form-field>
</div> </div>
<div class="row" style="display: flex;justify-content:space-around;width:100%;"> <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>
</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 { 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 { 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 { ProductsModel } from 'app/models/productsModel';
import { TesoUserDetails } from 'app/models/userModel'; import { TesoUserDetails } from 'app/models/userModel';
import { environment } from 'environments/environment'; import { environment } from 'environments/environment';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators'; 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 { BusinessLookUpComponent } from '../../Products/BusinessLookUp/business-look-up.component';
import { ProductsService } from '../../Products/products.service'; 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({ @Component({
selector: 'app-personalized-coupons', selector: 'app-personalized-coupons',
@ -17,26 +22,54 @@ import { ProductsService } from '../../Products/products.service';
styleUrls: ['./personalized-coupons.component.scss'] styleUrls: ['./personalized-coupons.component.scss']
}) })
export class PersonalizedCouponsComponent implements OnInit { export class PersonalizedCouponsComponent implements OnInit {
tesoGhana:boolean = false; tesoGhana: boolean = false;
businessTargetted: TesoBusinessDetail = {}; businessTargetted: TesoBusinessDetail = {};
private _unsubscribeAll: Subject<any> = new Subject<any>();
isLoading: boolean = false; isLoading: boolean = false;
products: ProductsModel[] = []; products: ProductsModel[] = [];
selectedProduct: any = {}; selectedProduct: any = {};
selectedType: string; selectedType: string;
couponTypes: CouponsType[] = []; couponTypes: CouponsType[] = [];
constructor(private _productService: ProductsService, newCoupon: CouponsModel = {
@Inject(MAT_DIALOG_DATA) public data: { subscriber: TesoUserDetails }, couponID: '',
public dialog: MatDialog,) { } 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 { ngOnInit(): void {
this._productService.data$.pipe(takeUntil(this._unsubscribeAll)).subscribe((d) => { this._productService.data$.pipe(takeUntil(this._unsubscribeAll)).subscribe((d) => {
this.products = d; this.products = d;
}); });
} }
imageLoader(path: string): string { imageLoader(path: string): string {
return environment.apiURL + `followeruserdp/${path}`; return environment.apiURL + `followeruserdp/${path}`;
} }
lookupBusiness() { lookupBusiness() {
const dialogRef = this.dialog.open(BusinessLookUpComponent, { const dialogRef = this.dialog.open(BusinessLookUpComponent, {
disableClose: true, disableClose: true,
@ -47,4 +80,25 @@ export class PersonalizedCouponsComponent implements OnInit {
this.businessTargetted = result; 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 class="flex items-center justify-between w-full" style="margin-bottom: 20px;">
<div> <div>
<div class="text-3xl font-semibold tracking-tight leading-8">Subscribers</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> </div>
<div class="flex flex-shrink-0 items-center mt-6 sm:mt-0 sm:ml-4"> <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 { PersonalizedCouponsComponent } from './PersonalizedCoupons/personalized-coupons.component';
import { MatDialogModule } from '@angular/material/dialog'; import { MatDialogModule } from '@angular/material/dialog';
import { MatSelectModule } from '@angular/material/select'; import { MatSelectModule } from '@angular/material/select';
import { PersonalizedCouponWorthPipe } from 'app/pipes/personalized-coupon-worth.pipe';
@NgModule({ @NgModule({
declarations: [ declarations: [
FollowersComponent, FollowersComponent,
PersonalizedCouponsComponent PersonalizedCouponsComponent,
PersonalizedCouponWorthPipe
], ],
imports: [ imports: [
RouterModule.forChild(followersRoutes), 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-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 --> <!-- Title and action buttons -->
<div class="flex items-center justify-between w-full" style="margin-bottom: 20px;" *ngIf="!isScreenSmall"> <div class="flex items-center justify-between w-full" style="margin-bottom: 20px;" *ngIf="!isScreenSmall">
<div> <div>
<div class="text-3xl font-semibold tracking-tight leading-8">Monthly Desires</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>
</div> </div>
@ -22,7 +23,7 @@
<div class="flex items-center justify-between w-full" style="margin-bottom: 20px;" *ngIf="isScreenSmall"> <div class="flex items-center justify-between w-full" style="margin-bottom: 20px;" *ngIf="isScreenSmall">
<div> <div>
<div class="text-3xl font-semibold tracking-tight leading-8">Monthly Desires</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>
</div> </div>
@ -45,14 +46,17 @@
<div class="product"> <div class="product">
<div class="product-content"> <div class="product-content">
<div class="product-img"> <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"> --> <!-- <img [src]="imageLoader(product.productImage)" alt="product image" style="max-height:200px;" *ngIf="productImage == null"> -->
</div> </div>
<div class="product-btns"> <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 Generate Coupons
</button> </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 Add Product
</button> </button>
</div> </div>
@ -63,9 +67,14 @@
<div class="product-info-top"> <div class="product-info-top">
<h2 class="sm-title">{{product.category}}</h2> <h2 class="sm-title">{{product.category}}</h2>
</div> </div>
<a href="javascript:void(0)" class="product-name" *ngIf="product.productName === null">{{product.productID}}</a> <a href="javascript:void(0)" class="product-name"
<a href="javascript:void(0)" class="product-name" *ngIf="product.productName != null">{{product.productName}}</a> *ngIf="product.productName === null">{{product.productID}}</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.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> --> <!-- </div> -->
</div> </div>
@ -74,9 +83,10 @@
<mat-paginator <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" 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> [pageSizeOptions]="[5, 10, 25, 100]" [showFirstLastButtons]="true"></mat-paginator>
<ng-template #noProducts> </div>
<div class="p-8 sm:p-16 border-t text-4xl font-semibold tracking-tight text-center">There are no monthly desires yet! <div class="flex flex-wrap w-full max-w-screen-xl mx-auto p-6 md:p-8" *ngIf="data.mostWishedProduct.length == 0">
</div> <div class="p-8 sm:p-16 border-t text-4xl font-semibold tracking-tight text-center">There are no monthly desires
</ng-template> yet!
</div>
</div> </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 { CouponDialogComponent } from '../../Products/CouponDialog/coupon-dialog.component';
import { ProductsService } from '../../Products/products.service'; import { ProductsService } from '../../Products/products.service';
import { DesiresService } from '../desires.service'; import { DesiresService } from '../desires.service';
import jwt_decode from 'jwt-decode';
import { AuthService } from 'app/core/auth/auth.service';
@Component({ @Component({
selector: 'app-list-desires', selector: 'app-list-desires',
@ -28,7 +30,7 @@ export class ListDesiresComponent implements OnInit {
availableP:any[]=[]; availableP:any[]=[];
private _unsubscribeAll: Subject<any> = new Subject<any>(); private _unsubscribeAll: Subject<any> = new Subject<any>();
constructor(private router: Router, private _tesoMediaWatcherService: tesoMediaWatcherService, 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) { } private _desireService: DesiresService,private _productService: ProductsService) { }
ngOnDestroy(): void { ngOnDestroy(): void {
@ -60,7 +62,8 @@ export class ListDesiresComponent implements OnInit {
generateCoupon(product: any) { generateCoupon(product: any) {
const selectedproduct = this._productService.getProduct(product); 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, { const dialogRef = this.dialog.open(CouponDialogComponent, {
disableClose: true, disableClose: true,
hasBackdrop: true, hasBackdrop: true,
@ -79,4 +82,11 @@ export class ListDesiresComponent implements OnInit {
imageLoader(path: string): string { imageLoader(path: string): string {
return environment.apiURL + `imagingproducts/${path}`; 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>
<div class="column"> <div class="column">
<mat-form-field appearance="fill" style="min-width: 200px;"> <mat-form-field appearance="fill" style="min-width: 200px;">
<mat-select [(ngModel)]="selectedType"> <mat-select [(ngModel)]="newCoupon.type">
<mat-option *ngFor="let type of couponTypes" [value]="type"> <mat-option *ngFor="let type of couponTypes" [value]="type.typeCode">
{{type.typeName}} {{type.typeName}}
</mat-option> </mat-option>
</mat-select> </mat-select>
@ -70,7 +70,7 @@
</mat-label> </mat-label>
</div> </div>
<div class="column"> <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> </div>
<div class="columns" style="display: flex;align-items: center"> <div class="columns" style="display: flex;align-items: center">
@ -81,7 +81,7 @@
</div> </div>
<div class="column"> <div class="column">
<mat-form-field appearance="fill" style="min-width: 200px;"> <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> </mat-form-field>
</div> </div>
@ -92,12 +92,12 @@
</mat-label> </mat-label>
<mat-form-field appearance="fill" style="margin-right:10px;"> <mat-form-field appearance="fill" style="margin-right:10px;">
<span matPrefix>From &nbsp;</span> <span matPrefix>From &nbsp;</span>
<input matInput type="number"> <input matInput type="number" [(ngModel)]="newCoupon.lower">
<span matSuffix>% &nbsp;</span> <span matSuffix>% &nbsp;</span>
</mat-form-field> </mat-form-field>
<mat-form-field appearance="fill" style="margin-left:10px;"> <mat-form-field appearance="fill" style="margin-left:10px;">
<span matPrefix>To &nbsp;</span> <span matPrefix>To &nbsp;</span>
<input matInput type="number"> <input matInput type="number" [(ngModel)]="newCoupon.upper">
<span matSuffix>% &nbsp;</span> <span matSuffix>% &nbsp;</span>
</mat-form-field> </mat-form-field>
</div> </div>
@ -106,7 +106,8 @@
<strong class="tileHead">Coupon Worth </strong> <strong class="tileHead">Coupon Worth </strong>
</mat-label> </mat-label>
<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> </mat-label>
</div> </div>
<div style="display: flex;align-items: baseline;margin-top: 20px;"> <div style="display: flex;align-items: baseline;margin-top: 20px;">
@ -114,15 +115,15 @@
<strong class="tileHead">Date Of Expiration </strong> <strong class="tileHead">Date Of Expiration </strong>
</mat-label> </mat-label>
<mat-form-field appearance="outline"> <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>
<mat-form-field appearance="fill"> <mat-form-field appearance="fill">
<span matPrefix>Time &nbsp;</span> <span matPrefix>Time &nbsp;</span>
<input matInput type="time"> <input matInput type="time" [(ngModel)]="expiryTime">
</mat-form-field> </mat-form-field>
</div> </div>
<div class="row" style="display: flex;justify-content:space-around;width:100%;"> <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>
</div> </div>
@ -172,8 +173,8 @@
<mat-label style="margin-right: 20px;"> <mat-label style="margin-right: 20px;">
<strong class="tileHead">Coupon Type</strong> <strong class="tileHead">Coupon Type</strong>
</mat-label> </mat-label>
<mat-select [(ngModel)]="selectedType"> <mat-select [(ngModel)]="newCoupon.type">
<mat-option *ngFor="let type of couponTypes" [value]="type"> <mat-option *ngFor="let type of couponTypes" [value]="type.typeCode">
{{type.typeName}} {{type.typeName}}
</mat-option> </mat-option>
</mat-select> </mat-select>
@ -189,7 +190,7 @@
</mat-label> </mat-label>
</div> </div>
<div class="column"> <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> </div>
<div class="columns" style="display: flex;align-items: center"> <div class="columns" style="display: flex;align-items: center">
@ -199,7 +200,7 @@
<mat-label style="margin-right: 48px;"> <mat-label style="margin-right: 48px;">
<strong class="tileHead">Number Of Coupons</strong> <strong class="tileHead">Number Of Coupons</strong>
</mat-label> </mat-label>
<input matInput style="margin-left:10px;" type="number"> <input matInput style="margin-left:10px;" type="number" [(ngModel)]="newCoupon.quantity">
</mat-form-field> </mat-form-field>
</div> </div>
@ -210,14 +211,14 @@
<div style="display: flex;align-items: baseline"> <div style="display: flex;align-items: baseline">
<mat-form-field appearance="outline" style="margin-right:10px;"> <mat-form-field appearance="outline" style="margin-right:10px;">
<span matPrefix>From &nbsp;</span> <span matPrefix>From &nbsp;</span>
<input matInput type="number"> <input matInput type="number" [(ngModel)]="newCoupon.lower">
<span matSuffix>% &nbsp;</span> <span matSuffix>% &nbsp;</span>
</mat-form-field> </mat-form-field>
</div> </div>
<div style="display: flex;align-items: baseline"> <div style="display: flex;align-items: baseline">
<mat-form-field appearance="outline" style="margin-left:10px;"> <mat-form-field appearance="outline" style="margin-left:10px;">
<span matPrefix>To &nbsp;</span> <span matPrefix>To &nbsp;</span>
<input matInput type="number"> <input matInput type="number" [(ngModel)]="newCoupon.upper">
<span matSuffix>% &nbsp;</span> <span matSuffix>% &nbsp;</span>
</mat-form-field> </mat-form-field>
</div> </div>
@ -226,7 +227,8 @@
<strong class="tileHead">Coupon Worth </strong> <strong class="tileHead">Coupon Worth </strong>
</mat-label> </mat-label>
<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> </mat-label>
</div> </div>
<div style="display: flex;align-items: baseline;margin-top: 20px;"> <div style="display: flex;align-items: baseline;margin-top: 20px;">
@ -234,17 +236,17 @@
<mat-label style="margin-right: 48px;"> <mat-label style="margin-right: 48px;">
<strong class="tileHead">Date Of Expiration </strong> <strong class="tileHead">Date Of Expiration </strong>
</mat-label> </mat-label>
<input matInput style="margin-left:10px;" type="date"> <input matInput style="margin-left:10px;" type="date" [(ngModel)]="expiryDate">
</mat-form-field> </mat-form-field>
</div> </div>
<div style="display: flex;align-items: baseline;"> <div style="display: flex;align-items: baseline;">
<mat-form-field appearance="outline"> <mat-form-field appearance="outline">
<span matPrefix>Time &nbsp;</span> <span matPrefix>Time &nbsp;</span>
<input matInput type="time"> <input matInput type="time" [(ngModel)]="expiryTime">
</mat-form-field> </mat-form-field>
</div> </div>
<div class="row" style="display: flex;justify-content:space-around;width:100%;"> <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>
</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 { 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 { ConfirmBoxEvokeService } from '@costlydeveloper/ngx-awesome-popup';
import { tesoMediaWatcherService } from '@teso/services/media-watcher'; import { tesoMediaWatcherService } from '@teso/services/media-watcher';
import { TesoBusinessDetail } from 'app/models/businessModel'; 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 { ProductsModel } from 'app/models/productsModel';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators'; import { takeUntil } from 'rxjs/operators';
import { CouponsModule } from '../../Coupons/coupons.module';
import { CouponsService } from '../../Coupons/coupons.service'; import { CouponsService } from '../../Coupons/coupons.service';
import { BusinessLookUpComponent } from '../BusinessLookUp/business-look-up.component'; import { BusinessLookUpComponent } from '../BusinessLookUp/business-look-up.component';
import { ProductsService } from '../products.service'; import { ProductsService } from '../products.service';
@ -16,17 +18,32 @@ import { ProductsService } from '../products.service';
templateUrl: './admin-coupon-dialog.component.html', templateUrl: './admin-coupon-dialog.component.html',
styleUrls: ['./admin-coupon-dialog.component.scss'] styleUrls: ['./admin-coupon-dialog.component.scss']
}) })
export class AdminCouponDialogComponent implements OnInit,OnDestroy { export class AdminCouponDialogComponent implements OnInit, OnDestroy {
businessTargetted:TesoBusinessDetail={}; businessTargetted: TesoBusinessDetail = {};
private _unsubscribeAll: Subject<any> = new Subject<any>(); private _unsubscribeAll: Subject<any> = new Subject<any>();
isLoading:boolean=false; isLoading: boolean = false;
selectedProduct:any={}; selectedProduct: any = {};
selectedType:any={}; selectedType: any = {};
couponTypes:CouponsType[]=[]; couponTypes: CouponsType[] = [];
isScreenSmall: boolean; 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( constructor(
private _couponService: CouponsService,@Inject(MAT_DIALOG_DATA) public data: { product: ProductsModel }, private _couponService: CouponsService, @Inject(MAT_DIALOG_DATA) public data: { product: ProductsModel },
public dialog: MatDialog,private _tesoMediaWatcherService: tesoMediaWatcherService, public dialog: MatDialog, private _tesoMediaWatcherService: tesoMediaWatcherService, public dialogRef: MatDialogRef<AdminCouponDialogComponent>,
private confirmBoxEvokeService: ConfirmBoxEvokeService,) { } private confirmBoxEvokeService: ConfirmBoxEvokeService,) { }
ngOnInit(): void { ngOnInit(): void {
@ -34,12 +51,12 @@ export class AdminCouponDialogComponent implements OnInit,OnDestroy {
this.couponTypes = d; this.couponTypes = d;
}); });
this._tesoMediaWatcherService.onMediaChange$ this._tesoMediaWatcherService.onMediaChange$
.pipe(takeUntil(this._unsubscribeAll)) .pipe(takeUntil(this._unsubscribeAll))
.subscribe(({ matchingAliases }) => { .subscribe(({ matchingAliases }) => {
// Check if the screen is small // Check if the screen is small
this.isScreenSmall = !matchingAliases.includes('md'); this.isScreenSmall = !matchingAliases.includes('md');
}); });
} }
lookupBusiness() { lookupBusiness() {
const dialogRef = this.dialog.open(BusinessLookUpComponent, { const dialogRef = this.dialog.open(BusinessLookUpComponent, {
@ -56,4 +73,20 @@ export class AdminCouponDialogComponent implements OnInit,OnDestroy {
this._unsubscribeAll.next(); this._unsubscribeAll.next();
this._unsubscribeAll.complete(); 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>
<div class="flex flex-shrink-0 items-center justify-center" style="margin-top: 10px !important;"> <div class="flex flex-shrink-0 items-center justify-center" style="margin-top: 10px !important;">
<a href="javascript:void(0)" <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> business not registered on teso ?</a>
</div> </div>
<table class="w-full bg-transparent" mat-table matSort [dataSource]="typesDataSource" [trackBy]="trackByFn" <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 { Component, Inject, OnInit, ViewChild } from '@angular/core';
import { FormControl } from '@angular/forms'; 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 { MatSort } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table'; import { MatTableDataSource } from '@angular/material/table';
import { TesoBusinessDetail } from 'app/models/businessModel'; import { TesoBusinessDetail } from 'app/models/businessModel';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { ProfileService } from '../../Profile/profile.service'; import { ProfileService } from '../../Profile/profile.service';
import { TempShopComponent } from '../../TempShop/temp-shop.component';
@Component({ @Component({
selector: 'app-business-look-up', selector: 'app-business-look-up',
@ -21,7 +22,7 @@ export class BusinessLookUpComponent implements OnInit {
htmlRequest: string; htmlRequest: string;
transferType: string; transferType: string;
private _unsubscribeAll: Subject<any> = new Subject<any>(); 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 { ngOnInit(): void {
@ -35,4 +36,16 @@ export class BusinessLookUpComponent implements OnInit {
selectBusiness(business: TesoBusinessDetail) { selectBusiness(business: TesoBusinessDetail) {
this.dialogRef.close(business); 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>
<div class="column"> <div class="column">
<mat-form-field appearance="fill" style="min-width: 200px;"> <mat-form-field appearance="fill" style="min-width: 200px;">
<mat-select [(ngModel)]="selectedType"> <mat-select [(ngModel)]="newCoupon.type">
<mat-option *ngFor="let type of couponTypes" [value]="type"> <mat-option *ngFor="let type of couponTypes" [value]="type.typeCode">
{{type.typeName}} {{type.typeName}}
</mat-option> </mat-option>
</mat-select> </mat-select>
@ -56,7 +56,7 @@
</mat-label> </mat-label>
</div> </div>
<div class="column"> <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> </div>
<div class="columns" style="display: flex;align-items: center"> <div class="columns" style="display: flex;align-items: center">
@ -67,7 +67,7 @@
</div> </div>
<div class="column"> <div class="column">
<mat-form-field appearance="fill" style="min-width: 200px;"> <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> </mat-form-field>
</div> </div>
@ -78,12 +78,12 @@
</mat-label> </mat-label>
<mat-form-field appearance="fill" style="margin-right:10px;"> <mat-form-field appearance="fill" style="margin-right:10px;">
<span matPrefix>From &nbsp;</span> <span matPrefix>From &nbsp;</span>
<input matInput type="number"> <input matInput type="number" [(ngModel)]="newCoupon.lower">
<span matSuffix>% &nbsp;</span> <span matSuffix>% &nbsp;</span>
</mat-form-field> </mat-form-field>
<mat-form-field appearance="fill" style="margin-left:10px;"> <mat-form-field appearance="fill" style="margin-left:10px;">
<span matPrefix>To &nbsp;</span> <span matPrefix>To &nbsp;</span>
<input matInput type="number"> <input matInput type="number" [(ngModel)]="newCoupon.upper">
<span matSuffix>% &nbsp;</span> <span matSuffix>% &nbsp;</span>
</mat-form-field> </mat-form-field>
</div> </div>
@ -92,7 +92,8 @@
<strong class="tileHead">Coupon Worth </strong> <strong class="tileHead">Coupon Worth </strong>
</mat-label> </mat-label>
<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> </mat-label>
</div> </div>
<div style="display: flex;align-items: baseline;margin-top: 20px;"> <div style="display: flex;align-items: baseline;margin-top: 20px;">
@ -100,15 +101,15 @@
<strong class="tileHead">Date Of Expiration </strong> <strong class="tileHead">Date Of Expiration </strong>
</mat-label> </mat-label>
<mat-form-field appearance="outline"> <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>
<mat-form-field appearance="fill"> <mat-form-field appearance="fill">
<span matPrefix>Time &nbsp;</span> <span matPrefix>Time &nbsp;</span>
<input matInput type="time"> <input matInput type="time" [(ngModel)]="expiryTime">
</mat-form-field> </mat-form-field>
</div> </div>
<div class="row" style="display: flex;justify-content:space-around;width:100%;"> <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>
</div> </div>
@ -144,8 +145,8 @@
<mat-label style="margin-right: 20px;"> <mat-label style="margin-right: 20px;">
<strong class="tileHead">Coupon Type</strong> <strong class="tileHead">Coupon Type</strong>
</mat-label> </mat-label>
<mat-select [(ngModel)]="selectedType"> <mat-select [(ngModel)]="newCoupon.type">
<mat-option *ngFor="let type of couponTypes" [value]="type"> <mat-option *ngFor="let type of couponTypes" [value]="type.typeCode">
{{type.typeName}} {{type.typeName}}
</mat-option> </mat-option>
</mat-select> </mat-select>
@ -161,7 +162,7 @@
</mat-label> </mat-label>
</div> </div>
<div class="column"> <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> </div>
<div class="columns" style="display: flex;align-items: center"> <div class="columns" style="display: flex;align-items: center">
@ -171,7 +172,7 @@
<mat-label style="margin-right: 48px;"> <mat-label style="margin-right: 48px;">
<strong class="tileHead">Number Of Coupons</strong> <strong class="tileHead">Number Of Coupons</strong>
</mat-label> </mat-label>
<input matInput style="margin-left:10px;" type="number"> <input matInput style="margin-left:10px;" type="number" [(ngModel)]="newCoupon.quantity">
</mat-form-field> </mat-form-field>
</div> </div>
@ -182,14 +183,14 @@
<div style="display: flex;align-items: baseline"> <div style="display: flex;align-items: baseline">
<mat-form-field appearance="outline" style="margin-right:10px;"> <mat-form-field appearance="outline" style="margin-right:10px;">
<span matPrefix>From &nbsp;</span> <span matPrefix>From &nbsp;</span>
<input matInput type="number"> <input matInput type="number" [(ngModel)]="newCoupon.lower">
<span matSuffix>% &nbsp;</span> <span matSuffix>% &nbsp;</span>
</mat-form-field> </mat-form-field>
</div> </div>
<div style="display: flex;align-items: baseline"> <div style="display: flex;align-items: baseline">
<mat-form-field appearance="outline" style="margin-left:10px;"> <mat-form-field appearance="outline" style="margin-left:10px;">
<span matPrefix>To &nbsp;</span> <span matPrefix>To &nbsp;</span>
<input matInput type="number"> <input matInput type="number" [(ngModel)]="newCoupon.upper">
<span matSuffix>% &nbsp;</span> <span matSuffix>% &nbsp;</span>
</mat-form-field> </mat-form-field>
</div> </div>
@ -198,7 +199,8 @@
<strong class="tileHead">Coupon Worth </strong> <strong class="tileHead">Coupon Worth </strong>
</mat-label> </mat-label>
<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> </mat-label>
</div> </div>
<div style="display: flex;align-items: baseline;margin-top: 20px;"> <div style="display: flex;align-items: baseline;margin-top: 20px;">
@ -206,17 +208,17 @@
<mat-label style="margin-right: 48px;"> <mat-label style="margin-right: 48px;">
<strong class="tileHead">Date Of Expiration </strong> <strong class="tileHead">Date Of Expiration </strong>
</mat-label> </mat-label>
<input matInput style="margin-left:10px;" type="date"> <input matInput style="margin-left:10px;" type="date" [(ngModel)]="expiryDate">
</mat-form-field> </mat-form-field>
</div> </div>
<div style="display: flex;align-items: baseline;"> <div style="display: flex;align-items: baseline;">
<mat-form-field appearance="outline"> <mat-form-field appearance="outline">
<span matPrefix>Time &nbsp;</span> <span matPrefix>Time &nbsp;</span>
<input matInput type="time"> <input matInput type="time" [(ngModel)]="expiryTime">
</mat-form-field> </mat-form-field>
</div> </div>
<div class="row" style="display: flex;justify-content:space-around;width:100%;"> <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>
</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 { 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 { ConfirmBoxEvokeService } from '@costlydeveloper/ngx-awesome-popup';
import { tesoMediaWatcherService } from '@teso/services/media-watcher'; 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 { ProductsModel } from 'app/models/productsModel';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators'; import { takeUntil } from 'rxjs/operators';
import { CouponsService } from '../../Coupons/coupons.service'; import { CouponsService } from '../../Coupons/coupons.service';
import { AdminCouponDialogComponent } from '../AdminCouponDialog/admin-coupon-dialog.component';
import { ProductsService } from '../products.service'; import { ProductsService } from '../products.service';
@Component({ @Component({
@ -14,16 +15,31 @@ import { ProductsService } from '../products.service';
templateUrl: './coupon-dialog.component.html', templateUrl: './coupon-dialog.component.html',
styleUrls: ['./coupon-dialog.component.scss'] styleUrls: ['./coupon-dialog.component.scss']
}) })
export class CouponDialogComponent implements OnInit,OnDestroy { export class CouponDialogComponent implements OnInit, OnDestroy {
private _unsubscribeAll: Subject<any> = new Subject<any>(); private _unsubscribeAll: Subject<any> = new Subject<any>();
isLoading:boolean=false; isLoading: boolean = false;
selectedProduct:any={}; selectedProduct: any = {};
selectedType:any={}; selectedType: any = {};
couponTypes:CouponsType[]=[]; couponTypes: CouponsType[] = [];
isScreenSmall: boolean; 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( constructor(
private _couponService: CouponsService,@Inject(MAT_DIALOG_DATA) public data: { product: ProductsModel }, private _couponService: CouponsService, @Inject(MAT_DIALOG_DATA) public data: { product: ProductsModel },
public dialog: MatDialog,private _productService: ProductsService,private _tesoMediaWatcherService: tesoMediaWatcherService, public dialog: MatDialog, private _tesoMediaWatcherService: tesoMediaWatcherService, public dialogRef: MatDialogRef<CouponDialogComponent>,
private confirmBoxEvokeService: ConfirmBoxEvokeService,) { } private confirmBoxEvokeService: ConfirmBoxEvokeService,) { }
ngOnInit(): void { ngOnInit(): void {
@ -31,12 +47,12 @@ export class CouponDialogComponent implements OnInit,OnDestroy {
this.couponTypes = d; this.couponTypes = d;
}); });
this._tesoMediaWatcherService.onMediaChange$ this._tesoMediaWatcherService.onMediaChange$
.pipe(takeUntil(this._unsubscribeAll)) .pipe(takeUntil(this._unsubscribeAll))
.subscribe(({ matchingAliases }) => { .subscribe(({ matchingAliases }) => {
// Check if the screen is small // Check if the screen is small
this.isScreenSmall = !matchingAliases.includes('md'); this.isScreenSmall = !matchingAliases.includes('md');
}); });
} }
ngOnDestroy(): void { ngOnDestroy(): void {
@ -44,4 +60,14 @@ export class CouponDialogComponent implements OnInit,OnDestroy {
this._unsubscribeAll.next(); this._unsubscribeAll.next();
this._unsubscribeAll.complete(); 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> <mat-icon [svgIcon]="'heroicons_outline:arrow-sm-left'"></mat-icon>
</button> </button>
<h2 class="ml-2.5 text-md sm:text-xl font-medium tracking-tight truncate"> <h2 class="ml-2.5 text-md sm:text-xl font-medium tracking-tight truncate">
Product # {{product.productID}} Product : {{product.productName}}
</h2> </h2>
</div> </div>
<div <div
@ -75,8 +75,8 @@
<div class="font-medium tracking-tight text-secondary">Add up to 10 high <div class="font-medium tracking-tight text-secondary">Add up to 10 high
quality images of the product quality images of the product
</div> </div>
<div class="font-medium tracking-tight text-secondary"> <div class="font-medium tracking-tight text-secondary" *ngIf="product.images != null">
{{productImages.length}} out of {{product.images.length}} out of
<strong>10</strong> <strong>10</strong>
</div> </div>
</div> </div>
@ -84,12 +84,12 @@
</div> </div>
<div class="imageList"> <div class="imageList">
<div class="imageHolder" *ngFor="let images of product.images"> <div class="imageHolder" *ngFor="let images of product.images">
<img src="{{images.imageSRC}}" class="productImage"> <img [src]="imageLoader(images.path)" alt="product image" class="productImage">
<button class="deleteelement" style="border:none;" <!-- <button class="deleteelement" style="border:none;"
(click)="removeImage(images)"> (click)="removeImage(images)">
<mat-icon [svgIcon]="'heroicons_outline:trash'" style="color: red;"> <mat-icon [svgIcon]="'heroicons_outline:trash'" style="color: red;">
</mat-icon> </mat-icon>
</button> </button> -->
</div> </div>
</div> </div>
</div> </div>
@ -175,19 +175,19 @@
<div class="font-medium tracking-tight text-secondary">Add up to 10 high <div class="font-medium tracking-tight text-secondary">Add up to 10 high
quality images of the product quality images of the product
</div> </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> <strong>10</strong>
</div> </div>
</div> </div>
</div> </div>
<div class="imageList"> <div class="imageList">
<div class="imageHolder" *ngFor="let images of productImages"> <div class="imageHolder" *ngFor="let images of productImages">
<img src="{{images.imageSRC}}" class="productImage"> <img [src]="imageLoader(images.path)" alt="product image" class="productImage">
<button class="deleteelement" style="border:none;" <!-- <button class="deleteelement" style="border:none;"
(click)="removeImage(images)"> (click)="removeImage(images)">
<mat-icon [svgIcon]="'heroicons_outline:trash'" style="color: red;"> <mat-icon [svgIcon]="'heroicons_outline:trash'" style="color: red;">
</mat-icon> </mat-icon>
</button> </button> -->
</div> </div>
</div> </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 { tesoMediaWatcherService } from '@teso/services/media-watcher';
import { ProductUpload } from 'app/models/generalModel'; import { ProductUpload } from 'app/models/generalModel';
import { ProductsModel } from 'app/models/productsModel'; import { ProductsModel } from 'app/models/productsModel';
import { environment } from 'environments/environment';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators'; import { takeUntil } from 'rxjs/operators';
import { ProductsService } from '../products.service'; import { ProductsService } from '../products.service';
@ -54,7 +55,7 @@ export class DetailsProductComponent implements OnInit {
private router: Router, private route: ActivatedRoute, private productServie: ProductsService) { private router: Router, private route: ActivatedRoute, private productServie: ProductsService) {
var snapshot = route.snapshot; var snapshot = route.snapshot;
this.productID = snapshot.paramMap.get('id'); this.productID = snapshot.paramMap.get('id');
this.product = productServie.getProduct(this.productID); this.product = productServie.getProduct(this.productID);
if(this.product == null){ if(this.product == null){
this.router.navigate(['products']); this.router.navigate(['products']);
} }
@ -70,16 +71,7 @@ export class DetailsProductComponent implements OnInit {
}); });
} }
quillConfig = modules; 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 { ngOnDestroy(): void {
// Unsubscribe from all subscriptions // Unsubscribe from all subscriptions
this._unsubscribeAll.next(); 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> <mat-icon [svgIcon]="'heroicons_outline:arrow-sm-left'"></mat-icon>
</button> </button>
<h2 class="ml-2.5 text-md sm:text-xl font-medium tracking-tight truncate"> <h2 class="ml-2.5 text-md sm:text-xl font-medium tracking-tight truncate">
Product # {{product.productID}} Product : {{product.productName}}
</h2> </h2>
</div> </div>
<div <div
@ -28,18 +28,19 @@
<strong class="tileHead">Product Name </strong> <strong class="tileHead">Product Name </strong>
</mat-label> </mat-label>
<mat-form-field appearance="fill" style="width: 350px;"> <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> </mat-form-field>
</div> </div>
<div style="display:flex;"> <div style="display:flex;">
<mat-label style="margin-right: 20px;"> <mat-label style="margin-right: 20px;">
<strong class="tileHead">Product Category</strong> <strong>Product Category</strong>
</mat-label> </mat-label>
<mat-form-field appearance="fill" style="width: 350px;"> <mat-form-field appearance="fill" style="width: 350px;">
<mat-select required> <mat-select [(ngModel)]="product.categoryID">
<mat-option value="low">Lowdsadsadsadsadsadsa</mat-option> <mat-option *ngFor="let category of categories"
<mat-option value="standard">Standardasdsadasdas</mat-option> [value]="category.catCode">
<mat-option value="high">Highsadsadsadas</mat-option> {{category.catName}}
</mat-option>
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
</div> </div>
@ -49,7 +50,7 @@
</mat-label> </mat-label>
<mat-form-field appearance="fill" style="width: 350px;"> <mat-form-field appearance="fill" style="width: 350px;">
<span matPrefix>GH¢ &nbsp;</span> <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> </mat-form-field>
</div> </div>
@ -61,7 +62,7 @@
</div> </div>
</div> </div>
<div class="column"> <div class="column">
<quill-editor [modules]="quillConfig" [(ngModel)]="htmlRequest" <quill-editor [modules]="quillConfig" [(ngModel)]="product.productDesc"
placeholder="Further details"> placeholder="Further details">
</quill-editor> </quill-editor>
@ -78,18 +79,18 @@
quality images of the product quality images of the product
</div> </div>
<div class="font-medium tracking-tight text-secondary"> <div class="font-medium tracking-tight text-secondary">
{{productImages.length}} out of {{product.images.length}} out of
<strong>10</strong> <strong>10</strong>
</div> </div>
</div> </div>
<input style="display: none" type="file" (change)="onFileSelected($event)" <input style="display: none" type="file" (change)="onFileSelected($event)"
#fileInput accept="image/*" capture="user"> #fileInput accept="image/*" capture="user">
<button style="background-color: blue;color:white;margin-right:20px;" mat-button <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> </button>
</div> </div>
<div class="imageList"> <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"> <img src="{{images.imageSRC}}" class="productImage">
<button class="deleteelement" style="border:none;" <button class="deleteelement" style="border:none;"
(click)="removeImage(images)"> (click)="removeImage(images)">
@ -103,8 +104,7 @@
</mat-tab-group> </mat-tab-group>
<div class="row" style="display: flex;justify-content:space-around;width:100%;"> <div class="row" style="display: flex;justify-content:space-around;width:100%;">
<button style="background-color: green;color:white;margin-right:20px;" mat-button <button style="background-color: green;color:white;margin-right:20px;" mat-button
(click)="submit()">Add (click)="submit()">Save Changes</button>
Product</button>
<button style="background-color: firebrick;color:white;margin-left:10px;" mat-button <button style="background-color: firebrick;color:white;margin-left:10px;" mat-button
cdkFocusInitial>Cancel</button> cdkFocusInitial>Cancel</button>
</div> </div>
@ -152,10 +152,11 @@
<strong>Product Category</strong> <strong>Product Category</strong>
</mat-label> </mat-label>
<mat-form-field appearance="fill"> <mat-form-field appearance="fill">
<mat-select required> <mat-select [(ngModel)]="product.categoryID">
<mat-option value="low">Lowdsadsadsadsadsadsa</mat-option> <mat-option *ngFor="let category of categories"
<mat-option value="standard">Standardasdsadasdas</mat-option> [value]="category.catCode">
<mat-option value="high">Highsadsadsadas</mat-option> {{category.catName}}
</mat-option>
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
</div> </div>
@ -218,8 +219,7 @@
</mat-tab> </mat-tab>
</mat-tab-group> </mat-tab-group>
<div class="row" style="display: flex;justify-content:space-around;width:100%;"> <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 <button style="background-color: green;color:white;margin-right:20px;" mat-button (click)="submit()">Save Changes</button>
Product</button>
<button style="background-color: firebrick;color:white;margin-left:10px;" mat-button <button style="background-color: firebrick;color:white;margin-left:10px;" mat-button
cdkFocusInitial>Cancel</button> cdkFocusInitial>Cancel</button>
</div> </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 { ActivatedRoute, Router } from '@angular/router';
import { ConfirmBoxEvokeService } from '@costlydeveloper/ngx-awesome-popup'; import { ConfirmBoxEvokeService } from '@costlydeveloper/ngx-awesome-popup';
import { tesoMediaWatcherService } from '@teso/services/media-watcher'; import { tesoMediaWatcherService } from '@teso/services/media-watcher';
import { ProductUpload } from 'app/models/generalModel'; 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 { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators'; import { takeUntil } from 'rxjs/operators';
import { ProductsService } from '../products.service'; import { ProductsService } from '../products.service';
@ -26,7 +26,7 @@ const modules = {
[{ 'align': [] }], [{ 'align': [] }],
['clean'], ['clean'],
['link'] ['link']
] ]
}; };
@ -47,16 +47,28 @@ export class EditProductComponent implements OnInit {
isScreenSmall: boolean; isScreenSmall: boolean;
productImages: ProductUpload[] = []; productImages: ProductUpload[] = [];
product: ProductsModel; product: ProductsModel;
categories: ProductCategory[] = [];
private _unsubscribeAll: Subject<any> = new Subject<any>(); private _unsubscribeAll: Subject<any> = new Subject<any>();
constructor(private _tesoMediaWatcherService: tesoMediaWatcherService, constructor(private _tesoMediaWatcherService: tesoMediaWatcherService,
private confirmBoxEvokeService: ConfirmBoxEvokeService, private confirmBoxEvokeService: ConfirmBoxEvokeService, private changeDetector: ChangeDetectorRef,
private router: Router, private route: ActivatedRoute, private productServie: ProductsService) { private router: Router, private route: ActivatedRoute, private _productService: ProductsService) {
var snapshot = route.snapshot; var snapshot = route.snapshot;
this.productID = snapshot.paramMap.get('id'); this.productID = snapshot.paramMap.get('id');
this.product = productServie.getProduct(this.productID); this.product = _productService.getProduct(this.productID);
if(this.product == null){ if (this.product == null) {
this.router.navigate(['products']); 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 { ngOnInit(): void {
@ -85,16 +97,6 @@ export class EditProductComponent implements OnInit {
this._unsubscribeAll.complete(); 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) { onFileSelected(event) {
const file: File = event.target.files[0]; const file: File = event.target.files[0];
@ -105,6 +107,7 @@ export class EditProductComponent implements OnInit {
file: file, file: file,
highlight: true, highlight: true,
imageSRC: "", imageSRC: "",
id: `${this.productImages.length + 1}`,
}; };
let reader = new FileReader(); let reader = new FileReader();
reader.onload = (event: any) => { reader.onload = (event: any) => {
@ -112,6 +115,13 @@ export class EditProductComponent implements OnInit {
} }
reader.readAsDataURL(file); reader.readAsDataURL(file);
var prodImage: ProductImages = {
id: productImage.id,
productID: '%local%',
path: productImage.imageSRC
};
this.product.images.push(prodImage);
this.productImages.push(productImage); this.productImages.push(productImage);
} else { } else {
@ -119,6 +129,7 @@ export class EditProductComponent implements OnInit {
file: file, file: file,
highlight: false, highlight: false,
imageSRC: "", imageSRC: "",
id: `${this.productImages.length + 1}`,
}; };
let reader = new FileReader(); let reader = new FileReader();
@ -127,6 +138,12 @@ export class EditProductComponent implements OnInit {
} }
reader.readAsDataURL(file); reader.readAsDataURL(file);
var prodImage: ProductImages = {
id: productImage.id,
productID: '%local%',
path: productImage.imageSRC
};
this.product.images.push(prodImage);
this.productImages.push(productImage); this.productImages.push(productImage);
} }
} else { } else {
@ -134,12 +151,15 @@ export class EditProductComponent implements OnInit {
} }
} }
removeImage(item: ProductUpload) { removeImage(item: ProductImages) {
this.productImages = this.productImages.filter((e) => e != item); 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() { 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(); this.confirmBoxEvokeService.warning("Add Product Images", "To add a new product, you must add at least one image to support it", "OK").subscribe();
} else { } else {

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

@ -28,7 +28,7 @@
<strong class="tileHead">Product Name </strong> <strong class="tileHead">Product Name </strong>
</mat-label> </mat-label>
<mat-form-field appearance="fill" style="width: 350px;"> <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> </mat-form-field>
</div> </div>
<div style="display:flex;"> <div style="display:flex;">
@ -36,7 +36,7 @@
<strong class="tileHead">Product Category</strong> <strong class="tileHead">Product Category</strong>
</mat-label> </mat-label>
<mat-form-field appearance="fill" style="width: 350px;"> <mat-form-field appearance="fill" style="width: 350px;">
<mat-select [(ngModel)]="selected"> <mat-select [(ngModel)]="newProduct.categoryID">
<mat-option *ngFor="let category of categories" <mat-option *ngFor="let category of categories"
[value]="category.catCode"> [value]="category.catCode">
{{category.catName}} {{category.catName}}
@ -50,7 +50,7 @@
</mat-label> </mat-label>
<mat-form-field appearance="fill" style="width: 350px;"> <mat-form-field appearance="fill" style="width: 350px;">
<span matPrefix>GH¢ &nbsp;</span> <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> </mat-form-field>
</div> </div>
@ -146,7 +146,7 @@
<mat-label style="margin-right: 20px;"> <mat-label style="margin-right: 20px;">
<strong>Product Name </strong> <strong>Product Name </strong>
</mat-label> </mat-label>
<input matInput style="margin-left:10px;" type="text"> <input matInput style="margin-left:10px;" type="text" [(ngModel)]="newProduct.productName">
</mat-form-field> </mat-form-field>
</div> </div>
<div style="display: flex;justify-content: space-between;"> <div style="display: flex;justify-content: space-between;">
@ -154,7 +154,7 @@
<mat-label style="margin-right: 20px;"> <mat-label style="margin-right: 20px;">
<strong>Product Category</strong> <strong>Product Category</strong>
</mat-label> </mat-label>
<mat-select [(ngModel)]="selected"> <mat-select [(ngModel)]="newProduct.categoryID">
<mat-option *ngFor="let category of categories" <mat-option *ngFor="let category of categories"
[value]="category.catCode"> [value]="category.catCode">
{{category.catName}} {{category.catName}}
@ -168,7 +168,7 @@
<strong> Price </strong> <strong> Price </strong>
</mat-label> </mat-label>
<span matSuffix>GH¢ &nbsp;</span> <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> </mat-form-field>
</div> </div>
@ -180,7 +180,7 @@
</div> </div>
</div> </div>
<div class="column"> <div class="column">
<quill-editor [modules]="quillConfig" [(ngModel)]="htmlRequest" <quill-editor [modules]="quillConfig" [(ngModel)]="newProduct.productDesc"
placeholder="Further details"> placeholder="Further details">
</quill-editor> </quill-editor>
@ -211,7 +211,7 @@
</button> </button>
<mat-menu #actionsMenu="matMenu"> <mat-menu #actionsMenu="matMenu">
<button (click)="takePhoto.click()" mat-menu-item>Take Photo</button> <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> </mat-menu>
</div> </div>
<div class="imageList"> <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 { ConfirmBoxEvokeService } from '@costlydeveloper/ngx-awesome-popup';
import { ProductsService } from '../products.service'; import { ProductsService } from '../products.service';
import { ProductCategory, ProductsModel } from 'app/models/productsModel'; 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 = { const modules = {
toolbar: [ toolbar: [
['bold', 'italic', 'underline', 'strike'], ['bold', 'italic', 'underline', 'strike'],
@ -25,7 +32,7 @@ const modules = {
[{ 'align': [] }], [{ 'align': [] }],
['clean'], ['clean'],
['link'] ['link']
] ]
}; };
@ -44,7 +51,7 @@ export class NewProductComponent implements OnInit, OnDestroy {
isScreenSmall: boolean; isScreenSmall: boolean;
productImages: ProductUpload[] = []; productImages: ProductUpload[] = [];
categories: ProductCategory[] = []; categories: ProductCategory[] = [];
newProduct: ProductsModel={ newProduct: ProductsModel = {
productName: '', productName: '',
businessID: '', businessID: '',
productDesc: '', productDesc: '',
@ -55,102 +62,151 @@ export class NewProductComponent implements OnInit, OnDestroy {
images: [] images: []
}; };
private _unsubscribeAll: Subject < any > = new Subject<any>(); private _unsubscribeAll: Subject<any> = new Subject<any>();
constructor(private _tesoMediaWatcherService: tesoMediaWatcherService, constructor(private _tesoMediaWatcherService: tesoMediaWatcherService,
private confirmBoxEvokeService: ConfirmBoxEvokeService, private changeDetector: ChangeDetectorRef,
private changeDetector:ChangeDetectorRef, private _productService: ProductsService, private router: Router,
private _productService: ProductsService) { } public dialog: MatDialog, private confirmBoxEvokeService: ConfirmBoxEvokeService,
private _authService: AuthService) { }
ngOnInit(): void {
this._tesoMediaWatcherService.onMediaChange$ ngOnInit(): void {
.pipe(takeUntil(this._unsubscribeAll)) this._tesoMediaWatcherService.onMediaChange$
.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');
});
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(); onFileSelected(event) {
// reader.onload = e => this.imageSrc = reader.result;
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); removeImage(item: ProductUpload) {
// } this.productImages = this.productImages.filter((e) => e != item);
// } }
onFileSelected(event) {
const file: File = event.target.files[0]; submit() {
if (file.type.includes("image")) {
console.log(file)
if (this.productImages.length == 0) { if (this.productImages.length == 0) {
var productImage: ProductUpload = { this.confirmBoxEvokeService.warning("Add Product Images", "To add a new product, you must add at least one image to support it", "OK").subscribe();
file: file, }
highlight: true, else if (this.newProduct.categoryID == '' || this.newProduct.productDesc == '' || this.newProduct.productName == '' || this.newProduct.unitPrice == 0) {
imageSRC: "", this.confirmBoxEvokeService.warning("Missing Fields", "To add a new product, you must fill up all fields", "OK").subscribe();
}; }
let reader = new FileReader(); else {
reader.onload = (event: any) => { this.confirmBoxEvokeService.success("Confirm Action", `Are you sure would like to add ${this.newProduct.productName} to your inventory on TESO ?`, "YES", "NO").subscribe(response => {
productImage.imageSRC = event.target.result; if (response.clickedButtonID == "yes") {
} this._productService.createProduct(this.newProduct, this.productImages).subscribe((response: ProductsModel) => {
reader.readAsDataURL(file); 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 { } else {
var productImage: ProductUpload = { const dialogRef = this.dialog.open(CouponDialogComponent, {
file: file, disableClose: true,
highlight: false, hasBackdrop: true,
imageSRC: "", data: { product: this.newProduct },
}; });
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();
}
}
removeImage(item: ProductUpload) {
this.productImages = this.productImages.filter((e) => e != item);
}
submit() { this.newProduct = {
if (this.productImages.length == 0) { productName: '',
this.confirmBoxEvokeService.warning("Add Product Images", "To add a new product, you must add at least one image to support it", "OK").subscribe(); businessID: '',
} else { 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> </mat-icon>
</button> </button>
</div> </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 class="product-price"><span>{{product.unitPrice | currency:"GHS "}}</span></p>
<p>{{product.productDesc | productDescShort}}</p> <p>{{product.productDesc | productDescShort}}</p>
<div style="width: 100%;display: flex;justify-content: center;margin-top:30px;"> <div style="width: 100%;display: flex;justify-content: center;margin-top:30px;">
@ -95,9 +95,9 @@
</div> </div>
</ng-container> </ng-container>
</div> </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" 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"> <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 class="p-8 sm:p-16 border-t text-4xl font-semibold tracking-tight text-center">There are no products!
</div> </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 { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { FormControl } from '@angular/forms'; import { FormControl } from '@angular/forms';
import { MatDialog } from '@angular/material/dialog'; 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 { MatSort } from '@angular/material/sort';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { ConfirmBoxEvokeService } from '@costlydeveloper/ngx-awesome-popup'; 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 { AdminCouponDialogComponent } from '../AdminCouponDialog/admin-coupon-dialog.component';
import { CouponDialogComponent } from '../CouponDialog/coupon-dialog.component'; import { CouponDialogComponent } from '../CouponDialog/coupon-dialog.component';
import { ProductsService } from '../products.service'; import { ProductsService } from '../products.service';
import jwt_decode from 'jwt-decode';
import { AuthService } from 'app/core/auth/auth.service';
@Component({ @Component({
selector: 'app-product-list', selector: 'app-product-list',
@ -27,7 +29,10 @@ export class ProductListComponent implements OnInit, OnDestroy {
isLoading: boolean = true; isLoading: boolean = true;
searchInputControl: FormControl = new FormControl(); searchInputControl: FormControl = new FormControl();
private _unsubscribeAll: Subject<any> = new Subject<any>(); private _unsubscribeAll: Subject<any> = new Subject<any>();
// MatPaginator Output
// pageEvent: PageEvent;
constructor(private router: Router, private _tesoMediaWatcherService: tesoMediaWatcherService, constructor(private router: Router, private _tesoMediaWatcherService: tesoMediaWatcherService,
private _authService: AuthService,
public dialog: MatDialog, private confirmBoxEvokeService: ConfirmBoxEvokeService, private _productService: ProductsService) { public dialog: MatDialog, private confirmBoxEvokeService: ConfirmBoxEvokeService, private _productService: ProductsService) {
} }
@ -74,7 +79,8 @@ export class ProductListComponent implements OnInit, OnDestroy {
this.router.navigate(['products/' + productID]); this.router.navigate(['products/' + productID]);
} }
generateCoupon(product: any) { generateCoupon(product: any) {
if (!true) { const tokenInfo = this.getDecodedAccessToken(this._authService.relevantToken);
if (tokenInfo.businessID != "1TESBU00000000") {
const dialogRef = this.dialog.open(CouponDialogComponent, { const dialogRef = this.dialog.open(CouponDialogComponent, {
disableClose: true, disableClose: true,
hasBackdrop: 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) { deleteProduct(product: any) {
this.confirmBoxEvokeService.danger("Delete Product", this.confirmBoxEvokeService.danger("Delete Product",
"Are you sure you would like to delete this product from your inventory ?", "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) { details(productID: any) {
this.router.navigate(['products/details/' + productID]); 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 { BusinessLookUpComponent } from './BusinessLookUp/business-look-up.component';
import { ProductDescriptionShort } from 'app/pipes/productDescriptionShort.pipe'; import { ProductDescriptionShort } from 'app/pipes/productDescriptionShort.pipe';
import { DetailsProductComponent } from './Details/details-product.component'; 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({ @NgModule({
declarations: [ declarations: [
@ -44,7 +47,9 @@ import { DetailsProductComponent } from './Details/details-product.component';
AdminCouponDialogComponent, AdminCouponDialogComponent,
BusinessLookUpComponent, BusinessLookUpComponent,
ProductDescriptionShort, ProductDescriptionShort,
DetailsProductComponent DetailsProductComponent,
ProductCouponWorthPipe,
ProductNameShort
], ],
imports: [ imports: [
RouterModule.forChild(productsRoutes), RouterModule.forChild(productsRoutes),

32
src/app/pages/admin/Products/products.service.ts

@ -1,11 +1,12 @@
import { Injectable } from '@angular/core'; 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 { BehaviorSubject, Observable } from 'rxjs';
import { tap } from 'rxjs/operators'; import { tap } from 'rxjs/operators';
import { analytics as analyticsData } from 'app/mock-api/dashboards/analytics/data'; import { analytics as analyticsData } from 'app/mock-api/dashboards/analytics/data';
import { environment } from 'environments/environment'; import { environment } from 'environments/environment';
import { ProductsModule } from './products.module'; 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({ @Injectable({
providedIn: 'root' providedIn: 'root'
@ -55,12 +56,17 @@ export class ProductsService {
}) })
); );
} }
filterProducts(productName: string): ProductsModel[] { filterProducts(productName: string): ProductsModel[] {
return this._dataFilterable.getValue().filter(p => p.productName.toLowerCase().includes(productName)); return this._dataFilterable.getValue().filter(p => p.productName.toLowerCase().includes(productName));
// this.products = response; // this.products = response;
} }
getProduct(id: string): ProductsModel { 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; return found;
} }
@ -70,4 +76,22 @@ export class ProductsService {
this._productsCategory.next(response); 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 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> <div>
<h4> <h4>
<mat-icon class="icon-size-5" [svgIcon]="'heroicons_solid:information-circle'"></mat-icon> Business <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 { ComponentFixture, TestBed } from '@angular/core/testing';
import { CouponsComponent } from './coupons.component'; import { TempShopComponent } from './temp-shop.component';
describe('CouponsComponent', () => { describe('TempShopComponent', () => {
let component: CouponsComponent; let component: TempShopComponent;
let fixture: ComponentFixture<CouponsComponent>; let fixture: ComponentFixture<TempShopComponent>;
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ CouponsComponent ] declarations: [ TempShopComponent ]
}) })
.compileComponents(); .compileComponents();
}); });
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(CouponsComponent); fixture = TestBed.createComponent(TempShopComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); 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-image: url('assets/images/bg.jpg');
background-position: center; background-position: center;
background-size: cover; background-size: cover;
} }
.mobileAuth{
background-image: url('assets/images/bg.jpg');
background-position: center;
background-size: cover;
height: 100vh;
}
.welcomeText { .welcomeText {
text-shadow: 2px 2px 20px #C3C76C; 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 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" <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"> 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"> <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"> <div class="w-full sm:w-auto py-8 px-4 sm:p-12 sm:rounded-2xl sm:shadow sm:bg-card">
<!-- Logo --> <!-- Logo -->
<div class="w-20"> <div class="w-20">
@ -27,7 +27,7 @@
<ngx-mat-intl-tel-input [preferredCountries]="['gh', 'ng']" [onlyCountries]="['gh', 'ng']" <ngx-mat-intl-tel-input [preferredCountries]="['gh', 'ng']" [onlyCountries]="['gh', 'ng']"
[enablePlaceholder]="true" [enableSearch]="false" name="phone"> [enablePlaceholder]="true" [enableSearch]="false" name="phone">
</ngx-mat-intl-tel-input> </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> </mat-form-field>
<!-- Actions --> <!-- Actions -->
<div class="inline-flex items-end justify-between w-full mt-3.5"> <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 { transform(value: string): string {
if(value.length > 57){ var content = this.stripHtml(value)
return value.substring(0, 58) + "...."; if (content.length > 57) {
}else{ return content.substring(0, 58) + "....";
return value; } 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 = { 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 = { export const environment = {
production: false, 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 * This import should be commented out in production mode because it will have a negative impact
* on performance if an error is thrown. * 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="assets/fonts/inter/inter.css" rel="stylesheet">
<link href="https://fonts.gstatic.com" rel="preconnect"> <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 --> <!-- Icon fonts -->
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"> <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link rel="manifest" href="manifest.webmanifest">
<meta name="theme-color" content="#1976d2">
</head> </head>
<body> <body>
@ -29,6 +31,7 @@
<!-- App root --> <!-- App root -->
<app-root></app-root> <app-root></app-root>
<noscript>Please enable JavaScript to continue using this application.</noscript>
</body> </body>
<script src="assets/js/jquery.min.js"></script> <script src="assets/js/jquery.min.js"></script>
<script src="assets/js/slick.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