Browse Source

90% UI done

devBranch
Benjamin Arhen 3 years ago
parent
commit
11b28c30e9
  1. 4
      angular.json
  2. 572
      package-lock.json
  3. 8
      package.json
  4. 2
      src/@teso/components/navigation/vertical/components/collapsable/collapsable.component.html
  5. 2
      src/@teso/components/navigation/vertical/components/group/group.component.html
  6. 1
      src/@teso/components/navigation/vertical/styles/appearances/compact.scss
  7. 11
      src/app/app.module.ts
  8. 2
      src/app/app.routing.ts
  9. 363
      src/app/layout/common/settings/settings.component.html
  10. 124
      src/app/layout/common/settings/settings.component.ts
  11. 28
      src/app/layout/common/settings/settings.module.ts
  12. 4
      src/app/layout/common/user/user.component.html
  13. 2
      src/app/layout/layout.module.ts
  14. 18
      src/app/layout/layouts/vertical/compact/compact.component.html
  15. 68
      src/app/layout/navigation/data.ts
  16. 1
      src/app/mock-api/common/user/data.ts
  17. 5
      src/app/models/generalModel.ts
  18. 1
      src/app/pages/admin/Adverts/adverts.component.html
  19. 0
      src/app/pages/admin/Adverts/adverts.component.scss
  20. 15
      src/app/pages/admin/Adverts/adverts.component.ts
  21. 12
      src/app/pages/admin/Adverts/adverts.module.ts
  22. 1
      src/app/pages/admin/Campaigns/campaigns.component.html
  23. 0
      src/app/pages/admin/Campaigns/campaigns.component.scss
  24. 15
      src/app/pages/admin/Campaigns/campaigns.component.ts
  25. 12
      src/app/pages/admin/Campaigns/campaigns.module.ts
  26. 115
      src/app/pages/admin/Coupons/Active/active-coupons.component.html
  27. 6
      src/app/pages/admin/Coupons/Active/active-coupons.component.scss
  28. 12
      src/app/pages/admin/Coupons/Active/active-coupons.component.spec.ts
  29. 23
      src/app/pages/admin/Coupons/Active/active-coupons.component.ts
  30. 134
      src/app/pages/admin/Coupons/AdminNewCoupons/admin-new-coupons.component.html
  31. 11
      src/app/pages/admin/Coupons/AdminNewCoupons/admin-new-coupons.component.scss
  32. 25
      src/app/pages/admin/Coupons/AdminNewCoupons/admin-new-coupons.component.spec.ts
  33. 22
      src/app/pages/admin/Coupons/AdminNewCoupons/admin-new-coupons.component.ts
  34. 115
      src/app/pages/admin/Coupons/Inactive/inactive-coupons.component.html
  35. 6
      src/app/pages/admin/Coupons/Inactive/inactive-coupons.component.scss
  36. 25
      src/app/pages/admin/Coupons/Inactive/inactive-coupons.component.spec.ts
  37. 23
      src/app/pages/admin/Coupons/Inactive/inactive-coupons.component.ts
  38. 120
      src/app/pages/admin/Coupons/NewCoupons/new-coupons.component.html
  39. 11
      src/app/pages/admin/Coupons/NewCoupons/new-coupons.component.scss
  40. 12
      src/app/pages/admin/Coupons/NewCoupons/new-coupons.component.spec.ts
  41. 15
      src/app/pages/admin/Coupons/NewCoupons/new-coupons.component.ts
  42. 33
      src/app/pages/admin/Coupons/coupons.component.html
  43. 213
      src/app/pages/admin/Coupons/coupons.component.ts
  44. 55
      src/app/pages/admin/Coupons/coupons.module.ts
  45. 12
      src/app/pages/admin/Coupons/coupons.routing.ts
  46. 8
      src/app/pages/admin/Followers/followers.component.html
  47. 83
      src/app/pages/admin/Monthly_Desires/ListDesires/list-desires.component.html
  48. 384
      src/app/pages/admin/Monthly_Desires/ListDesires/list-desires.component.scss
  49. 12
      src/app/pages/admin/Monthly_Desires/ListDesires/list-desires.component.spec.ts
  50. 154
      src/app/pages/admin/Monthly_Desires/ListDesires/list-desires.component.ts
  51. 2
      src/app/pages/admin/Monthly_Desires/desires.component.html
  52. 384
      src/app/pages/admin/Monthly_Desires/desires.component.scss
  53. 51
      src/app/pages/admin/Monthly_Desires/desires.module.ts
  54. 36
      src/app/pages/admin/Monthly_Desires/desires.routing.ts
  55. 129
      src/app/pages/admin/Products/AdminCouponDialog/admin-coupon-dialog.component.html
  56. 11
      src/app/pages/admin/Products/AdminCouponDialog/admin-coupon-dialog.component.scss
  57. 23
      src/app/pages/admin/Products/AdminCouponDialog/admin-coupon-dialog.component.ts
  58. 93
      src/app/pages/admin/Products/BusinessLookUp/business-look-up.component.html
  59. 19
      src/app/pages/admin/Products/BusinessLookUp/business-look-up.component.scss
  60. 12
      src/app/pages/admin/Products/BusinessLookUp/business-look-up.component.spec.ts
  61. 121
      src/app/pages/admin/Products/BusinessLookUp/business-look-up.component.ts
  62. 115
      src/app/pages/admin/Products/CouponDialog/coupon-dialog.component.html
  63. 11
      src/app/pages/admin/Products/CouponDialog/coupon-dialog.component.scss
  64. 16
      src/app/pages/admin/Products/CouponDialog/coupon-dialog.component.ts
  65. 215
      src/app/pages/admin/Products/EditProduct/edit-product.component.html
  66. 78
      src/app/pages/admin/Products/EditProduct/edit-product.component.scss
  67. 122
      src/app/pages/admin/Products/EditProduct/edit-product.component.ts
  68. 215
      src/app/pages/admin/Products/NewProduct/new-product.component.html
  69. 78
      src/app/pages/admin/Products/NewProduct/new-product.component.scss
  70. 124
      src/app/pages/admin/Products/NewProduct/new-product.component.ts
  71. 46
      src/app/pages/admin/Products/ProductList/product-list.component.html
  72. 175
      src/app/pages/admin/Products/ProductList/product-list.component.ts
  73. 25
      src/app/pages/admin/Products/products.component.spec.ts
  74. 18
      src/app/pages/admin/Products/products.module.ts
  75. 154
      src/app/pages/auth/sign-in/sign-in.component.html
  76. 62
      src/app/pages/auth/sign-in/sign-in.component.ts
  77. 4
      src/app/pages/auth/sign-in/sign-in.module.ts
  78. 16
      src/app/pipes/urlsanitizer.pipe.ts
  79. 84
      src/assets/styles/splash-screen.css
  80. 4
      src/index.html
  81. 3
      src/styles/styles.scss

4
angular.json

@ -59,8 +59,8 @@
"production": {
"budgets": [{
"type": "initial",
"maximumWarning": "3mb",
"maximumError": "5mb"
"maximumWarning": "10mb",
"maximumError": "15mb"
},
{
"type": "anyComponentStyle",

572
package-lock.json

@ -20,6 +20,7 @@
"@angular/platform-browser": "13.0.2",
"@angular/platform-browser-dynamic": "13.0.2",
"@angular/router": "13.0.2",
"@costlydeveloper/ngx-awesome-popup": "^3.1.3",
"@fullcalendar/angular": "4.4.5-beta",
"@fullcalendar/core": "4.4.2",
"@fullcalendar/daygrid": "4.4.2",
@ -33,16 +34,19 @@
"crypto-js": "3.3.0",
"dayjs": "^1.10.7",
"highlight.js": "11.2.0",
"libphonenumber-js": "^1.9.49",
"lodash-es": "4.17.21",
"moment": "2.29.1",
"ng-apexcharts": "1.5.12",
"ngx-markdown": "^12.1.0",
"ngx-mat-intl-tel-input": "^4.1.0",
"ngx-quill": "14.3.0",
"node.js": "^0.0.1-security",
"perfect-scrollbar": "1.5.2",
"quill": "1.3.7",
"rrule": "2.6.8",
"rxjs": "6.6.7",
"save-dev": "^0.0.1-security",
"source": "^0.0.3",
"source-map": "^0.7.3",
"sourcemap-codec": "^1.4.8",
@ -58,7 +62,7 @@
"@angular-eslint/eslint-plugin-template": "12.3.1",
"@angular-eslint/schematics": "^13.0.1",
"@angular-eslint/template-parser": "12.3.1",
"@angular/cli": "^13.2.2",
"@angular/cli": "^13.2.3",
"@angular/compiler-cli": "13.0.2",
"@tailwindcss/aspect-ratio": "0.2.1",
"@tailwindcss/line-clamp": "0.2.1",
@ -79,7 +83,7 @@
"eslint-plugin-jsdoc": "36.0.8",
"eslint-plugin-prefer-arrow": "1.2.3",
"jasmine-core": "3.8.0",
"karma": "6.3.4",
"karma": "^6.3.16",
"karma-chrome-launcher": "3.1.0",
"karma-coverage": "2.0.3",
"karma-jasmine": "4.0.1",
@ -709,12 +713,12 @@
}
},
"node_modules/@angular-devkit/schematics": {
"version": "13.2.2",
"resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-13.2.2.tgz",
"integrity": "sha512-kXPLTkSzRCeBX+SYUkC6MIrWCkH90Y8hVegR9R5n2YqhCJyE+dZlcx4d/8d3VHtZlzAs0LL8BU+liC3wMIagjA==",
"version": "13.2.3",
"resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-13.2.3.tgz",
"integrity": "sha512-+dyC4iKV0huvpjiuz4uyjLNK3FsCIp/Ghv5lXvhG6yok/dCAubsJItJOxi6G16aVCzG/E9zbsDfm9fNMyVOkgQ==",
"dev": true,
"dependencies": {
"@angular-devkit/core": "13.2.2",
"@angular-devkit/core": "13.2.3",
"jsonc-parser": "3.0.0",
"magic-string": "0.25.7",
"ora": "5.4.1",
@ -726,6 +730,49 @@
"yarn": ">= 1.13.0"
}
},
"node_modules/@angular-devkit/schematics/node_modules/@angular-devkit/core": {
"version": "13.2.3",
"resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-13.2.3.tgz",
"integrity": "sha512-/47RA8qmWzeS60xSdaprIn1MiSv0Iw83t0M9/ENH7irFS5vMAq62NCcwiWXH59pZmvvLbF+7xy/RgYUZLr4nHQ==",
"dev": true,
"dependencies": {
"ajv": "8.9.0",
"ajv-formats": "2.1.1",
"fast-json-stable-stringify": "2.1.0",
"magic-string": "0.25.7",
"rxjs": "6.6.7",
"source-map": "0.7.3"
},
"engines": {
"node": "^12.20.0 || ^14.15.0 || >=16.10.0",
"npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
"yarn": ">= 1.13.0"
},
"peerDependencies": {
"chokidar": "^3.5.2"
},
"peerDependenciesMeta": {
"chokidar": {
"optional": true
}
}
},
"node_modules/@angular-devkit/schematics/node_modules/ajv": {
"version": "8.9.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz",
"integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==",
"dev": true,
"dependencies": {
"fast-deep-equal": "^3.1.1",
"json-schema-traverse": "^1.0.0",
"require-from-string": "^2.0.2",
"uri-js": "^4.2.2"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/@angular-eslint/builder": {
"version": "12.7.0",
"resolved": "https://registry.npmjs.org/@angular-eslint/builder/-/builder-12.7.0.tgz",
@ -1094,16 +1141,16 @@
}
},
"node_modules/@angular/cli": {
"version": "13.2.2",
"resolved": "https://registry.npmjs.org/@angular/cli/-/cli-13.2.2.tgz",
"integrity": "sha512-i9pBm5bVWnabwrsw1Ut84BqGu75+Nol6sReh8NTs7zUdCKNC00kqFBZg/1nGGw0IwL3Q+W7rJMgZ9q0qAtFTXg==",
"version": "13.2.3",
"resolved": "https://registry.npmjs.org/@angular/cli/-/cli-13.2.3.tgz",
"integrity": "sha512-QsakxpdQuO67u4fQNuOASqabYUO9gJb/5CpUGpWbuBzru0/9CMEF1CtXoF4EoDiwa5sJMirz3SJMKhtzFlv1cQ==",
"dev": true,
"hasInstallScript": true,
"dependencies": {
"@angular-devkit/architect": "0.1302.2",
"@angular-devkit/core": "13.2.2",
"@angular-devkit/schematics": "13.2.2",
"@schematics/angular": "13.2.2",
"@angular-devkit/architect": "0.1302.3",
"@angular-devkit/core": "13.2.3",
"@angular-devkit/schematics": "13.2.3",
"@schematics/angular": "13.2.3",
"@yarnpkg/lockfile": "1.1.0",
"ansi-colors": "4.1.1",
"debug": "4.3.3",
@ -1129,6 +1176,64 @@
"yarn": ">= 1.13.0"
}
},
"node_modules/@angular/cli/node_modules/@angular-devkit/architect": {
"version": "0.1302.3",
"resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1302.3.tgz",
"integrity": "sha512-0m8jMKrFfIqsYt33zTUwSmyekyfuS67hna08RQ6USjzWQSE3z4S8ulCUARSjM6AzdMblX+whfy56nJUpT17NSA==",
"dev": true,
"dependencies": {
"@angular-devkit/core": "13.2.3",
"rxjs": "6.6.7"
},
"engines": {
"node": "^12.20.0 || ^14.15.0 || >=16.10.0",
"npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
"yarn": ">= 1.13.0"
}
},
"node_modules/@angular/cli/node_modules/@angular-devkit/core": {
"version": "13.2.3",
"resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-13.2.3.tgz",
"integrity": "sha512-/47RA8qmWzeS60xSdaprIn1MiSv0Iw83t0M9/ENH7irFS5vMAq62NCcwiWXH59pZmvvLbF+7xy/RgYUZLr4nHQ==",
"dev": true,
"dependencies": {
"ajv": "8.9.0",
"ajv-formats": "2.1.1",
"fast-json-stable-stringify": "2.1.0",
"magic-string": "0.25.7",
"rxjs": "6.6.7",
"source-map": "0.7.3"
},
"engines": {
"node": "^12.20.0 || ^14.15.0 || >=16.10.0",
"npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
"yarn": ">= 1.13.0"
},
"peerDependencies": {
"chokidar": "^3.5.2"
},
"peerDependenciesMeta": {
"chokidar": {
"optional": true
}
}
},
"node_modules/@angular/cli/node_modules/ajv": {
"version": "8.9.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz",
"integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==",
"dev": true,
"dependencies": {
"fast-deep-equal": "^3.1.1",
"json-schema-traverse": "^1.0.0",
"require-from-string": "^2.0.2",
"uri-js": "^4.2.2"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/@angular/common": {
"version": "13.0.2",
"resolved": "https://registry.npmjs.org/@angular/common/-/common-13.0.2.tgz",
@ -2980,6 +3085,19 @@
"node": ">=6.9.0"
}
},
"node_modules/@costlydeveloper/ngx-awesome-popup": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/@costlydeveloper/ngx-awesome-popup/-/ngx-awesome-popup-3.1.3.tgz",
"integrity": "sha512-bg5AzpXpnPTf2QbQTgucSk7U8NtvoD0M4tCMOIDFQvoKe+tuUD+tkBVKfX1/j020egd7o7hP5HnMxSEjjkqd4A==",
"dependencies": {
"tslib": "^2.3.0"
},
"peerDependencies": {
"@angular/common": ">8.0.0",
"@angular/core": ">8.0.0",
"rxjs": ">6.0.0"
}
},
"node_modules/@discoveryjs/json-ext": {
"version": "0.5.6",
"resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.6.tgz",
@ -3531,13 +3649,13 @@
}
},
"node_modules/@schematics/angular": {
"version": "13.2.2",
"resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-13.2.2.tgz",
"integrity": "sha512-LfTtu82I7K+BoSzSXV8gA5+ARhtOtOi/qY0uj2782/xzpe+zCbbftgc6MHiSx/a3KO7IStDwf7G6mjqas0v+4g==",
"version": "13.2.3",
"resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-13.2.3.tgz",
"integrity": "sha512-jloooGC7eco9AKxlIMMqFRptJYzZ0jNRBStWOp2dCISg6rmOKqpxbsHLtYFQIT1PnlomSxtKDAgYGQMDi9zhXw==",
"dev": true,
"dependencies": {
"@angular-devkit/core": "13.2.2",
"@angular-devkit/schematics": "13.2.2",
"@angular-devkit/core": "13.2.3",
"@angular-devkit/schematics": "13.2.3",
"jsonc-parser": "3.0.0"
},
"engines": {
@ -3546,6 +3664,58 @@
"yarn": ">= 1.13.0"
}
},
"node_modules/@schematics/angular/node_modules/@angular-devkit/core": {
"version": "13.2.3",
"resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-13.2.3.tgz",
"integrity": "sha512-/47RA8qmWzeS60xSdaprIn1MiSv0Iw83t0M9/ENH7irFS5vMAq62NCcwiWXH59pZmvvLbF+7xy/RgYUZLr4nHQ==",
"dev": true,
"dependencies": {
"ajv": "8.9.0",
"ajv-formats": "2.1.1",
"fast-json-stable-stringify": "2.1.0",
"magic-string": "0.25.7",
"rxjs": "6.6.7",
"source-map": "0.7.3"
},
"engines": {
"node": "^12.20.0 || ^14.15.0 || >=16.10.0",
"npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
"yarn": ">= 1.13.0"
},
"peerDependencies": {
"chokidar": "^3.5.2"
},
"peerDependenciesMeta": {
"chokidar": {
"optional": true
}
}
},
"node_modules/@schematics/angular/node_modules/ajv": {
"version": "8.9.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz",
"integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==",
"dev": true,
"dependencies": {
"fast-deep-equal": "^3.1.1",
"json-schema-traverse": "^1.0.0",
"require-from-string": "^2.0.2",
"uri-js": "^4.2.2"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/@socket.io/base64-arraybuffer": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@socket.io/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
"integrity": "sha512-dOlCBKnDw4iShaIsH/bxujKTM18+2TOAsYz+KSc11Am38H4q5Xw8Bbz97ZYdrVNM+um3p7w86Bvvmcn9q+5+eQ==",
"dev": true,
"engines": {
"node": ">= 0.6.0"
}
},
"node_modules/@tailwindcss/aspect-ratio": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/@tailwindcss/aspect-ratio/-/aspect-ratio-0.2.1.tgz",
@ -4889,15 +5059,6 @@
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
"node_modules/base64-arraybuffer": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz",
"integrity": "sha1-mBjHngWbE1X5fgQooBfIOOkLqBI=",
"dev": true,
"engines": {
"node": ">= 0.6.0"
}
},
"node_modules/base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
@ -6500,33 +6661,36 @@
}
},
"node_modules/engine.io": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-4.1.2.tgz",
"integrity": "sha512-t5z6zjXuVLhXDMiFJPYsPOWEER8B0tIsD3ETgw19S1yg9zryvUfY3Vhtk3Gf4sihw/bQGIqQ//gjvVlu+Ca0bQ==",
"version": "6.1.2",
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.1.2.tgz",
"integrity": "sha512-v/7eGHxPvO2AWsksyx2PUsQvBafuvqs0jJJQ0FdmJG1b9qIvgSbqDRGwNhfk2XHaTTbTXiC4quRE8Q9nRjsrQQ==",
"dev": true,
"dependencies": {
"@types/cookie": "^0.4.1",
"@types/cors": "^2.8.12",
"@types/node": ">=10.0.0",
"accepts": "~1.3.4",
"base64id": "2.0.0",
"cookie": "~0.4.1",
"cors": "~2.8.5",
"debug": "~4.3.1",
"engine.io-parser": "~4.0.0",
"ws": "~7.4.2"
"engine.io-parser": "~5.0.0",
"ws": "~8.2.3"
},
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/engine.io-parser": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-4.0.3.tgz",
"integrity": "sha512-xEAAY0msNnESNPc00e19y5heTPX4y/TJ36gr8t1voOaNmTojP9b3oK3BbJLFufW2XFPQaaijpFewm2g2Um3uqA==",
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.3.tgz",
"integrity": "sha512-BtQxwF27XUNnSafQLvDi0dQ8s3i6VgzSoQMJacpIcGNrlUdfHSKbgm3jmjCVvQluGzqwujQMPAoMai3oYSTurg==",
"dev": true,
"dependencies": {
"base64-arraybuffer": "0.1.4"
"@socket.io/base64-arraybuffer": "~1.0.2"
},
"engines": {
"node": ">=8.0.0"
"node": ">=10.0.0"
}
},
"node_modules/enhanced-resolve": {
@ -7954,9 +8118,9 @@
"dev": true
},
"node_modules/follow-redirects": {
"version": "1.14.7",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz",
"integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==",
"version": "1.14.8",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz",
"integrity": "sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==",
"dev": true,
"funding": [
{
@ -9464,15 +9628,15 @@
]
},
"node_modules/karma": {
"version": "6.3.4",
"resolved": "https://registry.npmjs.org/karma/-/karma-6.3.4.tgz",
"integrity": "sha512-hbhRogUYIulfkBTZT7xoPrCYhRBnBoqbbL4fszWD0ReFGUxU+LYBr3dwKdAluaDQ/ynT9/7C+Lf7pPNW4gSx4Q==",
"version": "6.3.16",
"resolved": "https://registry.npmjs.org/karma/-/karma-6.3.16.tgz",
"integrity": "sha512-nEU50jLvDe5yvXqkEJRf8IuvddUkOY2x5Xc4WXHz6dxINgGDrgD2uqQWeVrJs4hbfNaotn+HQ1LZJ4yOXrL7xQ==",
"dev": true,
"dependencies": {
"body-parser": "^1.19.0",
"braces": "^3.0.2",
"chokidar": "^3.5.1",
"colors": "^1.4.0",
"colors": "1.4.0",
"connect": "^3.7.0",
"di": "^0.0.1",
"dom-serialize": "^2.2.1",
@ -9481,16 +9645,17 @@
"http-proxy": "^1.18.1",
"isbinaryfile": "^4.0.8",
"lodash": "^4.17.21",
"log4js": "^6.3.0",
"log4js": "^6.4.1",
"mime": "^2.5.2",
"minimatch": "^3.0.4",
"mkdirp": "^0.5.5",
"qjobs": "^1.2.0",
"range-parser": "^1.2.1",
"rimraf": "^3.0.2",
"socket.io": "^3.1.0",
"socket.io": "^4.2.0",
"source-map": "^0.6.1",
"tmp": "^0.2.1",
"ua-parser-js": "^0.7.28",
"ua-parser-js": "^0.7.30",
"yargs": "^16.1.1"
},
"bin": {
@ -9597,6 +9762,18 @@
"source-map-support": "^0.5.5"
}
},
"node_modules/karma/node_modules/mkdirp": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"dev": true,
"dependencies": {
"minimist": "^1.2.5"
},
"bin": {
"mkdirp": "bin/cmd.js"
}
},
"node_modules/karma/node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
@ -9782,6 +9959,11 @@
"node": ">= 0.8.0"
}
},
"node_modules/libphonenumber-js": {
"version": "1.9.49",
"resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.9.49.tgz",
"integrity": "sha512-/wEOIONcVboFky+lWlCaF7glm1FhBz11M5PHeCApA+xDdVfmhKjHktHS8KjyGxouV5CSXIr4f3GvLSpJa4qMSg=="
},
"node_modules/license-webpack-plugin": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-4.0.1.tgz",
@ -10581,6 +10763,22 @@
"zone.js": "^0.10.2 || ^0.11.4"
}
},
"node_modules/ngx-mat-intl-tel-input": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/ngx-mat-intl-tel-input/-/ngx-mat-intl-tel-input-4.1.0.tgz",
"integrity": "sha512-9pOEd9YaFkC/rvE32W2okl9GUB6m+6sk5qCA9yv9keXRa7GDkxwX9gUfpkm48n6V6RLHgvh28RJVD/5ek6QGrw==",
"dependencies": {
"tslib": "^2.3.1"
},
"peerDependencies": {
"@angular/common": "~13.0.0",
"@angular/core": "~13.0.0",
"@angular/forms": "~13.0.0",
"@angular/platform-browser": "~13.0.0",
"@angular/platform-browser-dynamic": "~13.0.0",
"libphonenumber-js": "^1.9.41"
}
},
"node_modules/ngx-quill": {
"version": "14.3.0",
"resolved": "https://registry.npmjs.org/ngx-quill/-/ngx-quill-14.3.0.tgz",
@ -12992,6 +13190,11 @@
}
}
},
"node_modules/save-dev": {
"version": "0.0.1-security",
"resolved": "https://registry.npmjs.org/save-dev/-/save-dev-0.0.1-security.tgz",
"integrity": "sha512-k6knZTDNK8PKKbIqnvxiOveJinuw2LcQjqDoaorZWP9M5AR2EPsnpDeSbeoZZ0pHr5ze1uoaKdK8NBGQrJ34Uw=="
},
"node_modules/sax": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
@ -13360,29 +13563,26 @@
}
},
"node_modules/socket.io": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-3.1.2.tgz",
"integrity": "sha512-JubKZnTQ4Z8G4IZWtaAZSiRP3I/inpy8c/Bsx2jrwGrTbKeVU5xd6qkKMHpChYeM3dWZSO0QACiGK+obhBNwYw==",
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.4.1.tgz",
"integrity": "sha512-s04vrBswdQBUmuWJuuNTmXUVJhP0cVky8bBDhdkf8y0Ptsu7fKU2LuLbts9g+pdmAdyMMn8F/9Mf1/wbtUN0fg==",
"dev": true,
"dependencies": {
"@types/cookie": "^0.4.0",
"@types/cors": "^2.8.8",
"@types/node": ">=10.0.0",
"accepts": "~1.3.4",
"base64id": "~2.0.0",
"debug": "~4.3.1",
"engine.io": "~4.1.0",
"socket.io-adapter": "~2.1.0",
"socket.io-parser": "~4.0.3"
"debug": "~4.3.2",
"engine.io": "~6.1.0",
"socket.io-adapter": "~2.3.3",
"socket.io-parser": "~4.0.4"
},
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/socket.io-adapter": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.1.0.tgz",
"integrity": "sha512-+vDov/aTsLjViYTwS9fPy5pEtTkrbEKsw2M+oVSoFGw6OD1IpvlV1VPhUzNbofCQ8oyMbdYJqDtGdmHQK6TdPg==",
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.3.3.tgz",
"integrity": "sha512-Qd/iwn3VskrpNO60BeRyCyr8ZWw9CPZyitW4AQwmRZ8zCiyDiL+znRnWX6tDHXnWn1sJrM1+b6Mn6wEDJJ4aYQ==",
"dev": true
},
"node_modules/socket.io-parser": {
@ -15169,12 +15369,12 @@
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
},
"node_modules/ws": {
"version": "7.4.6",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz",
"integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==",
"version": "8.2.3",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz",
"integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==",
"dev": true,
"engines": {
"node": ">=8.3.0"
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
@ -15657,16 +15857,44 @@
}
},
"@angular-devkit/schematics": {
"version": "13.2.2",
"resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-13.2.2.tgz",
"integrity": "sha512-kXPLTkSzRCeBX+SYUkC6MIrWCkH90Y8hVegR9R5n2YqhCJyE+dZlcx4d/8d3VHtZlzAs0LL8BU+liC3wMIagjA==",
"version": "13.2.3",
"resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-13.2.3.tgz",
"integrity": "sha512-+dyC4iKV0huvpjiuz4uyjLNK3FsCIp/Ghv5lXvhG6yok/dCAubsJItJOxi6G16aVCzG/E9zbsDfm9fNMyVOkgQ==",
"dev": true,
"requires": {
"@angular-devkit/core": "13.2.2",
"@angular-devkit/core": "13.2.3",
"jsonc-parser": "3.0.0",
"magic-string": "0.25.7",
"ora": "5.4.1",
"rxjs": "6.6.7"
},
"dependencies": {
"@angular-devkit/core": {
"version": "13.2.3",
"resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-13.2.3.tgz",
"integrity": "sha512-/47RA8qmWzeS60xSdaprIn1MiSv0Iw83t0M9/ENH7irFS5vMAq62NCcwiWXH59pZmvvLbF+7xy/RgYUZLr4nHQ==",
"dev": true,
"requires": {
"ajv": "8.9.0",
"ajv-formats": "2.1.1",
"fast-json-stable-stringify": "2.1.0",
"magic-string": "0.25.7",
"rxjs": "6.6.7",
"source-map": "0.7.3"
}
},
"ajv": {
"version": "8.9.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz",
"integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==",
"dev": true,
"requires": {
"fast-deep-equal": "^3.1.1",
"json-schema-traverse": "^1.0.0",
"require-from-string": "^2.0.2",
"uri-js": "^4.2.2"
}
}
}
},
"@angular-eslint/builder": {
@ -15902,15 +16130,15 @@
}
},
"@angular/cli": {
"version": "13.2.2",
"resolved": "https://registry.npmjs.org/@angular/cli/-/cli-13.2.2.tgz",
"integrity": "sha512-i9pBm5bVWnabwrsw1Ut84BqGu75+Nol6sReh8NTs7zUdCKNC00kqFBZg/1nGGw0IwL3Q+W7rJMgZ9q0qAtFTXg==",
"version": "13.2.3",
"resolved": "https://registry.npmjs.org/@angular/cli/-/cli-13.2.3.tgz",
"integrity": "sha512-QsakxpdQuO67u4fQNuOASqabYUO9gJb/5CpUGpWbuBzru0/9CMEF1CtXoF4EoDiwa5sJMirz3SJMKhtzFlv1cQ==",
"dev": true,
"requires": {
"@angular-devkit/architect": "0.1302.2",
"@angular-devkit/core": "13.2.2",
"@angular-devkit/schematics": "13.2.2",
"@schematics/angular": "13.2.2",
"@angular-devkit/architect": "0.1302.3",
"@angular-devkit/core": "13.2.3",
"@angular-devkit/schematics": "13.2.3",
"@schematics/angular": "13.2.3",
"@yarnpkg/lockfile": "1.1.0",
"ansi-colors": "4.1.1",
"debug": "4.3.3",
@ -15926,6 +16154,44 @@
"semver": "7.3.5",
"symbol-observable": "4.0.0",
"uuid": "8.3.2"
},
"dependencies": {
"@angular-devkit/architect": {
"version": "0.1302.3",
"resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1302.3.tgz",
"integrity": "sha512-0m8jMKrFfIqsYt33zTUwSmyekyfuS67hna08RQ6USjzWQSE3z4S8ulCUARSjM6AzdMblX+whfy56nJUpT17NSA==",
"dev": true,
"requires": {
"@angular-devkit/core": "13.2.3",
"rxjs": "6.6.7"
}
},
"@angular-devkit/core": {
"version": "13.2.3",
"resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-13.2.3.tgz",
"integrity": "sha512-/47RA8qmWzeS60xSdaprIn1MiSv0Iw83t0M9/ENH7irFS5vMAq62NCcwiWXH59pZmvvLbF+7xy/RgYUZLr4nHQ==",
"dev": true,
"requires": {
"ajv": "8.9.0",
"ajv-formats": "2.1.1",
"fast-json-stable-stringify": "2.1.0",
"magic-string": "0.25.7",
"rxjs": "6.6.7",
"source-map": "0.7.3"
}
},
"ajv": {
"version": "8.9.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz",
"integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==",
"dev": true,
"requires": {
"fast-deep-equal": "^3.1.1",
"json-schema-traverse": "^1.0.0",
"require-from-string": "^2.0.2",
"uri-js": "^4.2.2"
}
}
}
},
"@angular/common": {
@ -17200,6 +17466,14 @@
"to-fast-properties": "^2.0.0"
}
},
"@costlydeveloper/ngx-awesome-popup": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/@costlydeveloper/ngx-awesome-popup/-/ngx-awesome-popup-3.1.3.tgz",
"integrity": "sha512-bg5AzpXpnPTf2QbQTgucSk7U8NtvoD0M4tCMOIDFQvoKe+tuUD+tkBVKfX1/j020egd7o7hP5HnMxSEjjkqd4A==",
"requires": {
"tslib": "^2.3.0"
}
},
"@discoveryjs/json-ext": {
"version": "0.5.6",
"resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.6.tgz",
@ -17629,16 +17903,50 @@
}
},
"@schematics/angular": {
"version": "13.2.2",
"resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-13.2.2.tgz",
"integrity": "sha512-LfTtu82I7K+BoSzSXV8gA5+ARhtOtOi/qY0uj2782/xzpe+zCbbftgc6MHiSx/a3KO7IStDwf7G6mjqas0v+4g==",
"version": "13.2.3",
"resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-13.2.3.tgz",
"integrity": "sha512-jloooGC7eco9AKxlIMMqFRptJYzZ0jNRBStWOp2dCISg6rmOKqpxbsHLtYFQIT1PnlomSxtKDAgYGQMDi9zhXw==",
"dev": true,
"requires": {
"@angular-devkit/core": "13.2.2",
"@angular-devkit/schematics": "13.2.2",
"@angular-devkit/core": "13.2.3",
"@angular-devkit/schematics": "13.2.3",
"jsonc-parser": "3.0.0"
},
"dependencies": {
"@angular-devkit/core": {
"version": "13.2.3",
"resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-13.2.3.tgz",
"integrity": "sha512-/47RA8qmWzeS60xSdaprIn1MiSv0Iw83t0M9/ENH7irFS5vMAq62NCcwiWXH59pZmvvLbF+7xy/RgYUZLr4nHQ==",
"dev": true,
"requires": {
"ajv": "8.9.0",
"ajv-formats": "2.1.1",
"fast-json-stable-stringify": "2.1.0",
"magic-string": "0.25.7",
"rxjs": "6.6.7",
"source-map": "0.7.3"
}
},
"ajv": {
"version": "8.9.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz",
"integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==",
"dev": true,
"requires": {
"fast-deep-equal": "^3.1.1",
"json-schema-traverse": "^1.0.0",
"require-from-string": "^2.0.2",
"uri-js": "^4.2.2"
}
}
}
},
"@socket.io/base64-arraybuffer": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@socket.io/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
"integrity": "sha512-dOlCBKnDw4iShaIsH/bxujKTM18+2TOAsYz+KSc11Am38H4q5Xw8Bbz97ZYdrVNM+um3p7w86Bvvmcn9q+5+eQ==",
"dev": true
},
"@tailwindcss/aspect-ratio": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/@tailwindcss/aspect-ratio/-/aspect-ratio-0.2.1.tgz",
@ -18689,12 +18997,6 @@
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
"base64-arraybuffer": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz",
"integrity": "sha1-mBjHngWbE1X5fgQooBfIOOkLqBI=",
"dev": true
},
"base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
@ -19925,27 +20227,30 @@
}
},
"engine.io": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-4.1.2.tgz",
"integrity": "sha512-t5z6zjXuVLhXDMiFJPYsPOWEER8B0tIsD3ETgw19S1yg9zryvUfY3Vhtk3Gf4sihw/bQGIqQ//gjvVlu+Ca0bQ==",
"version": "6.1.2",
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.1.2.tgz",
"integrity": "sha512-v/7eGHxPvO2AWsksyx2PUsQvBafuvqs0jJJQ0FdmJG1b9qIvgSbqDRGwNhfk2XHaTTbTXiC4quRE8Q9nRjsrQQ==",
"dev": true,
"requires": {
"@types/cookie": "^0.4.1",
"@types/cors": "^2.8.12",
"@types/node": ">=10.0.0",
"accepts": "~1.3.4",
"base64id": "2.0.0",
"cookie": "~0.4.1",
"cors": "~2.8.5",
"debug": "~4.3.1",
"engine.io-parser": "~4.0.0",
"ws": "~7.4.2"
"engine.io-parser": "~5.0.0",
"ws": "~8.2.3"
}
},
"engine.io-parser": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-4.0.3.tgz",
"integrity": "sha512-xEAAY0msNnESNPc00e19y5heTPX4y/TJ36gr8t1voOaNmTojP9b3oK3BbJLFufW2XFPQaaijpFewm2g2Um3uqA==",
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.3.tgz",
"integrity": "sha512-BtQxwF27XUNnSafQLvDi0dQ8s3i6VgzSoQMJacpIcGNrlUdfHSKbgm3jmjCVvQluGzqwujQMPAoMai3oYSTurg==",
"dev": true,
"requires": {
"base64-arraybuffer": "0.1.4"
"@socket.io/base64-arraybuffer": "~1.0.2"
}
},
"enhanced-resolve": {
@ -21012,9 +21317,9 @@
"dev": true
},
"follow-redirects": {
"version": "1.14.7",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz",
"integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==",
"version": "1.14.8",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz",
"integrity": "sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==",
"dev": true
},
"forwarded": {
@ -22112,15 +22417,15 @@
"dev": true
},
"karma": {
"version": "6.3.4",
"resolved": "https://registry.npmjs.org/karma/-/karma-6.3.4.tgz",
"integrity": "sha512-hbhRogUYIulfkBTZT7xoPrCYhRBnBoqbbL4fszWD0ReFGUxU+LYBr3dwKdAluaDQ/ynT9/7C+Lf7pPNW4gSx4Q==",
"version": "6.3.16",
"resolved": "https://registry.npmjs.org/karma/-/karma-6.3.16.tgz",
"integrity": "sha512-nEU50jLvDe5yvXqkEJRf8IuvddUkOY2x5Xc4WXHz6dxINgGDrgD2uqQWeVrJs4hbfNaotn+HQ1LZJ4yOXrL7xQ==",
"dev": true,
"requires": {
"body-parser": "^1.19.0",
"braces": "^3.0.2",
"chokidar": "^3.5.1",
"colors": "^1.4.0",
"colors": "1.4.0",
"connect": "^3.7.0",
"di": "^0.0.1",
"dom-serialize": "^2.2.1",
@ -22129,19 +22434,29 @@
"http-proxy": "^1.18.1",
"isbinaryfile": "^4.0.8",
"lodash": "^4.17.21",
"log4js": "^6.3.0",
"log4js": "^6.4.1",
"mime": "^2.5.2",
"minimatch": "^3.0.4",
"mkdirp": "^0.5.5",
"qjobs": "^1.2.0",
"range-parser": "^1.2.1",
"rimraf": "^3.0.2",
"socket.io": "^3.1.0",
"socket.io": "^4.2.0",
"source-map": "^0.6.1",
"tmp": "^0.2.1",
"ua-parser-js": "^0.7.28",
"ua-parser-js": "^0.7.30",
"yargs": "^16.1.1"
},
"dependencies": {
"mkdirp": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"dev": true,
"requires": {
"minimist": "^1.2.5"
}
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
@ -22348,6 +22663,11 @@
"type-check": "~0.4.0"
}
},
"libphonenumber-js": {
"version": "1.9.49",
"resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.9.49.tgz",
"integrity": "sha512-/wEOIONcVboFky+lWlCaF7glm1FhBz11M5PHeCApA+xDdVfmhKjHktHS8KjyGxouV5CSXIr4f3GvLSpJa4qMSg=="
},
"license-webpack-plugin": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-4.0.1.tgz",
@ -22950,6 +23270,14 @@
"tslib": "^2.1.0"
}
},
"ngx-mat-intl-tel-input": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/ngx-mat-intl-tel-input/-/ngx-mat-intl-tel-input-4.1.0.tgz",
"integrity": "sha512-9pOEd9YaFkC/rvE32W2okl9GUB6m+6sk5qCA9yv9keXRa7GDkxwX9gUfpkm48n6V6RLHgvh28RJVD/5ek6QGrw==",
"requires": {
"tslib": "^2.3.1"
}
},
"ngx-quill": {
"version": "14.3.0",
"resolved": "https://registry.npmjs.org/ngx-quill/-/ngx-quill-14.3.0.tgz",
@ -24748,6 +25076,11 @@
"neo-async": "^2.6.2"
}
},
"save-dev": {
"version": "0.0.1-security",
"resolved": "https://registry.npmjs.org/save-dev/-/save-dev-0.0.1-security.tgz",
"integrity": "sha512-k6knZTDNK8PKKbIqnvxiOveJinuw2LcQjqDoaorZWP9M5AR2EPsnpDeSbeoZZ0pHr5ze1uoaKdK8NBGQrJ34Uw=="
},
"sax": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
@ -25056,26 +25389,23 @@
"dev": true
},
"socket.io": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-3.1.2.tgz",
"integrity": "sha512-JubKZnTQ4Z8G4IZWtaAZSiRP3I/inpy8c/Bsx2jrwGrTbKeVU5xd6qkKMHpChYeM3dWZSO0QACiGK+obhBNwYw==",
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.4.1.tgz",
"integrity": "sha512-s04vrBswdQBUmuWJuuNTmXUVJhP0cVky8bBDhdkf8y0Ptsu7fKU2LuLbts9g+pdmAdyMMn8F/9Mf1/wbtUN0fg==",
"dev": true,
"requires": {
"@types/cookie": "^0.4.0",
"@types/cors": "^2.8.8",
"@types/node": ">=10.0.0",
"accepts": "~1.3.4",
"base64id": "~2.0.0",
"debug": "~4.3.1",
"engine.io": "~4.1.0",
"socket.io-adapter": "~2.1.0",
"socket.io-parser": "~4.0.3"
"debug": "~4.3.2",
"engine.io": "~6.1.0",
"socket.io-adapter": "~2.3.3",
"socket.io-parser": "~4.0.4"
}
},
"socket.io-adapter": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.1.0.tgz",
"integrity": "sha512-+vDov/aTsLjViYTwS9fPy5pEtTkrbEKsw2M+oVSoFGw6OD1IpvlV1VPhUzNbofCQ8oyMbdYJqDtGdmHQK6TdPg==",
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.3.3.tgz",
"integrity": "sha512-Qd/iwn3VskrpNO60BeRyCyr8ZWw9CPZyitW4AQwmRZ8zCiyDiL+znRnWX6tDHXnWn1sJrM1+b6Mn6wEDJJ4aYQ==",
"dev": true
},
"socket.io-parser": {
@ -26398,9 +26728,9 @@
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
},
"ws": {
"version": "7.4.6",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz",
"integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==",
"version": "8.2.3",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz",
"integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==",
"dev": true,
"requires": {}
},

8
package.json

@ -24,6 +24,7 @@
"@angular/platform-browser": "13.0.2",
"@angular/platform-browser-dynamic": "13.0.2",
"@angular/router": "13.0.2",
"@costlydeveloper/ngx-awesome-popup": "^3.1.3",
"@fullcalendar/angular": "4.4.5-beta",
"@fullcalendar/core": "4.4.2",
"@fullcalendar/daygrid": "4.4.2",
@ -37,16 +38,19 @@
"crypto-js": "3.3.0",
"dayjs": "^1.10.7",
"highlight.js": "11.2.0",
"libphonenumber-js": "^1.9.49",
"lodash-es": "4.17.21",
"moment": "2.29.1",
"ng-apexcharts": "1.5.12",
"ngx-markdown": "^12.1.0",
"ngx-mat-intl-tel-input": "^4.1.0",
"ngx-quill": "14.3.0",
"node.js": "^0.0.1-security",
"perfect-scrollbar": "1.5.2",
"quill": "1.3.7",
"rrule": "2.6.8",
"rxjs": "6.6.7",
"save-dev": "^0.0.1-security",
"source": "^0.0.3",
"source-map": "^0.7.3",
"sourcemap-codec": "^1.4.8",
@ -62,7 +66,7 @@
"@angular-eslint/eslint-plugin-template": "12.3.1",
"@angular-eslint/schematics": "^13.0.1",
"@angular-eslint/template-parser": "12.3.1",
"@angular/cli": "^13.2.2",
"@angular/cli": "^13.2.3",
"@angular/compiler-cli": "13.0.2",
"@tailwindcss/aspect-ratio": "0.2.1",
"@tailwindcss/line-clamp": "0.2.1",
@ -83,7 +87,7 @@
"eslint-plugin-jsdoc": "36.0.8",
"eslint-plugin-prefer-arrow": "1.2.3",
"jasmine-core": "3.8.0",
"karma": "6.3.4",
"karma": "^6.3.16",
"karma-chrome-launcher": "3.1.0",
"karma-coverage": "2.0.3",
"karma-jasmine": "4.0.1",

2
src/@teso/components/navigation/vertical/components/collapsable/collapsable.component.html

@ -4,7 +4,7 @@
<!-- Icon -->
<ng-container *ngIf="item.icon">
<mat-icon class="teso-vertical-navigation-item-icon" [ngClass]="item.classes?.icon" [svgIcon]="item.icon"></mat-icon>
<mat-icon style="color: white !important;" class="teso-vertical-navigation-item-icon" [ngClass]="item.classes?.icon" [svgIcon]="item.icon"></mat-icon>
</ng-container>
<!-- Title & Subtitle -->

2
src/@teso/components/navigation/vertical/components/group/group.component.html

@ -5,7 +5,7 @@
<!-- Icon -->
<ng-container *ngIf="item.icon">
<mat-icon class="teso-vertical-navigation-item-icon" [ngClass]="item.classes?.icon" [svgIcon]="item.icon"></mat-icon>
<mat-icon class="teso-vertical-navigation-item-icon" style="color: white !important;" [ngClass]="item.classes?.icon" [svgIcon]="item.icon"></mat-icon>
</ng-container>
<!-- Title & Subtitle -->

1
src/@teso/components/navigation/vertical/styles/appearances/compact.scss

@ -49,6 +49,7 @@ teso-vertical-navigation {
border-radius: 6px;
.teso-vertical-navigation-item-icon {
margin-right: 0;
color: #ffffff !important;
}
.teso-vertical-navigation-item-title-wrapper {
margin-top: 8px;

11
src/app/app.module.ts

@ -12,15 +12,9 @@ import { LayoutModule } from 'app/layout/layout.module';
import { AppComponent } from 'app/app.component';
import { appRoutes } from 'app/app.routing';
import { LocationStrategy, PathLocationStrategy } from '@angular/common';
import { AdvertsComponent } from './pages/admin/Adverts/adverts.component';
import { CampaignsComponent } from './pages/admin/Campaigns/campaigns.component';
import { CouponsComponent } from './pages/admin/Coupons/coupons.component';
import { FollowersComponent } from './pages/admin/Followers/followers.component';
import { DesiresComponent } from './pages/admin/Monthly_Desires/desires.component';
import { ProductsComponent } from './pages/admin/Products/products.component';
import { ProfileComponent } from './pages/admin/Profile/profile.component';
import { SettingsComponent } from './pages/admin/Settings/settings.component';
import { tesoModule } from '@teso/teso.module';
import { NgxAwesomePopupModule,ConfirmBoxConfigModule} from '@costlydeveloper/ngx-awesome-popup';
const routerConfig: ExtraOptions = {
preloadingStrategy : PreloadAllModules,
@ -36,7 +30,8 @@ const routerConfig: ExtraOptions = {
BrowserModule,
BrowserAnimationsModule,
RouterModule.forRoot(appRoutes, routerConfig),
NgxAwesomePopupModule.forRoot(), // Essential, mandatory main module.
ConfirmBoxConfigModule.forRoot(),
// teso, tesoConfig & tesoMockAPI
tesoModule,
tesoConfigModule.forRoot(appConfig),

2
src/app/app.routing.ts

@ -63,8 +63,6 @@ export const appRoutes: Route[] = [
},
children : [
{path: 'dashboard', loadChildren: () => import('app/pages/admin/Dashboard/dashboard.module').then(m => m.DashboardModule)},
{path: 'adverts', loadChildren: () => import('app/pages/admin/Adverts/adverts.module').then(m => m.AdvertsModule)},
{path: 'campaigns', loadChildren: () => import('app/pages/admin/Campaigns/campaigns.module').then(m => m.CampaignsModule)},
{path: 'coupons', loadChildren: () => import('app/pages/admin/Coupons/coupons.module').then(m => m.CouponsModule)},
{path: 'products', loadChildren: () => import('app/pages/admin/Products/products.module').then(m => m.ProductsModule)},
{path: 'followers', loadChildren: () => import('app/pages/admin/Followers/followers.module').then(m => m.FollowersModule)},

363
src/app/layout/common/settings/settings.component.html

@ -1,363 +0,0 @@
<div class="fixed flex items-center justify-center right-0 w-10 h-10 shadow-lg rounded-l-lg z-90 cursor-pointer bg-red-600 bg-opacity-90 print:hidden" [class.lg:right-0]="config.layout === 'centered' || config.layout === 'material'" [class.lg:right-16]="config.layout !== 'centered' && config.layout !== 'material'"
style="top: 275px" (click)="settingsDrawer.toggle()">
<mat-icon class="icon-size-5 text-white animate-spin-slow" [svgIcon]="'heroicons_solid:cog'"></mat-icon>
</div>
<teso-drawer class="w-screen min-w-screen sm:w-100 sm:min-w-100 z-999" fixed [mode]="'over'" [name]="'settingsDrawer'" [position]="'right'" #settingsDrawer>
<div class="flex flex-col w-full overflow-auto bg-card">
<div class="flex flex-row items-center px-6 h-20 min-h-20 text-white bg-primary">
<mat-icon class="icon-size-7 text-current" [svgIcon]="'heroicons_solid:cog'"></mat-icon>
<div class="ml-3 text-2xl font-semibold tracking-tight">Settings</div>
<button class="ml-auto" mat-icon-button (click)="settingsDrawer.close()">
<mat-icon
class="text-current"
[svgIcon]="'heroicons_outline:x'"></mat-icon>
</button>
</div>
<div class="flex flex-col p-6">
<!-- Theme -->
<div class="text-md font-semibold text-secondary">THEME</div>
<div class="grid grid-cols-2 sm:grid-cols-3 gap-3 mt-6">
<ng-container *ngFor="let theme of themes">
<div class="flex items-center justify-center px-4 py-3 rounded-full cursor-pointer ring-inset ring-primary bg-hover" [class.ring-2]="config.theme === theme[0]" (click)="setTheme(theme[0])">
<div class="flex-0 w-3 h-3 rounded-full" [style.background-color]="theme[1].primary"></div>
<div class="ml-2.5 font-medium leading-5 truncate" [class.text-secondary]="config.theme !== theme[0]">
{{theme[0] | titlecase}}
</div>
</div>
</ng-container>
</div>
<hr class="my-8">
<!-- Scheme -->
<div class="text-md font-semibold text-secondary">SCHEME</div>
<div class="grid grid-cols-3 gap-3 justify-items-start mt-6">
<!-- Auto -->
<div class="flex items-center py-3 pl-5 pr-6 rounded-full cursor-pointer ring-inset ring-primary bg-hover" [class.ring-2]="config.scheme === 'auto'" matTooltip="Automatically sets the scheme based on user's operating system's color scheme preference using 'prefer-color-scheme' media query."
(click)="setScheme('auto')">
<div class="flex items-center rounded-full overflow-hidden">
<mat-icon class="icon-size-5" [svgIcon]="'heroicons_solid:lightning-bolt'"></mat-icon>
</div>
<div class="flex items-center ml-2 font-medium leading-5" [class.text-secondary]="config.scheme !== 'auto'">
Auto
</div>
</div>
<!-- Dark -->
<div class="flex items-center py-3 pl-5 pr-6 rounded-full cursor-pointer ring-inset ring-primary bg-hover" [class.ring-2]="config.scheme === 'dark'" (click)="setScheme('dark')">
<div class="flex items-center rounded-full overflow-hidden">
<mat-icon class="icon-size-5" [svgIcon]="'heroicons_solid:moon'"></mat-icon>
</div>
<div class="flex items-center ml-2 font-medium leading-5" [class.text-secondary]="config.scheme !== 'dark'">
Dark
</div>
</div>
<!-- Light -->
<div class="flex items-center py-3 pl-5 pr-6 rounded-full cursor-pointer ring-inset ring-primary bg-hover" [class.ring-2]="config.scheme === 'light'" (click)="setScheme('light')">
<div class="flex items-center rounded-full overflow-hidden">
<mat-icon class="icon-size-5" [svgIcon]="'heroicons_solid:sun'"></mat-icon>
</div>
<div class="flex items-center ml-2 font-medium leading-5" [class.text-secondary]="config.scheme !== 'light'">
Light
</div>
</div>
</div>
<hr class="my-8">
<!-- Layout -->
<div class="text-md font-semibold text-secondary">LAYOUT</div>
<div class="grid grid-cols-3 gap-3 mt-6">
<!-- Empty -->
<div class="flex flex-col cursor-pointer" (click)="setLayout('empty')">
<div class="flex flex-col h-20 rounded-md overflow-hidden border-2 hover:opacity-80" [class.border-primary]="config.layout === 'empty'">
<div class="flex flex-col flex-auto bg-gray-50 dark:bg-gray-900"></div>
</div>
<div class="mt-2 text-md font-medium text-center text-secondary" [class.text-primary]="config.layout === 'empty'">
Empty
</div>
</div>
<!-- Classic -->
<div class="flex flex-col cursor-pointer" (click)="setLayout('classic')">
<div class="flex h-20 rounded-md overflow-hidden border-2 hover:opacity-80" [class.border-primary]="config.layout === 'classic'">
<div class="w-8 bg-gray-100 dark:bg-gray-800">
<div class="mt-3 mx-1.5 space-y-1">
<div class="h-1 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
<div class="h-1 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
<div class="h-1 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
<div class="h-1 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
<div class="h-1 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
</div>
</div>
<div class="flex flex-col flex-auto border-l">
<div class="h-3 bg-gray-100 dark:bg-gray-800">
<div class="flex items-center justify-end h-full mr-1.5">
<div class="w-1 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
</div>
</div>
<div class="flex flex-auto border-t bg-gray-50 dark:bg-gray-900"></div>
</div>
</div>
<div class="mt-2 text-md font-medium text-center text-secondary" [class.text-primary]="config.layout === 'classic'">
Classic
</div>
</div>
<!-- Classy -->
<div class="flex flex-col cursor-pointer" (click)="setLayout('classy')">
<div class="flex h-20 rounded-md overflow-hidden border-2 hover:opacity-80" [class.border-primary]="config.layout === 'classy'">
<div class="w-8 bg-gray-100 dark:bg-gray-800">
<div class="flex items-center mt-1 mx-1">
<div class="w-1 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1 h-1 ml-auto rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1 h-1 ml-0.5 rounded-full bg-gray-300 dark:bg-gray-700"></div>
</div>
<div class="w-4 h-4 mt-2.5 mx-auto rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="mt-2 mx-1 space-y-1">
<div class="h-1 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
<div class="h-1 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
<div class="h-1 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
<div class="h-1 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
</div>
</div>
<div class="flex flex-col flex-auto border-l">
<div class="h-3 bg-gray-100 dark:bg-gray-800">
<div class="flex items-center justify-end h-full mr-2">
<div class="w-1 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
</div>
</div>
<div class="flex flex-auto border-t bg-gray-50 dark:bg-gray-900"></div>
</div>
</div>
<div class="mt-2 text-md font-medium text-center text-secondary" [class.text-primary]="config.layout === 'classy'">
Classy
</div>
</div>
<!-- Compact -->
<div class="flex flex-col cursor-pointer" (click)="setLayout('compact')">
<div class="flex h-20 rounded-md overflow-hidden border-2 hover:opacity-80" [class.border-primary]="config.layout === 'compact'">
<div class="w-5 bg-gray-100 dark:bg-gray-800">
<div class="w-3 h-3 mt-2 mx-auto rounded-sm bg-gray-300 dark:bg-gray-700"></div>
<div class="flex flex-col items-center w-full mt-2 space-y-1">
<div class="w-3 h-2.5 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
<div class="w-3 h-2.5 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
<div class="w-3 h-2.5 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
</div>
</div>
<div class="flex flex-col flex-auto border-l">
<div class="h-3 bg-gray-100 dark:bg-gray-800">
<div class="flex items-center justify-end h-full mr-1.5">
<div class="w-1 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
</div>
</div>
<div class="flex flex-auto border-t bg-gray-50 dark:bg-gray-900"></div>
</div>
</div>
<div class="mt-2 text-md font-medium text-center text-secondary" [class.text-primary]="config.layout === 'compact'">
Compact
</div>
</div>
<!-- Dense -->
<div class="flex flex-col cursor-pointer" (click)="setLayout('dense')">
<div class="flex h-20 rounded-md overflow-hidden border-2 hover:opacity-80" [class.border-primary]="config.layout === 'dense'">
<div class="w-4 bg-gray-100 dark:bg-gray-800">
<div class="w-2 h-2 mt-2 mx-auto rounded-sm bg-gray-300 dark:bg-gray-700"></div>
<div class="flex flex-col items-center w-full mt-2 space-y-1">
<div class="w-2 h-2 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
<div class="w-2 h-2 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
<div class="w-2 h-2 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
</div>
</div>
<div class="flex flex-col flex-auto border-l">
<div class="h-3 bg-gray-100 dark:bg-gray-800">
<div class="flex items-center justify-end h-full mr-1.5">
<div class="w-1 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
</div>
</div>
<div class="flex flex-auto border-t bg-gray-50 dark:bg-gray-900"></div>
</div>
</div>
<div class="mt-2 text-md font-medium text-center text-secondary" [class.text-primary]="config.layout === 'dense'">
Dense
</div>
</div>
<!-- Futuristic -->
<div class="flex flex-col cursor-pointer" (click)="setLayout('futuristic')">
<div class="flex h-20 rounded-md overflow-hidden border-2 hover:opacity-80" [class.border-primary]="config.layout === 'futuristic'">
<div class="w-8 bg-gray-100 dark:bg-gray-800">
<div class="flex flex-col flex-auto h-full py-3 px-1.5 space-y-1">
<div class="h-1 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
<div class="h-1 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
<div class="h-1 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
<div class="flex-auto"></div>
<div class="h-1 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
</div>
</div>
<div class="flex flex-col flex-auto border-l">
<div class="h-3 bg-gray-100 dark:bg-gray-800">
<div class="flex items-center justify-end h-full mr-1.5">
<div class="w-1 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
</div>
</div>
<div class="flex flex-auto border-t bg-gray-50 dark:bg-gray-900"></div>
</div>
</div>
<div class="mt-2 text-md font-medium text-center text-secondary" [class.text-primary]="config.layout === 'futuristic'">
Futuristic
</div>
</div>
<!-- Thin -->
<div class="flex flex-col cursor-pointer" (click)="setLayout('thin')">
<div class="flex h-20 rounded-md overflow-hidden border-2 hover:opacity-80" [class.border-primary]="config.layout === 'thin'">
<div class="w-3 bg-gray-100 dark:bg-gray-800">
<div class="w-1.5 h-1.5 mt-2 mx-auto rounded-sm bg-gray-300 dark:bg-gray-700"></div>
<div class="flex flex-col items-center w-full mt-2 space-y-1">
<div class="w-1.5 h-1.5 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1.5 h-1.5 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1.5 h-1.5 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1.5 h-1.5 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1.5 h-1.5 rounded-full bg-gray-300 dark:bg-gray-700"></div>
</div>
</div>
<div class="flex flex-col flex-auto border-l">
<div class="h-3 bg-gray-100 dark:bg-gray-800">
<div class="flex items-center justify-end h-full mr-1.5">
<div class="w-1 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
</div>
</div>
<div class="flex flex-auto border-t bg-gray-50 dark:bg-gray-900"></div>
</div>
</div>
<div class="mt-2 text-md font-medium text-center text-secondary" [class.text-primary]="config.layout === 'thin'">
Thin
</div>
</div>
<div class="col-span-2"></div>
<!-- Centered -->
<div class="flex flex-col cursor-pointer" (click)="setLayout('centered')">
<div class="flex h-20 rounded-md overflow-hidden border-2 hover:opacity-80" [class.border-primary]="config.layout === 'centered'">
<div class="flex flex-col flex-auto my-1 mx-2 border rounded-md overflow-hidden">
<div class="flex items-center h-3 bg-gray-100 dark:bg-gray-800">
<div class="flex ml-1.5">
<div class="w-1 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-3 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-3 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-3 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
</div>
<div class="flex items-center justify-end ml-auto mr-1.5">
<div class="w-1 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
</div>
</div>
<div class="flex flex-auto border-t bg-gray-50 dark:bg-gray-900"></div>
</div>
</div>
<div class="mt-2 text-md font-medium text-center text-secondary" [class.text-primary]="config.layout === 'centered'">
Centered
</div>
</div>
<!-- Enterprise -->
<div class="flex flex-col cursor-pointer" (click)="setLayout('enterprise')">
<div class="flex flex-col h-20 rounded-md overflow-hidden border-2 hover:opacity-80" [class.border-primary]="config.layout === 'enterprise'">
<div class="flex items-center h-3 px-2 bg-gray-100 dark:bg-gray-800">
<div class="w-2 h-2 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="flex items-center justify-end ml-auto space-x-1">
<div class="w-1 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
</div>
</div>
<div class="flex items-center h-3 px-2 border-t border-b space-x-1 bg-gray-100 dark:bg-gray-800">
<div class="w-3 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-3 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-3 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-3 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
</div>
<div class="flex flex-col flex-auto my-1 mx-2 border rounded overflow-hidden">
<div class="flex flex-auto bg-gray-50 dark:bg-gray-900"></div>
</div>
</div>
<div class="mt-2 text-md font-medium text-center text-secondary" [class.text-primary]="config.layout === 'enterprise'">
Enterprise
</div>
</div>
<!-- Material -->
<div class="flex flex-col cursor-pointer" (click)="setLayout('material')">
<div class="flex flex-col h-20 rounded-md overflow-hidden border-2 hover:opacity-80" [class.border-primary]="config.layout === 'material'">
<div class="flex flex-col flex-auto my-1 mx-2 border rounded overflow-hidden">
<div class="flex items-center h-4 px-2 bg-gray-100 dark:bg-gray-800">
<div class="w-2 h-2 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="flex items-center justify-end ml-auto space-x-1">
<div class="w-1 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
</div>
</div>
<div class="flex items-center h-2 px-2 space-x-1 bg-gray-100 dark:bg-gray-800">
<div class="w-3 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-3 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-3 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-3 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
</div>
<div class="flex flex-auto border-t bg-gray-50 dark:bg-gray-900"></div>
</div>
</div>
<div class="mt-2 text-md font-medium text-center text-secondary" [class.text-primary]="config.layout === 'material'">
Material
</div>
</div>
<!-- Modern -->
<div class="flex flex-col cursor-pointer" (click)="setLayout('modern')">
<div class="flex flex-col h-20 rounded-md overflow-hidden border-2 hover:opacity-80" [class.border-primary]="config.layout === 'modern'">
<div class="flex items-center h-4 px-2 border-b bg-gray-100 dark:bg-gray-800">
<div class="w-2 h-2 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="flex items-center h-3 ml-2 space-x-1">
<div class="w-3 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-3 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-3 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
</div>
<div class="flex items-center justify-end ml-auto space-x-1">
<div class="w-1 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
</div>
</div>
<div class="flex flex-col flex-auto">
<div class="flex flex-auto bg-gray-50 dark:bg-gray-900"></div>
</div>
</div>
<div class="mt-2 text-md font-medium text-center text-secondary" [class.text-primary]="config.layout === 'modern'">
Modern
</div>
</div>
</div>
</div>
</div>
</teso-drawer>

124
src/app/layout/common/settings/settings.component.ts

@ -1,124 +0,0 @@
import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
import { Router } from '@angular/router';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { tesoConfigService } from '@teso/services/config';
import { tesoTailwindService } from '@teso/services/tailwind';
import { AppConfig, Scheme, Theme } from 'app/core/config/app.config';
import { Layout } from 'app/layout/layout.types';
@Component({
selector : 'settings',
templateUrl : './settings.component.html',
styles : [
`
settings {
position: static;
display: block;
flex: none;
width: auto;
}
`
],
encapsulation: ViewEncapsulation.None
})
export class SettingsComponent implements OnInit, OnDestroy
{
config: AppConfig;
layout: Layout;
scheme: 'dark' | 'light';
theme: string;
themes: [string, any][] = [];
private _unsubscribeAll: Subject<any> = new Subject<any>();
/**
* Constructor
*/
constructor(
private _router: Router,
private _tesoConfigService: tesoConfigService,
private _tesoTailwindService: tesoTailwindService
)
{
}
// -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On init
*/
ngOnInit(): void
{
// Get the themes
this._tesoTailwindService.tailwindConfig$
.pipe(takeUntil(this._unsubscribeAll))
.subscribe((config) => {
this.themes = Object.entries(config.themes);
});
// Subscribe to config changes
this._tesoConfigService.config$
.pipe(takeUntil(this._unsubscribeAll))
.subscribe((config: AppConfig) => {
// Store the config
this.config = config;
});
}
/**
* On destroy
*/
ngOnDestroy(): void
{
// Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.complete();
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Set the layout on the config
*
* @param layout
*/
setLayout(layout: string): void
{
// Clear the 'layout' query param to allow layout changes
this._router.navigate([], {
queryParams : {
layout: null
},
queryParamsHandling: 'merge'
}).then(() => {
// Set the config
this._tesoConfigService.config = {layout};
});
}
/**
* Set the scheme on the config
*
* @param scheme
*/
setScheme(scheme: Scheme): void
{
this._tesoConfigService.config = {scheme};
}
/**
* Set the theme on the config
*
* @param theme
*/
setTheme(theme: Theme): void
{
this._tesoConfigService.config = {theme};
}
}

28
src/app/layout/common/settings/settings.module.ts

@ -1,28 +0,0 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';
import { MatIconModule } from '@angular/material/icon';
import { MatTooltipModule } from '@angular/material/tooltip';
import { tesoDrawerModule } from '@teso/components/drawer';
import { SettingsComponent } from 'app/layout/common/settings/settings.component';
import { MatButtonModule } from '@angular/material/button';
@NgModule({
declarations: [
SettingsComponent
],
imports: [
CommonModule,
RouterModule,
MatIconModule,
MatTooltipModule,
tesoDrawerModule,
MatButtonModule
],
exports : [
SettingsComponent
]
})
export class SettingsModule
{
}

4
src/app/layout/common/user/user.component.html

@ -35,10 +35,6 @@
<mat-icon [svgIcon]="'heroicons_outline:user-circle'"></mat-icon>
<span>Profile</span>
</button>
<button mat-menu-item>
<mat-icon [svgIcon]="'heroicons_outline:cog'"></mat-icon>
<span>Settings</span>
</button>
<button
mat-menu-item
[matMenuTriggerFor]="userStatus">

2
src/app/layout/layout.module.ts

@ -5,7 +5,6 @@ import { tesoDrawerModule } from '@teso/components/drawer';
import { LayoutComponent } from 'app/layout/layout.component';
import { EmptyLayoutModule } from 'app/layout/layouts/empty/empty.module';
import { CompactLayoutModule } from 'app/layout/layouts/vertical/compact/compact.module';
import { SettingsModule } from 'app/layout/common/settings/settings.module';
import { SharedModule } from 'app/shared/shared.module';
const layoutModules = [
@ -24,7 +23,6 @@ const layoutModules = [
MatTooltipModule,
tesoDrawerModule,
SharedModule,
SettingsModule,
...layoutModules,
],
exports : [

18
src/app/layout/layouts/vertical/compact/compact.component.html

@ -23,22 +23,30 @@
<!-- <languages></languages> -->
<!-- <teso-fullscreen class="hidden md:block"></teso-fullscreen> -->
<goldcoins></goldcoins>
<messages></messages>
<!-- <messages></messages> -->
<notifications></notifications>
<button class="lg:hidden" mat-icon-button (click)="quickChat.toggle()">
<!-- <button class="lg:hidden" mat-icon-button (click)="quickChat.toggle()">
<mat-icon [svgIcon]="'heroicons_outline:chat-alt-2'"></mat-icon>
</button>
</button> -->
<user></user>
</div>
</div>
<!-- Content -->
<div class="flex flex-col flex-auto">
<div class="flex flex-col flex-auto" *ngIf="!isScreenSmall">
<!-- *ngIf="true" hack is required here for router-outlet to work correctly.
Otherwise, layout changes won't be registered and the view won't be updated! -->
<router-outlet *ngIf="true"></router-outlet>
</div>
<div class="flex flex-col flex-auto" *ngIf="isScreenSmall" style="min-height: 100vmax;">
<!-- *ngIf="true" hack is required here for router-outlet to work correctly.
Otherwise, layout changes won't be registered and the view won't be updated! -->
<router-outlet *ngIf="true"></router-outlet>
<!-- <div>
<app-scroll-to-top></app-scroll-to-top>
</div> -->
</div>
<!-- Footer -->
<div class="flex flex-0 items-center justify-start w-full h-14 px-4 md:px-6 z-49 border-t bg-card dark:bg-transparent print:hidden" style="position: sticky;bottom:0;">
<span class="font-medium text-secondary">Teso Ghana &copy; {{currentYear}} | Developed by Bacware Technology</span>
@ -47,4 +55,4 @@
</div>
<!-- Quick chat -->
<quick-chat #quickChat="quickChat"></quick-chat>
<!-- <quick-chat #quickChat="quickChat"></quick-chat> -->

68
src/app/layout/navigation/data.ts

@ -2,15 +2,6 @@
import { tesoNavigationItem } from '@teso/components/navigation';
export const defaultNavigation: tesoNavigationItem[] = [
{
id: 'example',
title: 'Example',
type: 'basic',
icon: 'heroicons_outline:chart-pie',
link: '/dashboard'
}
];
export const compactNavigation: tesoNavigationItem[] = [
{
id: 'dashboard',
title: 'Dashboard',
@ -18,22 +9,59 @@ export const compactNavigation: tesoNavigationItem[] = [
icon: 'heroicons_outline:chart-pie',
link: '/dashboard'
},
{
id: 'adverts',
title: 'Ads',
id: 'coupons',
title: 'Coupons',
type: 'basic',
icon: 'heroicons_outline:credit-card',
link: '/coupons'
},
{
id: 'products',
title: 'Products',
type: 'basic',
icon: 'heroicons_outline:shopping-bag',
link: '/products'
},
{
id: 'followers',
title: 'Followers',
type: 'basic',
icon: 'heroicons_outline:users',
link: '/followers'
},
{
id: 'desire',
title: 'Monthly Desires',
type: 'basic',
icon: 'heroicons_outline:play',
link: '/adverts'
icon: 'heroicons_outline:heart',
link: '/desires'
},
];
export const compactNavigation: tesoNavigationItem[] = [
{
id: 'campaigns',
title: 'Campaigns',
id: 'dashboard',
title: 'Dashboard',
type: 'basic',
icon: 'mat_outline:ads_click',
link: '/campaigns'
icon: 'heroicons_outline:chart-pie',
link: '/dashboard'
},
// {
// id: 'adverts',
// title: 'Ads',
// type: 'basic',
// icon: 'heroicons_outline:play',
// link: '/adverts'
// },
// {
// id: 'campaigns',
// title: 'Campaigns',
// type: 'basic',
// icon: 'mat_outline:ads_click',
// link: '/campaigns'
// },
{
id: 'coupons',
title: 'Coupons',
@ -54,7 +82,7 @@ export const compactNavigation: tesoNavigationItem[] = [
type: 'basic',
icon: 'heroicons_outline:users',
link: '/followers'
},
},
{
id: 'desire',
title: 'Monthly Desires',

1
src/app/mock-api/common/user/data.ts

@ -3,6 +3,5 @@ export const user = {
id : 'cfaad35d-07a3-4447-a6c3-d8c3d54fd5df',
name : 'Dummy User',
email : 'dummy@bacware.com',
avatar: 'assets/images/avatars/brian-hughes.jpg',
status: 'online'
};

5
src/app/models/generalModel.ts

@ -0,0 +1,5 @@
export interface ProductUpload {
highlight: boolean;
file: File;
imageSRC:string;
}

1
src/app/pages/admin/Adverts/adverts.component.html

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

0
src/app/pages/admin/Adverts/adverts.component.scss

15
src/app/pages/admin/Adverts/adverts.component.ts

@ -1,15 +0,0 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-adverts',
templateUrl: './adverts.component.html',
styleUrls: ['./adverts.component.scss']
})
export class AdvertsComponent implements OnInit {
constructor() { }
ngOnInit(): void {
}
}

12
src/app/pages/admin/Adverts/adverts.module.ts

@ -1,12 +0,0 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
@NgModule({
declarations: [],
imports: [
CommonModule
]
})
export class AdvertsModule { }

1
src/app/pages/admin/Campaigns/campaigns.component.html

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

0
src/app/pages/admin/Campaigns/campaigns.component.scss

15
src/app/pages/admin/Campaigns/campaigns.component.ts

@ -1,15 +0,0 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-campaigns',
templateUrl: './campaigns.component.html',
styleUrls: ['./campaigns.component.scss']
})
export class CampaignsComponent implements OnInit {
constructor() { }
ngOnInit(): void {
}
}

12
src/app/pages/admin/Campaigns/campaigns.module.ts

@ -1,12 +0,0 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
@NgModule({
declarations: [],
imports: [
CommonModule
]
})
export class CampaignsModule { }

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

@ -0,0 +1,115 @@
<div class="xl:col-span-2 flex flex-col flex-auto bg-card shadow rounded-2xl overflow-hidden">
<div class="overflow-x-auto mx-6 py-10">
<table class="w-full bg-transparent" mat-table matSort [dataSource]="couponsDataSource" [trackBy]="trackByFn"
#couponsTable>
<!-- Transaction ID -->
<ng-container matColumnDef="product">
<th mat-header-cell mat-sort-header *matHeaderCellDef>
Product
</th>
<td mat-cell *matCellDef="let transaction">
<span class="pr-6 whitespace-wrap">
{{transaction.name}}
</span>
</td>
</ng-container>
<!-- Request Name -->
<ng-container matColumnDef="quantity">
<th mat-header-cell mat-sort-header *matHeaderCellDef>
Quantity Left
</th>
<td mat-cell *matCellDef="let transaction">
<span class="pr-6 whitespace-wrap" style="display: flex;justify-content: center;">
{{transaction.quantity}}
</span>
</td>
</ng-container>
<ng-container matColumnDef="type">
<th mat-header-cell mat-sort-header *matHeaderCellDef>
Coupon Type
</th>
<td mat-cell *matCellDef="let transaction">
<span class="pr-6 whitespace-wrap">
{{transaction.type}}
</span>
</td>
</ng-container>
<ng-container matColumnDef="condition">
<th mat-header-cell mat-sort-header *matHeaderCellDef>
Condition
</th>
<td mat-cell *matCellDef="let transaction">
<span class="pr-6 whitespace-wrap">
{{transaction.condition}}
</span>
</td>
</ng-container>
<ng-container matColumnDef="range">
<th mat-header-cell mat-sort-header *matHeaderCellDef>
Range
</th>
<td mat-cell *matCellDef="let transaction">
<span class="pr-6 whitespace-wrap" style="display: flex;justify-content: center;font-weight: bold;">
{{transaction.range}}
</span>
</td>
</ng-container>
<ng-container matColumnDef="claimed">
<th mat-header-cell mat-sort-header *matHeaderCellDef>
Number Claimed
</th>
<td mat-cell *matCellDef="let transaction">
<span class="pr-6 whitespace-wrap" style="display: flex;justify-content: center;">
{{transaction.claimed}}
</span>
</td>
</ng-container>
<ng-container matColumnDef="status">
<th mat-header-cell mat-sort-header *matHeaderCellDef>
Status
</th>
<td mat-cell *matCellDef="let transaction">
<span class="pr-6 whitespace-wrap">
Active
</span>
</td>
</ng-container>
<ng-container matColumnDef="expiration">
<th mat-header-cell mat-sort-header *matHeaderCellDef>
Expiration
</th>
<td mat-cell *matCellDef="let transaction">
<span class="pr-6 whitespace-wrap">
{{transaction.expiration | date}}
</span>
</td>
</ng-container>
<!-- Amount -->
<ng-container matColumnDef="actions">
<th mat-header-cell mat-sort-header *matHeaderCellDef>
Actions
</th>
<td mat-cell *matCellDef="let transaction">
<div style="display: flex;">
<button class=" sm:inline-flex" mat-flat-button style="background-color: #96260a;color:white;">Cancel
</button>
</div>
</td>
</ng-container>
<!-- 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="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>
</table>
</div>
</div>

6
src/app/pages/admin/Coupons/Active/active-coupons.component.scss

@ -0,0 +1,6 @@
.coupons-row{
color: #003445 !important;
}
.pr-6 {
color: #003445;
}

12
src/app/pages/admin/Campaigns/campaigns.component.spec.ts → src/app/pages/admin/Coupons/Active/active-coupons.component.spec.ts

@ -1,20 +1,20 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { CampaignsComponent } from './campaigns.component';
import { ActiveCouponsComponent } from './active-coupons.component';
describe('CampaignsComponent', () => {
let component: CampaignsComponent;
let fixture: ComponentFixture<CampaignsComponent>;
describe('ActiveCouponsComponent', () => {
let component: ActiveCouponsComponent;
let fixture: ComponentFixture<ActiveCouponsComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ CampaignsComponent ]
declarations: [ ActiveCouponsComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(CampaignsComponent);
fixture = TestBed.createComponent(ActiveCouponsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

23
src/app/pages/admin/Coupons/Active/active-coupons.component.ts

@ -0,0 +1,23 @@
import { ChangeDetectionStrategy, Component, Input, OnInit, ViewChild } from '@angular/core';
import { MatSort } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table';
@Component({
selector: 'activecoupons',
templateUrl: './active-coupons.component.html',
styleUrls: ['./active-coupons.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
exportAs: 'activecoupons'
})
export class ActiveCouponsComponent implements OnInit {
@Input() couponsDataSource: MatTableDataSource<any>;
couponsTableColumns: string[] = ['product', 'quantity', 'type', 'condition', 'range', 'claimed', 'status', 'expiration', 'actions'];
@ViewChild('couponsTable', { read: MatSort }) couponsTableMatSort: MatSort;
constructor() { }
ngOnInit(): void {
}
trackByFn(index: number, item: any): any {
return item.id || index;
}
}

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

@ -0,0 +1,134 @@
<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;">New Coupons </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">Target Business </strong>
</mat-label>
</div>
<div class="column">
<mat-label style="margin-right: 48px;">
Target Name
</mat-label>
<button style="background-color: blue;color:white;margin-right:20px;" mat-button (click)="lookupBusiness()">Search</button>
</div>
</div>
<hr/>
<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">Target Name </strong>
</mat-label>
</div>
<div class="column">
<mat-form-field appearance="fill" style="min-width: 200px;">
<mat-select required>
<mat-option value="low">Product 1</mat-option>
<mat-option value="standard">Product 2</mat-option>
<mat-option value="high">Product 3</mat-option>
<mat-option value="high">Product 4</mat-option>
</mat-select>
</mat-form-field>
</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;">
GH¢200
</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-form-field appearance="fill" style="min-width: 200px;">
<mat-select required>
<mat-option value="low">Freebie</mat-option>
<mat-option value="standard">Discount</mat-option>
<mat-option value="high">Proximity Freebie</mat-option>
<mat-option value="high">Proximity Discount</mat-option>
</mat-select>
</mat-form-field>
</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;"></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 style="margin-left:10px;" type="number">
</mat-form-field>
</div>
</div>
<div style="display: flex;align-items: baseline">
<mat-label style="margin-right: 48px;">
<strong class="tileHead">Percentage Off </strong>
</mat-label>
<mat-form-field appearance="fill" style="margin-right:10px;">
<span matPrefix>From &nbsp;</span>
<input matInput type="number">
<span matSuffix>% &nbsp;</span>
</mat-form-field>
<mat-form-field appearance="fill" style="margin-left:10px;">
<span matPrefix>To &nbsp;</span>
<input matInput type="number">
<span matSuffix>% &nbsp;</span>
</mat-form-field>
</div>
<div style="display: flex;align-items: baseline">
<mat-label style="margin-right: 48px;">
<strong class="tileHead">Coupon Worth </strong>
</mat-label>
<mat-label>
From GH¢200 To GH¢800
</mat-label>
</div>
<div style="display: flex;align-items: baseline;margin-top: 20px;">
<mat-label style="margin-right: 30px;">
<strong class="tileHead">Date Of Expiration </strong>
</mat-label>
<mat-form-field appearance="outline">
<input matInput style="margin-left:10px;" type="date">
</mat-form-field>
<mat-form-field appearance="fill" style="width: 200px;">
<span matPrefix>Time &nbsp;</span>
<input matInput type="time">
</mat-form-field>
</div>
<div class="row" style="display: flex;justify-content:space-around;width:100%;">
<button style="background-color: blue;color:white;margin-right:20px;" mat-button>Generate Coupons</button>
</div>
</div>
</mat-dialog-content>

11
src/app/pages/admin/Coupons/AdminNewCoupons/admin-new-coupons.component.scss

@ -0,0 +1,11 @@
.columns {
display: flex;
flex-direction: row;
flex-wrap: wrap;
width: 100%;
}
.column {
flex: 50%;
font-size: medium;
}

25
src/app/pages/admin/Coupons/AdminNewCoupons/admin-new-coupons.component.spec.ts

@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AdminNewCouponsComponent } from './admin-new-coupons.component';
describe('AdminNewCouponsComponent', () => {
let component: AdminNewCouponsComponent;
let fixture: ComponentFixture<AdminNewCouponsComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ AdminNewCouponsComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(AdminNewCouponsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

22
src/app/pages/admin/Coupons/AdminNewCoupons/admin-new-coupons.component.ts

@ -0,0 +1,22 @@
import { Component, Inject, OnInit } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialog } from '@angular/material/dialog';
import { BusinessLookUpComponent } from '../../Products/BusinessLookUp/business-look-up.component';
@Component({
selector: 'app-admin-new-coupons',
templateUrl: './admin-new-coupons.component.html',
styleUrls: ['./admin-new-coupons.component.scss']
})
export class AdminNewCouponsComponent implements OnInit {
constructor(@Inject(MAT_DIALOG_DATA) public data: { product: any }, public dialog: MatDialog,) { }
ngOnInit(): void {
}
lookupBusiness() {
const dialogRef = this.dialog.open(BusinessLookUpComponent, {
disableClose: true,
hasBackdrop: true,
});
}
}

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

@ -0,0 +1,115 @@
<div class="xl:col-span-2 flex flex-col flex-auto bg-card shadow rounded-2xl overflow-hidden">
<div class="overflow-x-auto mx-6 py-10">
<table class="w-full bg-transparent" mat-table matSort [dataSource]="couponsDataSource" [trackBy]="trackByFn"
#couponsTable>
<!-- Transaction ID -->
<ng-container matColumnDef="product">
<th mat-header-cell mat-sort-header *matHeaderCellDef>
Product
</th>
<td mat-cell *matCellDef="let transaction">
<span class="pr-6 whitespace-wrap">
{{transaction.name}}
</span>
</td>
</ng-container>
<!-- Request Name -->
<ng-container matColumnDef="quantity">
<th mat-header-cell mat-sort-header *matHeaderCellDef>
Quantity Left
</th>
<td mat-cell *matCellDef="let transaction">
<span class="pr-6 whitespace-wrap" style="display: flex;justify-content: center;">
{{transaction.quantity}}
</span>
</td>
</ng-container>
<ng-container matColumnDef="type">
<th mat-header-cell mat-sort-header *matHeaderCellDef>
Coupon Type
</th>
<td mat-cell *matCellDef="let transaction">
<span class="pr-6 whitespace-wrap">
{{transaction.type}}
</span>
</td>
</ng-container>
<ng-container matColumnDef="condition">
<th mat-header-cell mat-sort-header *matHeaderCellDef>
Condition
</th>
<td mat-cell *matCellDef="let transaction">
<span class="pr-6 whitespace-wrap">
{{transaction.condition}}
</span>
</td>
</ng-container>
<ng-container matColumnDef="range">
<th mat-header-cell mat-sort-header *matHeaderCellDef>
Range
</th>
<td mat-cell *matCellDef="let transaction">
<span class="pr-6 whitespace-wrap" style="display: flex;justify-content: center;font-weight: bold;">
{{transaction.range}}
</span>
</td>
</ng-container>
<ng-container matColumnDef="claimed">
<th mat-header-cell mat-sort-header *matHeaderCellDef>
Number Claimed
</th>
<td mat-cell *matCellDef="let transaction">
<span class="pr-6 whitespace-wrap" style="display: flex;justify-content: center;">
{{transaction.claimed}}
</span>
</td>
</ng-container>
<ng-container matColumnDef="status">
<th mat-header-cell mat-sort-header *matHeaderCellDef>
Status
</th>
<td mat-cell *matCellDef="let transaction">
<span class="pr-6 whitespace-wrap">
Expired
</span>
</td>
</ng-container>
<ng-container matColumnDef="expiration">
<th mat-header-cell mat-sort-header *matHeaderCellDef>
Expiration
</th>
<td mat-cell *matCellDef="let transaction">
<span class="pr-6 whitespace-wrap">
{{transaction.expiration | date}}
</span>
</td>
</ng-container>
<!-- Amount -->
<ng-container matColumnDef="actions">
<th mat-header-cell mat-sort-header *matHeaderCellDef>
Actions
</th>
<td mat-cell *matCellDef="let transaction">
<div style="display: flex;">
<button class=" sm:inline-flex" mat-flat-button style="background-color: #0152cc;color: white;">Regenerate
</button>
</div>
</td>
</ng-container>
<!-- 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="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>
</table>
</div>
</div>

6
src/app/pages/admin/Coupons/Inactive/inactive-coupons.component.scss

@ -0,0 +1,6 @@
.coupons-row{
color: #003445 !important;
}
.pr-6 {
color: #003445;
}

25
src/app/pages/admin/Coupons/Inactive/inactive-coupons.component.spec.ts

@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { InactiveCouponsComponent } from './inactive-coupons.component';
describe('InactiveCouponsComponent', () => {
let component: InactiveCouponsComponent;
let fixture: ComponentFixture<InactiveCouponsComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ InactiveCouponsComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(InactiveCouponsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

23
src/app/pages/admin/Coupons/Inactive/inactive-coupons.component.ts

@ -0,0 +1,23 @@
import { ChangeDetectionStrategy, Component, Input, OnInit, ViewChild } from '@angular/core';
import { MatSort } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table';
@Component({
selector: 'inactivecoupons',
templateUrl: './inactive-coupons.component.html',
styleUrls: ['./inactive-coupons.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
exportAs: 'inactivecoupons'
})
export class InactiveCouponsComponent implements OnInit {
@Input() couponsDataSource: MatTableDataSource<any>;
couponsTableColumns: string[] = ['product', 'quantity', 'type', 'condition', 'range', 'claimed', 'status', 'expiration', 'actions'];
@ViewChild('couponsTable', { read: MatSort }) couponsTableMatSort: MatSort;
constructor() { }
ngOnInit(): void {
}
trackByFn(index: number, item: any): any {
return item.id || index;
}
}

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

@ -0,0 +1,120 @@
<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;">New Coupons </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">Target Product </strong>
</mat-label>
</div>
<div class="column">
<mat-form-field appearance="fill" style="min-width: 200px;">
<mat-select required>
<mat-option value="low">Product 1</mat-option>
<mat-option value="standard">Product 2</mat-option>
<mat-option value="high">Product 3</mat-option>
<mat-option value="high">Product 4</mat-option>
</mat-select>
</mat-form-field>
</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;">
GH¢200
</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-form-field appearance="fill" style="min-width: 200px;">
<mat-select required>
<mat-option value="low">Freebie</mat-option>
<mat-option value="standard">Discount</mat-option>
<mat-option value="high">Proximity Freebie</mat-option>
<mat-option value="high">Proximity Discount</mat-option>
</mat-select>
</mat-form-field>
</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;"></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 style="margin-left:10px;" type="number">
</mat-form-field>
</div>
</div>
<div style="display: flex;align-items: baseline">
<mat-label style="margin-right: 48px;">
<strong class="tileHead">Percentage Off </strong>
</mat-label>
<mat-form-field appearance="fill" style="margin-right:10px;">
<span matPrefix>From &nbsp;</span>
<input matInput type="number">
<span matSuffix>% &nbsp;</span>
</mat-form-field>
<mat-form-field appearance="fill" style="margin-left:10px;">
<span matPrefix>To &nbsp;</span>
<input matInput type="number">
<span matSuffix>% &nbsp;</span>
</mat-form-field>
</div>
<div style="display: flex;align-items: baseline">
<mat-label style="margin-right: 48px;">
<strong class="tileHead">Coupon Worth </strong>
</mat-label>
<mat-label>
From GH¢200 To GH¢800
</mat-label>
</div>
<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-form-field appearance="outline">
<input matInput style="margin-left:10px;" type="date">
</mat-form-field>
<mat-form-field appearance="fill">
<span matPrefix>Time &nbsp;</span>
<input matInput type="time">
</mat-form-field>
</div>
<div class="row" style="display: flex;justify-content:space-around;width:100%;">
<button style="background-color: blue;color:white;margin-right:20px;" mat-button>Generate Coupons</button>
</div>
</div>
</mat-dialog-content>

11
src/app/pages/admin/Coupons/NewCoupons/new-coupons.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/Adverts/adverts.component.spec.ts → src/app/pages/admin/Coupons/NewCoupons/new-coupons.component.spec.ts

@ -1,20 +1,20 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AdvertsComponent } from './adverts.component';
import { NewCouponsComponent } from './new-coupons.component';
describe('AdvertsComponent', () => {
let component: AdvertsComponent;
let fixture: ComponentFixture<AdvertsComponent>;
describe('NewCouponsComponent', () => {
let component: NewCouponsComponent;
let fixture: ComponentFixture<NewCouponsComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ AdvertsComponent ]
declarations: [ NewCouponsComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(AdvertsComponent);
fixture = TestBed.createComponent(NewCouponsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

15
src/app/pages/admin/Coupons/NewCoupons/new-coupons.component.ts

@ -0,0 +1,15 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-new-coupons',
templateUrl: './new-coupons.component.html',
styleUrls: ['./new-coupons.component.scss']
})
export class NewCouponsComponent implements OnInit {
constructor() { }
ngOnInit(): void {
}
}

33
src/app/pages/admin/Coupons/coupons.component.html

@ -1 +1,32 @@
<p>coupons works!</p>
<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">
<!-- Title and action buttons -->
<div class="flex items-center justify-between w-full" style="margin-bottom: 20px;">
<div>
<div class="text-3xl font-semibold tracking-tight leading-8">Coupons</div>
<div class="font-medium tracking-tight text-secondary">Here is a list of all your coupons on Teso (850)
</div>
</div>
<div class="flex flex-shrink-0 items-center mt-6 sm:mt-0 sm:ml-4">
<button class="ml-3" mat-flat-button style="margin-right: 10px;background-color: #003445;" (click)="generateCoupons()">
<mat-icon class="icon-size-5" [svgIcon]="'heroicons_solid:plus'" style="color: white !important;"></mat-icon>
<span class="ml-2" style="color: white;">Add Coupon</span>
</button>
</div>
</div>
<mat-tab-group mat-align-tabs="center" style="width: 1440px;">
<mat-tab label="Active Coupons">
<activecoupons [couponsDataSource]="activecouponsData"></activecoupons>
</mat-tab>
<mat-tab label="Inactive Coupons">
<inactivecoupons [couponsDataSource]="inactivecouponsData"></inactivecoupons>
</mat-tab>
</mat-tab-group>
</div>
</div>

213
src/app/pages/admin/Coupons/coupons.component.ts

@ -1,4 +1,11 @@
import { Component, OnInit } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { MatTableDataSource } from '@angular/material/table';
import { Router } from '@angular/router';
import { ConfirmBoxEvokeService } from '@costlydeveloper/ngx-awesome-popup';
import { tesoMediaWatcherService } from '@teso/services/media-watcher';
import { AdminNewCouponsComponent } from './AdminNewCoupons/admin-new-coupons.component';
import { NewCouponsComponent } from './NewCoupons/new-coupons.component';
@Component({
selector: 'app-coupons',
@ -6,10 +13,212 @@ import { Component, OnInit } from '@angular/core';
styleUrls: ['./coupons.component.scss']
})
export class CouponsComponent implements OnInit {
constructor() { }
activecouponsData: MatTableDataSource<any> = new MatTableDataSource();
inactivecouponsData: MatTableDataSource<any> = new MatTableDataSource();
constructor(private router: Router, private _tesoMediaWatcherService: tesoMediaWatcherService,
public dialog: MatDialog, private confirmBoxEvokeService: ConfirmBoxEvokeService) { }
ngOnInit(): void {
this.activecouponsData.data = [
{
id: '2bfa2be5-7688-48d5-b5ac-dc0d9ac97f14',
expiration:"2022-03-15",
name: 'Nadia Mcknight',
range: '60% - 95%',
condition: 'By one get one free',
quantity:17,
type: "Freebie",
claimed: 50,
},
{
id: '77a4383b-b5a5-4943-bc46-04c3431d1566',
expiration:"2022-03-15",
name: 'Best Blackburn',
range: '60% - 95%',
condition: 'By one get one free',
quantity:17,
type: "Freebie",
claimed: 50,
},
{
id: '8bb0f597-673a-47ca-8c77-2f83219cb9af',
expiration:"2022-03-15",
name: 'Duncan Carver',
range: '60% - 95%',
condition: 'By one get one free',
quantity:17,
type: "Freebie",
claimed: 50,
},
{
id: 'c318e31f-1d74-49c5-8dae-2bc5805e2fdb',
expiration:"2022-03-15",
name: 'Martin Richards',
range: '60% - 95%',
condition: 'By one get one free',
quantity:17,
type: "Freebie",
claimed: 50,
},
{
id: '0a8bc517-631a-4a93-aacc-000fa2e8294c',
expiration:"2022-03-15",
name: 'Candice Munoz',
range: '60% - 95%',
condition: 'By one get one free',
quantity:17,
type: "Freebie",
claimed: 50,
},
{
id: 'a4c9945a-757b-40b0-8942-d20e0543cabd',
expiration:"2022-03-15",
name: 'Vickie Mosley',
range: '60% - 95%',
condition: 'By one get one free',
quantity:17,
type: "Freebie",
claimed: 50,
},
{
id: 'b8258ccf-48b5-46a2-9c95-e0bd7580c645',
expiration:"2022-03-15",
name: 'Tina Harris',
range: '60% - 95%',
condition: 'By one get one free',
quantity:17,
type: "Freebie",
claimed: 50,
},
{
id: 'f004ea79-98fc-436c-9ba5-6cfe32fe583d',
expiration:"2022-03-15",
name: 'Holt Manning',
range: '60% - 95%',
condition: 'By one get one free',
quantity:17,
type: "Freebie",
claimed: 50,
},
{
id: '8b69fe2d-d7cc-4a3d-983d-559173e37d37',
expiration:"2022-03-15",
name: 'Misty Ramsey',
range: '60% - 95%',
condition: 'By one get one free',
quantity:17,
type: "Freebie",
claimed: 50,
}
];
this.inactivecouponsData.data = [
{
id: '2bfa2be5-7688-48d5-b5ac-dc0d9ac97f14',
expiration:"2022-03-15",
name: 'Nadia Mcknight',
range: '60% - 95%',
condition: 'By one get one free',
quantity:17,
type: "Freebie",
claimed: 50,
},
{
id: '77a4383b-b5a5-4943-bc46-04c3431d1566',
expiration:"2022-03-15",
name: 'Best Blackburn',
range: '60% - 95%',
condition: 'By one get one free',
quantity:17,
type: "Freebie",
claimed: 50,
},
{
id: '8bb0f597-673a-47ca-8c77-2f83219cb9af',
expiration:"2022-03-15",
name: 'Duncan Carver',
range: '60% - 95%',
condition: 'By one get one free',
quantity:17,
type: "Freebie",
claimed: 50,
},
{
id: 'c318e31f-1d74-49c5-8dae-2bc5805e2fdb',
expiration:"2022-03-15",
name: 'Martin Richards',
range: '60% - 95%',
condition: 'By one get one free',
quantity:17,
type: "Freebie",
claimed: 50,
},
{
id: '0a8bc517-631a-4a93-aacc-000fa2e8294c',
expiration:"2022-03-15",
name: 'Candice Munoz',
range: '60% - 95%',
condition: 'By one get one free',
quantity:17,
type: "Freebie",
claimed: 50,
},
{
id: 'a4c9945a-757b-40b0-8942-d20e0543cabd',
expiration:"2022-03-15",
name: 'Vickie Mosley',
range: '60% - 95%',
condition: 'By one get one free',
quantity:17,
type: "Freebie",
claimed: 50,
},
{
id: 'b8258ccf-48b5-46a2-9c95-e0bd7580c645',
expiration:"2022-03-15",
name: 'Tina Harris',
range: '60% - 95%',
condition: 'By one get one free',
quantity:17,
type: "Freebie",
claimed: 50,
},
{
id: 'f004ea79-98fc-436c-9ba5-6cfe32fe583d',
expiration:"2022-03-15",
name: 'Holt Manning',
range: '60% - 95%',
condition: 'By one get one free',
quantity:17,
type: "Freebie",
claimed: 50,
},
{
id: '8b69fe2d-d7cc-4a3d-983d-559173e37d37',
expiration:"2022-03-15",
name: 'Misty Ramsey',
range: '60% - 95%',
condition: 'By one get one free',
quantity:17,
type: "Freebie",
claimed: 50,
}
]
}
generateCoupons(){
if (!true) {
const dialogRef = this.dialog.open(NewCouponsComponent, {
disableClose: true,
hasBackdrop: true,
});
} else {
const dialogRef = this.dialog.open(AdminNewCouponsComponent, {
disableClose: true,
hasBackdrop: true,
});
}
}
}

55
src/app/pages/admin/Coupons/coupons.module.ts

@ -1,11 +1,62 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { CouponsComponent } from './coupons.component';
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 { RouterModule } from '@angular/router';
import { couponsRoutes } from './coupons.routing';
import { ActiveCouponsComponent } from './Active/active-coupons.component';
import { InactiveCouponsComponent } from './Inactive/inactive-coupons.component';
import { NewCouponsComponent } from './NewCoupons/new-coupons.component';
import { AdminNewCouponsComponent } from './AdminNewCoupons/admin-new-coupons.component';
@NgModule({
declarations: [],
declarations: [
CouponsComponent,
ActiveCouponsComponent,
InactiveCouponsComponent,
NewCouponsComponent,
AdminNewCouponsComponent
],
imports: [
RouterModule.forChild(couponsRoutes),
MatButtonModule,
MatButtonToggleModule,
MatDividerModule,
MatIconModule,
MatFormFieldModule,
MatInputModule,
MatMenuModule,
MatProgressBarModule,
MatRippleModule,
MatSidenavModule,
MatSortModule,
MatTableModule,
MatTabsModule,
MatDialogModule,
TranslocoModule,
SharedModule,
MatPaginatorModule,
MatSelectModule,
MatTooltipModule,
CommonModule
]
})

12
src/app/pages/admin/Coupons/coupons.routing.ts

@ -0,0 +1,12 @@
import { Route } from '@angular/router';
import { CouponsComponent } from './coupons.component';
export const couponsRoutes: Route[] = [
{
path : '',
component: CouponsComponent,
// resolve : {
// data: FollowersResolver
// }
}
];

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

@ -37,17 +37,17 @@
</mat-icon>
<span class="ml-2">Send Coupon</span>
</a>
<a class="flex flex-auto items-center justify-center py-4 hover:bg-hover" [href]="'mailto:' + member.email">
<!-- <a class="flex flex-auto items-center justify-center py-4 hover:bg-hover" [href]="'mailto:' + member.email">
<mat-icon class="icon-size-5 text-hint" [svgIcon]="'heroicons_solid:mail'"></mat-icon>
<span class="ml-2">Send Message</span>
</a>
</div>
<div class="flex items-center w-full border-t divide-x">
</a> -->
<a class="flex flex-auto items-center justify-center py-4 hover:bg-hover" [href]="'tel:' + member.phone">
<mat-icon class="icon-size-5 text-hint" [svgIcon]="'heroicons_solid:user'"></mat-icon>
<span class="ml-2">View Profile</span>
</a>
</div>
<!-- <div class="flex items-center w-full border-t divide-x">
</div> -->
</div>
</ng-container>
</div>

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

@ -0,0 +1,83 @@
<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">
<!-- Title and action buttons -->
<div class="flex items-center justify-between w-full" style="margin-bottom: 20px;" *ngIf="!isScreenSmall">
<div>
<div class="text-3xl font-semibold tracking-tight leading-8">Products</div>
<div class="font-medium tracking-tight text-secondary">Here is a list of all products in your Teso (850)
</div>
</div>
<div class="flex flex-shrink-0 items-center mt-6 sm:mt-0 sm:ml-4">
<!-- Search -->
<mat-form-field class="teso-mat-dense teso-mat-no-subscript teso-mat-rounded min-w-64">
<mat-icon class="icon-size-5" matPrefix [svgIcon]="'heroicons_solid:search'"></mat-icon>
<input matInput [autocomplete]="'off'" [placeholder]="'Look-up product'">
</mat-form-field>
</div>
</div>
<div class="flex items-center justify-between w-full" style="margin-bottom: 20px;" *ngIf="isScreenSmall">
<div>
<div class="text-3xl font-semibold tracking-tight leading-8">Products</div>
<div class="font-medium tracking-tight text-secondary">(850) products
</div>
</div>
<div class="flex-col items-center justify-end mt-6 ml-10">
<button mat-flat-button [color]="'primary'" (click)="createProduct()" style="margin-bottom: 10px;">
<mat-icon class="icon-size-5" [svgIcon]="'heroicons_solid:plus'"></mat-icon>
<span class="ml-2">Add Product</span>
</button>
<!-- Search -->
<mat-form-field class="teso-mat-dense teso-mat-no-subscript teso-mat-rounded ">
<mat-icon class="icon-size-5" matPrefix [svgIcon]="'heroicons_solid:search'"></mat-icon>
<input matInput [autocomplete]="'off'" [placeholder]="'Search'">
</mat-form-field>
</div>
</div>
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6 w-full min-w-0">
<ng-container *ngFor="let product of data.teamproducts">
<!-- <div class="flex flex-col flex-auto items-center bg-card shadow rounded-2xl overflow-hidden"> -->
<div class="product">
<div class="product-content">
<div class="product-img">
<img [src]="product.image" alt="product image" style="max-height:200px;">
</div>
<div class="product-btns">
<button type="button" class="btn-buy" (click)="generateCoupon(product)" *ngIf="product.title.includes('Developer')">
Generate Coupons
</button>
<button type="button" class="btn-cart" (click)="createProduct()" *ngIf="!product.title.includes('Developer')">
Add Product
</button>
</div>
</div>
<div class="product-info">
<div class="product-info-top">
<h2 class="sm-title">@product.CategoryID</h2>
</div>
<a href="#" class="product-name">@product.ProductName</a>
<p class="product-price"><span></span></p>
<p class="product-price"><span>&#8373;</span> @product.UnitPrice</p>
<p>@product.ProductDesc</p>
</div>
<!-- </div> -->
</div>
</ng-container>
</div>
<mat-paginator
class="sticky sm:absolute sm:inset-x-0 sm:bottom-0 border-b sm:border-t sm:border-b-0 z-10 bg-gray-50 dark:bg-transparent"
[pageSizeOptions]="[5, 10, 25, 100]" [showFirstLastButtons]="true"></mat-paginator>
<ng-template #noProducts>
<div class="p-8 sm:p-16 border-t text-4xl font-semibold tracking-tight text-center">There are no monthly desires yet!
</div>
</ng-template>
</div>
</div>

384
src/app/pages/admin/Monthly_Desires/ListDesires/list-desires.component.scss

@ -0,0 +1,384 @@
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
body {
font-family: 'Quicksand', sans-serif;
}
.btn2 {
background-color: transparent;
/* Blue background */
border: none;
/* Remove borders */
color: #003445;
/* White text */
padding: 12px 16px;
/* Some padding */
font-size: 16px;
/* Set a font size */
cursor: pointer;
/* Mouse pointer on hover */
}
/* Darker background on mouse-over */
/* .btn2:hover {
background-color: RoyalBlue;
}*/
.btn-outline-success {
color: #28a745;
background-color: transparent;
background-image: none;
border-color: #28a745 !important;
}
.btn {
display: inline-block;
font-weight: 400;
text-align: center;
white-space: nowrap;
vertical-align: middle;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
border: 1px solid transparent;
padding: .375rem .75rem;
font-size: 1rem;
line-height: 1.5;
border-radius: .25rem;
transition: color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out;
}
.btn-danger {
color: #fff;
background-color: #dc3545;
border-color: #dc3545;
}
.btn {
display: inline-block;
font-weight: 400;
text-align: center;
white-space: nowrap;
vertical-align: middle;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
border: 1px solid transparent;
padding: .375rem .75rem;
font-size: 1rem;
line-height: 1.5;
border-radius: .25rem;
transition: color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out;
}
.form-control {
display: block;
width: 70%;
padding: .375rem .75rem;
font-size: 1rem;
line-height: 1.5;
color: #495057;
background-color: #fff;
background-clip: padding-box;
border: 1px solid #ced4da;
border-radius: .25rem;
transition: border-color .15s ease-in-out, box-shadow .15s ease-in-out;
}
[type=search] {
outline-offset: -2px;
-webkit-appearance: none;
}
button,
input {
overflow: visible;
}
/* Utility stylings */
img {
width: 100%;
display: block;
}
.container {
width: 88vw;
margin: 0 auto;
}
.lg-title,
.md-title,
.sm-title {
font-family: 'Roboto', sans-serif;
padding: 0.6rem 0;
text-transform: capitalize;
}
.lg-title {
font-size: 2.5rem;
font-weight: 500;
text-align: center;
padding: 1.3rem 0;
opacity: 0.9;
}
.md-title {
font-size: 2rem;
font-family: 'Roboto', sans-serif;
}
.sm-title {
font-weight: 300;
font-size: 1rem;
text-transform: uppercase;
}
.text-light {
font-size: 1rem;
font-weight: 600;
line-height: 1.5;
opacity: 0.5;
margin: 0.4rem 0;
}
/* product section */
.products {
background: #f8f9fa;
// padding: 3.2rem 0;
}
.products .text-light {
text-align: center;
width: 70%;
margin: 0.9rem auto;
}
.product {
// margin: 2rem;
position: relative;
}
.product-content {
background: #ededed;
padding: 1rem 0.5rem 1rem 0.5rem;
cursor: pointer;
}
.product-img {
background: rgba(255, 255, 255, 0.5);
box-shadow: 0 0 20px 10px rgba(255, 255, 255, 0.5);
width: 200px;
height: 200px;
margin: 0 auto;
border-radius: 50%;
transition: background 0.5s ease;
}
.product-btns {
display: flex;
justify-content: center;
margin-top: 1.4rem;
opacity: 0;
transition: opacity 0.6s ease;
}
.btn-cart,
.btn-buy {
background: transparent;
border: 1px solid black;
padding: 0.8rem 0;
width: 125px;
font-family: inherit;
text-transform: uppercase;
cursor: pointer;
border: none;
transition: all 0.6s ease;
}
.btn-cart {
background: black;
color: white;
}
.btn-cart:hover {
background: #40c9a2;
}
.btn-buy {
background: white;
}
.btn-buy:hover {
background: #40c9a2;
color: #fff;
}
.product-info {
background: white;
padding: 2rem;
}
.product-info-top {
display: flex;
justify-content: space-between;
align-items: center;
}
.rating span {
color: #40c9a2;
}
.product-name {
color: black;
display: block;
text-decoration: none;
font-size: 1rem;
text-transform: uppercase;
font-weight: bold;
}
.product-price {
padding-top: 0.6rem;
padding-right: 0.6rem;
display: inline-block;
}
.product-price:first-of-type {
text-decoration: line-through;
color: #40c9a2;
}
.product-img img {
transition: transform 0.6s ease;
}
.product:hover .product-img img {
transform: scale(1.1);
}
.product:hover .product-img {
background: #40c9a2;
}
.product:hover .product-btns {
opacity: 1;
}
.off-info .sm-title {
background: #40c9a2;
color: white;
display: inline-block;
padding: 0.5rem;
position: absolute;
top: 0;
left: 0;
writing-mode: vertical-lr;
transform: rotate(180deg);
z-index: 1;
letter-spacing: 3px;
cursor: pointer;
}
/* product collection */
.product-collection {
padding: 3.2rem 0;
}
.product-collection-wrapper {
padding: 3.2rem 0;
}
.flex {
display: flex;
justify-content: center;
align-items: flex-end;
}
.product-col-r-bottom>div:first-child {
background: #eaa001;
}
.product-col-r-bottom>div:last-child {
background: #0090ff;
}
.product-col-content {
text-align: center;
color: white;
}
.product-collection .text-light {
opacity: 1;
font-size: 0.8;
font-weight: 400;
line-height: 1.7;
}
.btn-dark {
background: black;
color: white;
outline: 0;
border-radius: 25px;
padding: 0.7rem 1rem;
border: 0;
margin-top: 1rem;
cursor: pointer;
transition: all 0.6s ease;
font-size: 1rem;
font-family: inherit;
}
.btn-dark:hover {
background: #40c9a2;
}
/* Media Queries */
@media screen and (min-width: 992px) {
.product-items {
display: grid;
grid-template-columns: repeat(2, 1fr);
}
.product-col-r-bottom {
display: grid;
grid-template-columns: repeat(2, 1fr);
}
}
@media screen and (min-width: 1200px) {
.product-items {
grid-template-columns: repeat(3, 1fr);
}
.products .text-light {
width: 50%;
}
}
@media screen and (min-width: 1336px) {
.product-items {
grid-template-columns: repeat(3, 1fr);
}
.product-collection-wrapper {
display: grid;
grid-template-columns: repeat(2, 1fr);
}
.product-col-left {
height: 121.5vh;
}
}

12
src/app/pages/admin/Products/ProductList/product-list.component.spec.ts → src/app/pages/admin/Monthly_Desires/ListDesires/list-desires.component.spec.ts

@ -1,20 +1,20 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ProductListComponent } from './product-list.component';
import { ListDesiresComponent } from './list-desires.component';
describe('ProductListComponent', () => {
let component: ProductListComponent;
let fixture: ComponentFixture<ProductListComponent>;
describe('ListDesiresComponent', () => {
let component: ListDesiresComponent;
let fixture: ComponentFixture<ListDesiresComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ ProductListComponent ]
declarations: [ ListDesiresComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(ProductListComponent);
fixture = TestBed.createComponent(ListDesiresComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

154
src/app/pages/admin/Monthly_Desires/ListDesires/list-desires.component.ts

@ -0,0 +1,154 @@
import { Component, OnInit, ViewChild } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { MatPaginator } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
import { Router } from '@angular/router';
import { ConfirmBoxEvokeService } from '@costlydeveloper/ngx-awesome-popup';
import { tesoMediaWatcherService } from '@teso/services/media-watcher';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { AdminCouponDialogComponent } from '../../Products/AdminCouponDialog/admin-coupon-dialog.component';
import { CouponDialogComponent } from '../../Products/CouponDialog/coupon-dialog.component';
@Component({
selector: 'app-list-desires',
templateUrl: './list-desires.component.html',
styleUrls: ['./list-desires.component.scss']
})
export class ListDesiresComponent implements OnInit {
data: { teamproducts: { id: string; image: string; name: string; email: string; phone: string; title: string; }[]; };
@ViewChild(MatPaginator) private _paginator: MatPaginator;
@ViewChild(MatSort) private _sort: MatSort;
isScreenSmall: boolean;
private _unsubscribeAll: Subject<any> = new Subject<any>();
constructor(private router: Router, private _tesoMediaWatcherService: tesoMediaWatcherService,
public dialog: MatDialog, private confirmBoxEvokeService: ConfirmBoxEvokeService) { }
ngOnDestroy(): void {
// Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.complete();
}
ngOnInit(): void {
// Subscribe to media changes
this._tesoMediaWatcherService.onMediaChange$
.pipe(takeUntil(this._unsubscribeAll))
.subscribe(({ matchingAliases }) => {
// Check if the screen is small
this.isScreenSmall = !matchingAliases.includes('md');
});
this.data = {
teamproducts: [
{
id: '2bfa2be5-7688-48d5-b5ac-dc0d9ac97f14',
image: 'assets/images/brands/bagset.png',
name: 'Nadia Mcknight',
email: 'nadiamcknight@mail.com',
phone: '+1-943-511-2203',
title: 'Project Director'
},
{
id: '77a4383b-b5a5-4943-bc46-04c3431d1566',
image: 'assets/images/brands/bagset.png',
name: 'Best Blackburn',
email: 'blackburn.best@beadzza.me',
phone: '+1-814-498-3701',
title: 'Senior Developer'
},
{
id: '8bb0f597-673a-47ca-8c77-2f83219cb9af',
image: 'assets/images/brands/bagset.png',
name: 'Duncan Carver',
email: 'duncancarver@mail.info',
phone: '+1-968-547-2111',
title: 'Senior Developer'
},
{
id: 'c318e31f-1d74-49c5-8dae-2bc5805e2fdb',
image: 'assets/images/brands/bagset.png',
name: 'Martin Richards',
email: 'martinrichards@mail.biz',
phone: '+1-902-500-2668',
title: 'Junior Developer'
},
{
id: '0a8bc517-631a-4a93-aacc-000fa2e8294c',
image: 'assets/images/brands/bagset.png',
name: 'Candice Munoz',
email: 'candicemunoz@mail.co.uk',
phone: '+1-838-562-2769',
title: 'Lead Designer'
},
{
id: 'a4c9945a-757b-40b0-8942-d20e0543cabd',
image: 'assets/images/brands/bagset.png',
name: 'Vickie Mosley',
email: 'vickiemosley@mail.net',
phone: '+1-939-555-3054',
title: 'Designer'
},
{
id: 'b8258ccf-48b5-46a2-9c95-e0bd7580c645',
image: 'assets/images/brands/bagset.png',
name: 'Tina Harris',
email: 'tinaharris@mail.ca',
phone: '+1-933-464-2431',
title: 'Designer'
},
{
id: 'f004ea79-98fc-436c-9ba5-6cfe32fe583d',
image: 'assets/images/brands/bagset.png',
name: 'Holt Manning',
email: 'holtmanning@mail.org',
phone: '+1-822-531-2600',
title: 'Marketing Manager'
},
{
id: '8b69fe2d-d7cc-4a3d-983d-559173e37d37',
image: 'assets/images/brands/bagset.png',
name: 'Misty Ramsey',
email: 'mistyramsey@mail.us',
phone: '+1-990-457-2106',
title: 'Consultant'
}
]
}
}
createProduct() {
this.router.navigate(['products/create-product']);
}
editProduct(productID: any) {
this.router.navigate(['products/' + productID]);
}
generateCoupon(product: any) {
if (!true) {
const dialogRef = this.dialog.open(CouponDialogComponent, {
disableClose: true,
hasBackdrop: true,
data: { product: product },
});
} else {
const dialogRef = this.dialog.open(AdminCouponDialogComponent, {
disableClose: true,
hasBackdrop: true,
data: { product: product },
});
}
}
deleteProduct(product: any) {
this.confirmBoxEvokeService.danger("Delete Product",
"Are you sure you would like to delete this product from your inventory ?",
"Yes", "No")
.subscribe((response: any) => {
console.log(response)
if (response.clickedButtonID === "yes") {
this.data.teamproducts = this.data.teamproducts.filter((e) => e == product);
}
});
}
}

2
src/app/pages/admin/Monthly_Desires/desires.component.html

@ -1 +1 @@
<p>desires works!</p>
<router-outlet></router-outlet>

384
src/app/pages/admin/Monthly_Desires/desires.component.scss

@ -0,0 +1,384 @@
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
body {
font-family: 'Quicksand', sans-serif;
}
.btn2 {
background-color: transparent;
/* Blue background */
border: none;
/* Remove borders */
color: #003445;
/* White text */
padding: 12px 16px;
/* Some padding */
font-size: 16px;
/* Set a font size */
cursor: pointer;
/* Mouse pointer on hover */
}
/* Darker background on mouse-over */
/* .btn2:hover {
background-color: RoyalBlue;
}*/
.btn-outline-success {
color: #28a745;
background-color: transparent;
background-image: none;
border-color: #28a745 !important;
}
.btn {
display: inline-block;
font-weight: 400;
text-align: center;
white-space: nowrap;
vertical-align: middle;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
border: 1px solid transparent;
padding: .375rem .75rem;
font-size: 1rem;
line-height: 1.5;
border-radius: .25rem;
transition: color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out;
}
.btn-danger {
color: #fff;
background-color: #dc3545;
border-color: #dc3545;
}
.btn {
display: inline-block;
font-weight: 400;
text-align: center;
white-space: nowrap;
vertical-align: middle;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
border: 1px solid transparent;
padding: .375rem .75rem;
font-size: 1rem;
line-height: 1.5;
border-radius: .25rem;
transition: color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out;
}
.form-control {
display: block;
width: 70%;
padding: .375rem .75rem;
font-size: 1rem;
line-height: 1.5;
color: #495057;
background-color: #fff;
background-clip: padding-box;
border: 1px solid #ced4da;
border-radius: .25rem;
transition: border-color .15s ease-in-out, box-shadow .15s ease-in-out;
}
[type=search] {
outline-offset: -2px;
-webkit-appearance: none;
}
button,
input {
overflow: visible;
}
/* Utility stylings */
img {
width: 100%;
display: block;
}
.container {
width: 88vw;
margin: 0 auto;
}
.lg-title,
.md-title,
.sm-title {
font-family: 'Roboto', sans-serif;
padding: 0.6rem 0;
text-transform: capitalize;
}
.lg-title {
font-size: 2.5rem;
font-weight: 500;
text-align: center;
padding: 1.3rem 0;
opacity: 0.9;
}
.md-title {
font-size: 2rem;
font-family: 'Roboto', sans-serif;
}
.sm-title {
font-weight: 300;
font-size: 1rem;
text-transform: uppercase;
}
.text-light {
font-size: 1rem;
font-weight: 600;
line-height: 1.5;
opacity: 0.5;
margin: 0.4rem 0;
}
/* product section */
.products {
background: #f8f9fa;
// padding: 3.2rem 0;
}
.products .text-light {
text-align: center;
width: 70%;
margin: 0.9rem auto;
}
.product {
// margin: 2rem;
position: relative;
}
.product-content {
background: #ededed;
padding: 1rem 0.5rem 1rem 0.5rem;
cursor: pointer;
}
.product-img {
background: rgba(255, 255, 255, 0.5);
box-shadow: 0 0 20px 10px rgba(255, 255, 255, 0.5);
width: 200px;
height: 200px;
margin: 0 auto;
border-radius: 50%;
transition: background 0.5s ease;
}
.product-btns {
display: flex;
justify-content: center;
margin-top: 1.4rem;
opacity: 0;
transition: opacity 0.6s ease;
}
.btn-cart,
.btn-buy {
background: transparent;
border: 1px solid black;
padding: 0.8rem 0;
width: 125px;
font-family: inherit;
text-transform: uppercase;
cursor: pointer;
border: none;
transition: all 0.6s ease;
}
.btn-cart {
background: black;
color: white;
}
.btn-cart:hover {
background: #40c9a2;
}
.btn-buy {
background: white;
}
.btn-buy:hover {
background: #40c9a2;
color: #fff;
}
.product-info {
background: white;
padding: 2rem;
}
.product-info-top {
display: flex;
justify-content: space-between;
align-items: center;
}
.rating span {
color: #40c9a2;
}
.product-name {
color: black;
display: block;
text-decoration: none;
font-size: 1rem;
text-transform: uppercase;
font-weight: bold;
}
.product-price {
padding-top: 0.6rem;
padding-right: 0.6rem;
display: inline-block;
}
.product-price:first-of-type {
text-decoration: line-through;
color: #40c9a2;
}
.product-img img {
transition: transform 0.6s ease;
}
.product:hover .product-img img {
transform: scale(1.1);
}
.product:hover .product-img {
background: #40c9a2;
}
.product:hover .product-btns {
opacity: 1;
}
.off-info .sm-title {
background: #40c9a2;
color: white;
display: inline-block;
padding: 0.5rem;
position: absolute;
top: 0;
left: 0;
writing-mode: vertical-lr;
transform: rotate(180deg);
z-index: 1;
letter-spacing: 3px;
cursor: pointer;
}
/* product collection */
.product-collection {
padding: 3.2rem 0;
}
.product-collection-wrapper {
padding: 3.2rem 0;
}
.flex {
display: flex;
justify-content: center;
align-items: flex-end;
}
.product-col-r-bottom>div:first-child {
background: #eaa001;
}
.product-col-r-bottom>div:last-child {
background: #0090ff;
}
.product-col-content {
text-align: center;
color: white;
}
.product-collection .text-light {
opacity: 1;
font-size: 0.8;
font-weight: 400;
line-height: 1.7;
}
.btn-dark {
background: black;
color: white;
outline: 0;
border-radius: 25px;
padding: 0.7rem 1rem;
border: 0;
margin-top: 1rem;
cursor: pointer;
transition: all 0.6s ease;
font-size: 1rem;
font-family: inherit;
}
.btn-dark:hover {
background: #40c9a2;
}
/* Media Queries */
@media screen and (min-width: 992px) {
.product-items {
display: grid;
grid-template-columns: repeat(2, 1fr);
}
.product-col-r-bottom {
display: grid;
grid-template-columns: repeat(2, 1fr);
}
}
@media screen and (min-width: 1200px) {
.product-items {
grid-template-columns: repeat(3, 1fr);
}
.products .text-light {
width: 50%;
}
}
@media screen and (min-width: 1336px) {
.product-items {
grid-template-columns: repeat(3, 1fr);
}
.product-collection-wrapper {
display: grid;
grid-template-columns: repeat(2, 1fr);
}
.product-col-left {
height: 121.5vh;
}
}

51
src/app/pages/admin/Monthly_Desires/desires.module.ts

@ -1,12 +1,59 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ListDesiresComponent } from './ListDesires/list-desires.component';
import { DesiresComponent } from './desires.component';
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 { RouterModule } from '@angular/router';
import { TranslocoModule } from '@ngneat/transloco';
import { SharedModule } from 'app/shared/shared.module';
import { QuillModule } from 'ngx-quill';
import { desiresRoutes } from './desires.routing';
@NgModule({
declarations: [],
declarations: [
DesiresComponent,
ListDesiresComponent
],
imports: [
CommonModule
RouterModule.forChild(desiresRoutes),
QuillModule.forRoot(),
MatButtonModule,
MatButtonToggleModule,
MatDividerModule,
MatIconModule,
MatFormFieldModule,
MatInputModule,
MatMenuModule,
MatProgressBarModule,
MatRippleModule,
MatSidenavModule,
MatSortModule,
MatTableModule,
MatTabsModule,
MatDialogModule,
TranslocoModule,
SharedModule,
MatPaginatorModule,
MatSelectModule,
MatTooltipModule,
]
})
export class DesiresModule { }

36
src/app/pages/admin/Monthly_Desires/desires.routing.ts

@ -0,0 +1,36 @@
import { Route } from '@angular/router';
import { EditProductComponent } from '../Products/EditProduct/edit-product.component';
import { NewProductComponent } from '../Products/NewProduct/new-product.component';
import { DesiresComponent } from './desires.component';
import { ListDesiresComponent } from './ListDesires/list-desires.component';
export const desiresRoutes: Route[] = [
{
path : '',
component: DesiresComponent,
// resolve : {
// data: FollowersResolver
// }
children : [
{
path : '',
pathMatch: 'full',
component: ListDesiresComponent,
},
{
path : 'create-product',
component: NewProductComponent,
// resolve : {
// course: AcademyCourseResolver
// }
},
// {
// path : ':id',
// component: EditProductComponent,
// // resolve : {
// // course: AcademyCourseResolver
// // }
// }
]
}
];

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

@ -0,0 +1,129 @@
<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 {{data.product.name}}</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">Target Business </strong>
</mat-label>
</div>
<div class="column">
<mat-label style="margin-right: 48px;">
Target Name
</mat-label>
<button style="background-color: blue;color:white;margin-right:20px;" mat-button (click)="lookupBusiness()">Search</button>
</div>
</div>
<hr/>
<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">Target Name </strong>
</mat-label>
</div>
<div class="column">
<mat-label style="margin-right: 48px;">
Target Name
</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;">
GH¢200
</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-form-field appearance="fill" style="min-width: 200px;">
<mat-select required>
<mat-option value="low">Freebie</mat-option>
<mat-option value="standard">Discount</mat-option>
<mat-option value="high">Proximity Freebie</mat-option>
<mat-option value="high">Proximity Discount</mat-option>
</mat-select>
</mat-form-field>
</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;"></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 style="margin-left:10px;" type="number">
</mat-form-field>
</div>
</div>
<div style="display: flex;align-items: baseline">
<mat-label style="margin-right: 48px;">
<strong class="tileHead">Percentage Off </strong>
</mat-label>
<mat-form-field appearance="fill" style="margin-right:10px;">
<span matPrefix>From &nbsp;</span>
<input matInput type="number">
<span matSuffix>% &nbsp;</span>
</mat-form-field>
<mat-form-field appearance="fill" style="margin-left:10px;">
<span matPrefix>To &nbsp;</span>
<input matInput type="number">
<span matSuffix>% &nbsp;</span>
</mat-form-field>
</div>
<div style="display: flex;align-items: baseline">
<mat-label style="margin-right: 48px;">
<strong class="tileHead">Coupon Worth </strong>
</mat-label>
<mat-label>
From GH¢200 To GH¢800
</mat-label>
</div>
<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-form-field appearance="outline">
<input matInput style="margin-left:10px;" type="date">
</mat-form-field>
<mat-form-field appearance="fill">
<span matPrefix>Time &nbsp;</span>
<input matInput type="time">
</mat-form-field>
</div>
<div class="row" style="display: flex;justify-content:space-around;width:100%;">
<button style="background-color: blue;color:white;margin-right:20px;" mat-button>Generate Coupons</button>
</div>
</div>
</mat-dialog-content>

11
src/app/pages/admin/Products/AdminCouponDialog/admin-coupon-dialog.component.scss

@ -0,0 +1,11 @@
.columns {
display: flex;
flex-direction: row;
flex-wrap: wrap;
width: 100%;
}
.column {
flex: 50%;
font-size: medium;
}

23
src/app/pages/admin/Products/AdminCouponDialog/admin-coupon-dialog.component.ts

@ -0,0 +1,23 @@
import { Component, Inject, OnInit } from '@angular/core';
import { MatDialog, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { BusinessLookUpComponent } from '../BusinessLookUp/business-look-up.component';
@Component({
selector: 'admin-coupon-dialog',
templateUrl: './admin-coupon-dialog.component.html',
styleUrls: ['./admin-coupon-dialog.component.scss']
})
export class AdminCouponDialogComponent implements OnInit {
constructor(@Inject(MAT_DIALOG_DATA) public data: { product: any }, public dialog: MatDialog,) { }
ngOnInit(): void {
}
lookupBusiness() {
const dialogRef = this.dialog.open(BusinessLookUpComponent, {
disableClose: true,
hasBackdrop: true,
});
}
}

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

@ -0,0 +1,93 @@
<div class="row" style="display: flex;justify-content:end;margin-bottom: 20px;">
<button mat-button [mat-dialog-close]="true" cdkFocusInitial>x</button>
</div>
<mat-dialog-content class="mat-typography">
<div class="flex flex-shrink-0 items-center mt-6 sm:mt-0 sm:ml-4 justify-center">
<!-- Search -->
<mat-form-field class="teso-mat-dense teso-mat-no-subscript teso-mat-rounded min-w-64">
<mat-icon class="icon-size-5" matPrefix [svgIcon]="'heroicons_solid:search'"></mat-icon>
<input matInput [autocomplete]="'off'" [placeholder]="'Enter Business Name or email here'">
</mat-form-field>
<button class="ml-3" mat-flat-button [color]="'primary'" style="margin-left: 10px;">
<mat-icon class="icon-size-5" [svgIcon]="'heroicons_solid:search'"></mat-icon>
<span class="ml-2">Search</span>
</button>
</div>
<div class="flex flex-shrink-0 items-center justify-center" style="margin-top: 10px !important;">
<a href="javascript:void(0)"
style="color: #1890ff;text-decoration: none;background-color: transparent;outline: 0;cursor: pointer;">Target
Business not registered on teso ?</a>
</div>
<table class="w-full bg-transparent" mat-table matSort [dataSource]="typesDataSource" [trackBy]="trackByFn"
#typesTable>
<!-- Transaction ID -->
<ng-container matColumnDef="transactionId">
<th mat-header-cell mat-sort-header *matHeaderCellDef>
Business
</th>
<td mat-cell *matCellDef="let transaction" style="display: flex;padding:10px;">
<div id="circle">
<div>
<span>{{transaction.name.substring(0,2).toUpperCase()}}</span>
</div>
</div>
<div class="flex flex-col">
<span class="pr-6 font-medium text-sm text-secondary whitespace-nowrap">
{{transaction.name}}
</span>
<span class="pr-6 font-small text-sm text-secondary whitespace-nowrap">
{{transaction.email}}
</span>
</div>
</td>
</ng-container>
<!-- Request Name -->
<ng-container matColumnDef="name">
<th mat-header-cell mat-sort-header *matHeaderCellDef>
Contact
</th>
<td mat-cell *matCellDef="let transaction">
<span class="pr-6 whitespace-wrap">
{{transaction.phone}}
</span>
</td>
</ng-container>
<ng-container matColumnDef="members">
<th mat-header-cell mat-sort-header *matHeaderCellDef>
Address
</th>
<td mat-cell *matCellDef="let transaction">
<span class="pr-6 whitespace-wrap">
AK-151-0000
</span>
</td>
</ng-container>
<!-- Amount -->
<ng-container matColumnDef="actions">
<th mat-header-cell mat-sort-header *matHeaderCellDef>
Actions
</th>
<td mat-cell *matCellDef="let transaction">
<div style="display: flex;">
<button class="sm:inline-flex" mat-flat-button [color]="'primary'">
Select
</button>
</div>
</td>
</ng-container>
<!-- 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="typesTableColumns"></tr>
<tr class="users-row h-14" mat-row *matRowDef="let row; columns: typesTableColumns;"></tr>
<tr class="h-16 border-0" mat-footer-row *matFooterRowDef="['recentOrdersTableFooter']"></tr>
</table>
</mat-dialog-content>

19
src/app/pages/admin/Products/BusinessLookUp/business-look-up.component.scss

@ -0,0 +1,19 @@
.users-row {
cursor: pointer;
}
#circle {
background: #00a4be;
width: 45px;
height: 45px;
border-radius: 64px;
color: #003445;
font-weight: bold;
text-align: center;
justify-content: center;
display: flex;
align-content: center;
align-items: center;
font-size: initial;
margin-right: 15px;
}

12
src/app/pages/admin/Products/NewProduct/new-product.component.spec.ts → src/app/pages/admin/Products/BusinessLookUp/business-look-up.component.spec.ts

@ -1,20 +1,20 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { NewProductComponent } from './new-product.component';
import { BusinessLookUpComponent } from './business-look-up.component';
describe('NewProductComponent', () => {
let component: NewProductComponent;
let fixture: ComponentFixture<NewProductComponent>;
describe('BusinessLookUpComponent', () => {
let component: BusinessLookUpComponent;
let fixture: ComponentFixture<BusinessLookUpComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ NewProductComponent ]
declarations: [ BusinessLookUpComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(NewProductComponent);
fixture = TestBed.createComponent(BusinessLookUpComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

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

@ -0,0 +1,121 @@
import { Component, OnInit, ViewChild } from '@angular/core';
import { FormControl } from '@angular/forms';
import { MatSort } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table';
import { Subject } from 'rxjs';
export const finance = {
types: [
{
id: '1b6fd296-bc6a-4d45-bf4f-e45519a58cf5',
transactionId: '528651571NT',
name: 'Morgan Page',
phone: "+233 244 419 000",
email:"business@bacware.com",
status: 'Active',
date: '2019-10-07T22:22:37.274Z'
},
{
id: '2dec6074-98bd-4623-9526-6480e4776569',
transactionId: '421436904YT',
name: 'Nita Hebert',
phone: "+233 244 419 000",
email:"business@bacware.com",
status: 'Active',
date: '2019-12-18T14:51:24.461Z'
},
{
id: 'ae7c065f-4197-4021-a799-7a221822ad1d',
transactionId: '685377421YT',
name: 'Marsha Chambers',
phone: "+233 244 419 000",
email:"business@bacware.com",
status: 'Active',
date: '2019-12-25T17:52:14.304Z'
},
{
id: '0c43dd40-74f6-49d5-848a-57a4a45772ab',
transactionId: '884960091RT',
name: 'Charmaine Jackson',
phone: "+233 244 419 000",
email:"business@bacware.com",
status: 'Active',
date: '2019-11-29T06:32:16.111Z'
},
{
id: 'e5c9f0ed-a64c-4bfe-a113-29f80b4e162c',
transactionId: '361402213NT',
name: 'Maura Carey',
phone: "+233 244 419 000",
email:"business@bacware.com",
status: 'Active',
date: '2019-11-24T12:13:23.064Z'
}, {
id: '1b6fd296-bc6a-4d45-bf4f-e45519a58cf5',
transactionId: '528651571NT',
name: 'Morgan Page',
phone: "+233 244 419 000",
email:"business@bacware.com",
status: 'Active',
date: '2019-10-07T22:22:37.274Z'
},
{
id: '2dec6074-98bd-4623-9526-6480e4776569',
transactionId: '421436904YT',
name: 'Nita Hebert',
phone: "+233 244 419 000",
email:"business@bacware.com",
status: 'Active',
date: '2019-12-18T14:51:24.461Z'
},
{
id: 'ae7c065f-4197-4021-a799-7a221822ad1d',
transactionId: '685377421YT',
name: 'Marsha Chambers',
phone: "+233 244 419 000",
email:"business@bacware.com",
status: 'Active',
date: '2019-12-25T17:52:14.304Z'
},
{
id: '0c43dd40-74f6-49d5-848a-57a4a45772ab',
transactionId: '884960091RT',
name: 'Charmaine Jackson',
phone: "+233 244 419 000",
email:"business@bacware.com",
status: 'Active',
date: '2019-11-29T06:32:16.111Z'
},
{
id: 'e5c9f0ed-a64c-4bfe-a113-29f80b4e162c',
transactionId: '361402213NT',
name: 'Maura Carey',
phone: "+233 244 419 000",
email:"business@bacware.com",
status: 'Active',
date: '2019-11-24T12:13:23.064Z'
},
]
};
@Component({
selector: 'app-business-look-up',
templateUrl: './business-look-up.component.html',
styleUrls: ['./business-look-up.component.scss']
})
export class BusinessLookUpComponent implements OnInit {
@ViewChild('typesTable', { read: MatSort }) typesTableMatSort: MatSort;
searchInputControl: FormControl = new FormControl();
data: any;
typesDataSource: MatTableDataSource<any> = new MatTableDataSource();
typesTableColumns: string[] = ['transactionId', 'name', 'members', 'actions'];
htmlRequest: string;
transferType: string;
private _unsubscribeAll: Subject<any> = new Subject<any>();
constructor() { }
ngOnInit(): void {
this.typesDataSource.data = finance.types;
}
trackByFn(index: number, item: any): any {
return item.id || index;
}
}

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

@ -0,0 +1,115 @@
<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 {{data.product.name}}</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">Target Name </strong>
</mat-label>
</div>
<div class="column">
<mat-label style="margin-right: 48px;">
Target Name
</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;">
GH¢200
</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-form-field appearance="fill" style="min-width: 200px;">
<mat-select required>
<mat-option value="low">Freebie</mat-option>
<mat-option value="standard">Discount</mat-option>
<mat-option value="high">Proximity Freebie</mat-option>
<mat-option value="high">Proximity Discount</mat-option>
</mat-select>
</mat-form-field>
</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;"></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 style="margin-left:10px;" type="number">
</mat-form-field>
</div>
</div>
<div style="display: flex;align-items: baseline">
<mat-label style="margin-right: 48px;">
<strong class="tileHead">Percentage Off </strong>
</mat-label>
<mat-form-field appearance="fill" style="margin-right:10px;">
<span matPrefix>From &nbsp;</span>
<input matInput type="number">
<span matSuffix>% &nbsp;</span>
</mat-form-field>
<mat-form-field appearance="fill" style="margin-left:10px;">
<span matPrefix>To &nbsp;</span>
<input matInput type="number">
<span matSuffix>% &nbsp;</span>
</mat-form-field>
</div>
<div style="display: flex;align-items: baseline">
<mat-label style="margin-right: 48px;">
<strong class="tileHead">Coupon Worth </strong>
</mat-label>
<mat-label>
From GH¢200 To GH¢800
</mat-label>
</div>
<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-form-field appearance="outline">
<input matInput style="margin-left:10px;" type="date">
</mat-form-field>
<mat-form-field appearance="fill">
<span matPrefix>Time &nbsp;</span>
<input matInput type="time">
</mat-form-field>
</div>
<div class="row" style="display: flex;justify-content:space-around;width:100%;">
<button style="background-color: blue;color:white;margin-right:20px;" mat-button>Generate Coupons</button>
</div>
</div>
</mat-dialog-content>

11
src/app/pages/admin/Products/CouponDialog/coupon-dialog.component.scss

@ -0,0 +1,11 @@
.columns {
display: flex;
flex-direction: row;
flex-wrap: wrap;
width: 100%;
}
.column {
flex: 50%;
font-size: medium;
}

16
src/app/pages/admin/Products/CouponDialog/coupon-dialog.component.ts

@ -0,0 +1,16 @@
import { Component, Inject, OnInit } from '@angular/core';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
@Component({
selector: 'app-coupon-dialog',
templateUrl: './coupon-dialog.component.html',
styleUrls: ['./coupon-dialog.component.scss']
})
export class CouponDialogComponent implements OnInit {
constructor(@Inject(MAT_DIALOG_DATA) public data: { product: any },) { }
ngOnInit(): void {
}
}

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

@ -1,10 +1,11 @@
<div class="absolute inset-0 flex flex-col min-w-0 ">
<div class="absolute inset-0 flex flex-col min-w-0 " *ngIf="!isScreenSmall">
<mat-drawer-container class="flex-auto h-full">
<!-- Drawer content -->
<mat-drawer-content class="flex flex-col">
<!-- Header -->
<div class="flex flex-0 items-center py-2 pl-4 pr-6 sm:py-4 md:pl-6 md:pr-8 border-b lg:border-b-0 bg-card dark:bg-transparent">
<div
class="flex flex-0 items-center py-2 pl-4 pr-6 sm:py-4 md:pl-6 md:pr-8 border-b lg:border-b-0 bg-card dark:bg-transparent">
<!-- Title & Actions -->
<button mat-icon-button [routerLink]="['../']">
<mat-icon [svgIcon]="'heroicons_outline:arrow-sm-left'"></mat-icon>
@ -13,9 +14,215 @@
Product #
</h2>
</div>
<div class=" flex flex-0 items-center py-2 pl-4 pr-6 sm:py-4 md:pl-6 md:pr-8 border-b lg:border-b-0 bg-card dark:bg-transparent">
<div
class=" flex flex-0 items-center py-2 pl-4 pr-6 sm:py-4 md:pl-6 md:pr-8 border-b lg:border-b-0 bg-card dark:bg-transparent">
<div class="flex-auto" cdkScrollable>
<div class="min-h-180 border-2 border-dashed border-gray-300 rounded-2xl py-2 pl-4 pr-6 sm:py-4 md:pl-6 md:pr-8 mx-auto"
style="max-width: 780px;">
<mat-tab-group mat-align-tabs="center">
<mat-tab label="Product Information">
<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;">
<mat-label style="margin-right: 48px;">
<strong class="tileHead">Product Name </strong>
</mat-label>
<mat-form-field appearance="fill" style="width: 350px;">
<input matInput style="margin-left:10px;" type="text">
</mat-form-field>
</div>
<div style="display:flex;">
<mat-label style="margin-right: 20px;">
<strong class="tileHead">Product Category</strong>
</mat-label>
<mat-form-field appearance="fill" style="width: 350px;">
<mat-select required>
<mat-option value="low">Lowdsadsadsadsadsadsa</mat-option>
<mat-option value="standard">Standardasdsadasdas</mat-option>
<mat-option value="high">Highsadsadsadas</mat-option>
</mat-select>
</mat-form-field>
</div>
<div style="display: flex;">
<mat-label style="margin-right: 48px;">
<strong class="tileHead">Original Price </strong>
</mat-label>
<mat-form-field appearance="fill" style="width: 350px;">
<span matPrefix>GH¢ &nbsp;</span>
<input matInput style="margin-left:10px;" type="number">
</mat-form-field>
</div>
<div class="columns" style="display: flex;justify-content:space-around;">
<div class="column">
<div>
<strong class="tileHead">Product Description :</strong>
</div>
</div>
</div>
<div class="column">
<quill-editor [modules]="quillConfig" [(ngModel)]="htmlRequest"
placeholder="Further details">
</quill-editor>
</div>
</div>
</mat-tab>
<mat-tab label="Images">
<div class="py-2 pl-4 pr-6 sm:py-4 md:pl-6 md:pr-8"
style="min-height: 550px !important;">
<div class="flex justify-between">
<div>
<div class="font-medium tracking-tight text-secondary">Add up to 10 high
quality images of the product
</div>
<div class="font-medium tracking-tight text-secondary">
{{productImages.length}} out of
<strong>10</strong>
</div>
</div>
<input style="display: none" type="file" (change)="onFileSelected($event)"
#fileInput accept="image/*" capture="user">
<button style="background-color: blue;color:white;margin-right:20px;" mat-button
(click)="fileInput.click()" [disabled]="productImages.length > 9">Add Image
</button>
</div>
<div class="imageList">
<div class="imageHolder" *ngFor="let images of productImages">
<img src="{{images.imageSRC}}" class="productImage">
<button class="deleteelement" style="border:none;"
(click)="removeImage(images)">
<mat-icon [svgIcon]="'heroicons_outline:trash'" style="color: red;">
</mat-icon>
</button>
</div>
</div>
</div>
</mat-tab>
</mat-tab-group>
<div class="row" style="display: flex;justify-content:space-around;width:100%;">
<button style="background-color: green;color:white;margin-right:20px;" mat-button
(click)="submit()">Add
Product</button>
<button style="background-color: firebrick;color:white;margin-left:10px;" mat-button
cdkFocusInitial>Cancel</button>
</div>
</div>
</div>
</div>
</mat-drawer-content>
</mat-drawer-container>
</div>
<div class="absolute inset-0 flex flex-col min-w-0 " *ngIf="isScreenSmall">
<mat-drawer-container class="flex-auto h-full">
<!-- Drawer content -->
<mat-drawer-content class="flex flex-col">
<!-- Header -->
<div
class="flex flex-0 items-center py-2 pl-4 pr-6 sm:py-4 md:pl-6 md:pr-8 border-b lg:border-b-0 bg-card dark:bg-transparent">
<!-- Title & Actions -->
<button mat-icon-button [routerLink]="['../']">
<mat-icon [svgIcon]="'heroicons_outline:arrow-sm-left'"></mat-icon>
</button>
<h2 class="ml-2.5 text-md sm:text-xl font-medium tracking-tight truncate">
Product #
</h2>
</div>
<div
class=" flex flex-0 items-center py-2 pl-4 pr-6 sm:py-4 md:pl-6 md:pr-8 border-b lg:border-b-0 bg-card dark:bg-transparent">
<div class="flex-auto overflow-y-auto" cdkScrollable>
<div class="min-h-400 max-h-400 border-2 border-dashed border-gray-300 rounded-2xl py-2 pl-4 pr-6 sm:py-4 md:pl-6 md:pr-8">
<div class="min-h-180 border-2 border-dashed border-gray-300 rounded-2xl py-2 pl-4 pr-6 sm:py-4 md:pl-6 md:pr-8 mx-auto"
style="max-width: 780px;">
<mat-tab-group mat-align-tabs="center">
<mat-tab label="Product Information">
<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;">
<mat-label style="margin-right: 20px;">
<strong>Product Name </strong>
</mat-label>
<mat-form-field appearance="fill">
<input matInput style="margin-left:10px;" type="text">
</mat-form-field>
</div>
<div style="display:flex;">
<mat-label style="margin-right: 20px;">
<strong>Product Category</strong>
</mat-label>
<mat-form-field appearance="fill">
<mat-select required>
<mat-option value="low">Lowdsadsadsadsadsadsa</mat-option>
<mat-option value="standard">Standardasdsadasdas</mat-option>
<mat-option value="high">Highsadsadsadas</mat-option>
</mat-select>
</mat-form-field>
</div>
<div style="display: flex;">
<mat-label style="margin-right: 48px;">
<strong>Original Price </strong>
</mat-label>
<mat-form-field appearance="fill">
<span matPrefix>GH¢ &nbsp;</span>
<input matInput style="margin-left:10px;" type="number">
</mat-form-field>
</div>
<div class="columns" style="display: flex;justify-content:space-around;">
<div class="column">
<div>
<strong>Product Description :</strong>
</div>
</div>
</div>
<div class="column">
<quill-editor [modules]="quillConfig" [(ngModel)]="htmlRequest"
placeholder="Further details">
</quill-editor>
</div>
</div>
</mat-tab>
<mat-tab label="Images">
<div class="py-2 pl-2 pr-2 sm:py-4 md:pl-6 md:pr-8">
<div class="flex justify-between">
<div>
<div class="font-medium tracking-tight text-secondary">Add up to 10 high
quality images of the product
</div>
<div class="font-medium tracking-tight text-secondary">0 out of
<strong>10</strong>
</div>
</div>
<input style="display: none" type="file" (change)="onFileSelected($event)"
#fileInput accept="image/*" capture="user">
<button style="background-color: blue;color:white;margin-right:20px;" mat-button
(click)="fileInput.click()" [disabled]="productImages.length > 9">
<mat-icon [svgIcon]="'heroicons_outline:plus'" style="color: white;">
</mat-icon>
</button>
</div>
<div class="imageList">
<div class="imageHolder" *ngFor="let images of productImages">
<img src="{{images.imageSRC}}" class="productImage">
<button class="deleteelement" style="border:none;"
(click)="removeImage(images)">
<mat-icon [svgIcon]="'heroicons_outline:trash'" style="color: red;">
</mat-icon>
</button>
</div>
</div>
</div>
</mat-tab>
</mat-tab-group>
<div class="row" style="display: flex;justify-content:space-around;width:100%;">
<button style="background-color: green;color:white;margin-right:20px;" mat-button (click)="submit()">Add
Product</button>
<button style="background-color: firebrick;color:white;margin-left:10px;" mat-button
cdkFocusInitial>Cancel</button>
</div>
</div>
</div>
</div>

78
src/app/pages/admin/Products/EditProduct/edit-product.component.scss

@ -0,0 +1,78 @@
.columns {
display: flex;
flex-direction: row;
flex-wrap: wrap;
width: 100%;
}
.column {
flex: 50%;
font-size: medium;
}
.tileHead {
font-size: 17px;
color: #003445;
font-family: Roboto, "Helvetica Neue", sans-serif;
line-height: 3rem;
}
.ticketLine > a:hover {
color: cornflowerblue;
text-decoration: underline;
}
.ticketLine > a {
color: cornflowerblue;
font-style: italic;
}
::ng-deep {
@media screen and (min-width: 992px) {
.ql-container .ql-editor {
min-height: 160px;
max-height: 270px;
height: 270px;
}
}
}
.imageList {
display: flex;
flex-wrap: wrap;
}
.imageHolder {
flex-grow: 1;
width: 22%;
max-width: 180px;
height: 180px;
margin: 10px;
border: 3px solid #003445;
padding: 5px;
position: relative;
}
.productImage{
max-width: 130px;
margin: 10px;
}
.deleteelement {
position: absolute;
top: 5px;
right: 5px;
}
@media screen and (max-width: 992px) {
.imageHolder {
flex-grow: 1;
width: 22%;
max-width: 180px;
min-width: 180px;
height: 180px;
margin: 10px;
border: 3px solid #003445;
padding: 5px;
position: relative;
}
}

122
src/app/pages/admin/Products/EditProduct/edit-product.component.ts

@ -1,4 +1,32 @@
import { Component, OnInit } from '@angular/core';
import { ConfirmBoxEvokeService } from '@costlydeveloper/ngx-awesome-popup';
import { tesoMediaWatcherService } from '@teso/services/media-watcher';
import { ProductUpload } from 'app/models/generalModel';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
const modules = {
toolbar: [
['bold', 'italic', 'underline', 'strike'],
['blockquote', 'code-block'],
[{ 'header': 1 }, { 'header': 2 }],
[{ 'list': 'ordered' }, { 'list': 'bullet' }],
[{ 'script': 'sub' }, { 'script': 'super' }],
[{ 'indent': '-1' }, { 'indent': '+1' }],
[{ 'direction': 'rtl' }],
[{ 'size': ['small', false, 'large', 'huge'] }],
[{ 'header': [1, 2, 3, 4, 5, 6, false] }],
[{ 'color': [] }, { 'background': [] }],
[{ 'font': [] }],
[{ 'align': [] }],
['clean'],
]
};
@Component({
selector: 'app-edit-product',
@ -6,10 +34,100 @@ import { Component, OnInit } from '@angular/core';
styleUrls: ['./edit-product.component.scss']
})
export class EditProductComponent implements OnInit {
constructor() { }
htmlRequest: string;
transferType: string;
placeHolder: string = "";
selected: any;
isScreenSmall: boolean;
productImages: ProductUpload[] = [];
private _unsubscribeAll: Subject<any> = new Subject<any>();
constructor(private _tesoMediaWatcherService: tesoMediaWatcherService, private confirmBoxEvokeService: ConfirmBoxEvokeService) { }
ngOnInit(): void {
this._tesoMediaWatcherService.onMediaChange$
.pipe(takeUntil(this._unsubscribeAll))
.subscribe(({ matchingAliases }) => {
// Check if the screen is small
this.isScreenSmall = !matchingAliases.includes('md');
});
}
quillConfig = modules;
items = [
{ id: 1, name: 'Python' },
{ id: 2, name: 'Node Js' },
{ id: 3, name: 'Java' },
{ id: 4, name: 'PHP', disabled: true },
{ id: 5, name: 'Django' },
{ id: 6, name: 'Angular' },
{ id: 7, name: 'Vue' },
{ id: 8, name: 'ReactJs' },
];
ngOnDestroy(): void {
// Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.complete();
}
// readURL(event: Event): void {
// if (event.target.files && event.target.files[0]) {
// const file = event.target.files[0];
// const reader = new FileReader();
// reader.onload = e => this.imageSrc = reader.result;
// reader.readAsDataURL(file);
// }
// }
onFileSelected(event) {
const file: File = event.target.files[0];
if (file.type.includes("image")) {
console.log(file)
if (this.productImages.length == 0) {
var productImage: ProductUpload = {
file: file,
highlight: true,
imageSRC: "",
};
let reader = new FileReader();
reader.onload = (event: any) => {
productImage.imageSRC = event.target.result;
}
reader.readAsDataURL(file);
this.productImages.push(productImage);
} else {
var productImage: ProductUpload = {
file: file,
highlight: false,
imageSRC: "",
};
let reader = new FileReader();
reader.onload = (event: any) => {
productImage.imageSRC = event.target.result;
}
reader.readAsDataURL(file);
this.productImages.push(productImage);
}
} else {
this.confirmBoxEvokeService.danger("Incompatible File", "Only images can be added", "OK").subscribe();
}
}
removeImage(item: ProductUpload) {
this.productImages = this.productImages.filter((e) => e != item);
}
submit() {
if (this.productImages.length == 0) {
this.confirmBoxEvokeService.warning("Add Product Images", "To add a new product, you must add at least one image to support it", "OK").subscribe();
} else {
}
}
}

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

@ -1,10 +1,11 @@
<div class="absolute inset-0 flex flex-col min-w-0 ">
<div class="absolute inset-0 flex flex-col min-w-0 " *ngIf="!isScreenSmall">
<mat-drawer-container class="flex-auto h-full">
<!-- Drawer content -->
<mat-drawer-content class="flex flex-col">
<!-- Header -->
<div class="flex flex-0 items-center py-2 pl-4 pr-6 sm:py-4 md:pl-6 md:pr-8 border-b lg:border-b-0 bg-card dark:bg-transparent">
<div
class="flex flex-0 items-center py-2 pl-4 pr-6 sm:py-4 md:pl-6 md:pr-8 border-b lg:border-b-0 bg-card dark:bg-transparent">
<!-- Title & Actions -->
<button mat-icon-button [routerLink]="['../']">
<mat-icon [svgIcon]="'heroicons_outline:arrow-sm-left'"></mat-icon>
@ -13,9 +14,215 @@
New Product
</h2>
</div>
<div class=" flex flex-0 items-center py-2 pl-4 pr-6 sm:py-4 md:pl-6 md:pr-8 border-b lg:border-b-0 bg-card dark:bg-transparent">
<div
class=" flex flex-0 items-center py-2 pl-4 pr-6 sm:py-4 md:pl-6 md:pr-8 border-b lg:border-b-0 bg-card dark:bg-transparent">
<div class="flex-auto" cdkScrollable>
<div class="min-h-180 border-2 border-dashed border-gray-300 rounded-2xl py-2 pl-4 pr-6 sm:py-4 md:pl-6 md:pr-8 mx-auto"
style="max-width: 780px;">
<mat-tab-group mat-align-tabs="center">
<mat-tab label="Product Information">
<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;">
<mat-label style="margin-right: 48px;">
<strong class="tileHead">Product Name </strong>
</mat-label>
<mat-form-field appearance="fill" style="width: 350px;">
<input matInput style="margin-left:10px;" type="text">
</mat-form-field>
</div>
<div style="display:flex;">
<mat-label style="margin-right: 20px;">
<strong class="tileHead">Product Category</strong>
</mat-label>
<mat-form-field appearance="fill" style="width: 350px;">
<mat-select required>
<mat-option value="low">Lowdsadsadsadsadsadsa</mat-option>
<mat-option value="standard">Standardasdsadasdas</mat-option>
<mat-option value="high">Highsadsadsadas</mat-option>
</mat-select>
</mat-form-field>
</div>
<div style="display: flex;">
<mat-label style="margin-right: 48px;">
<strong class="tileHead">Original Price </strong>
</mat-label>
<mat-form-field appearance="fill" style="width: 350px;">
<span matPrefix>GH¢ &nbsp;</span>
<input matInput style="margin-left:10px;" type="number">
</mat-form-field>
</div>
<div class="columns" style="display: flex;justify-content:space-around;">
<div class="column">
<div>
<strong class="tileHead">Product Description :</strong>
</div>
</div>
</div>
<div class="column">
<quill-editor [modules]="quillConfig" [(ngModel)]="htmlRequest"
placeholder="Further details">
</quill-editor>
</div>
</div>
</mat-tab>
<mat-tab label="Images">
<div class="py-2 pl-4 pr-6 sm:py-4 md:pl-6 md:pr-8"
style="min-height: 550px !important;">
<div class="flex justify-between">
<div>
<div class="font-medium tracking-tight text-secondary">Add up to 10 high
quality images of the product
</div>
<div class="font-medium tracking-tight text-secondary">
{{productImages.length}} out of
<strong>10</strong>
</div>
</div>
<input style="display: none" type="file" (change)="onFileSelected($event)"
#fileInput accept="image/*" capture="user">
<button style="background-color: blue;color:white;margin-right:20px;" mat-button
(click)="fileInput.click()" [disabled]="productImages.length > 9">Add Image
</button>
</div>
<div class="imageList">
<div class="imageHolder" *ngFor="let images of productImages">
<img src="{{images.imageSRC}}" class="productImage">
<button class="deleteelement" style="border:none;"
(click)="removeImage(images)">
<mat-icon [svgIcon]="'heroicons_outline:trash'" style="color: red;">
</mat-icon>
</button>
</div>
</div>
</div>
</mat-tab>
</mat-tab-group>
<div class="row" style="display: flex;justify-content:space-around;width:100%;">
<button style="background-color: green;color:white;margin-right:20px;" mat-button
(click)="submit()">Add
Product</button>
<button style="background-color: firebrick;color:white;margin-left:10px;" mat-button
cdkFocusInitial>Cancel</button>
</div>
</div>
</div>
</div>
</mat-drawer-content>
</mat-drawer-container>
</div>
<div class="absolute inset-0 flex flex-col min-w-0 " *ngIf="isScreenSmall">
<mat-drawer-container class="flex-auto h-full">
<!-- Drawer content -->
<mat-drawer-content class="flex flex-col">
<!-- Header -->
<div
class="flex flex-0 items-center py-2 pl-4 pr-6 sm:py-4 md:pl-6 md:pr-8 border-b lg:border-b-0 bg-card dark:bg-transparent">
<!-- Title & Actions -->
<button mat-icon-button [routerLink]="['../']">
<mat-icon [svgIcon]="'heroicons_outline:arrow-sm-left'"></mat-icon>
</button>
<h2 class="ml-2.5 text-md sm:text-xl font-medium tracking-tight truncate">
New Product
</h2>
</div>
<div
class=" flex flex-0 items-center py-2 pl-4 pr-6 sm:py-4 md:pl-6 md:pr-8 border-b lg:border-b-0 bg-card dark:bg-transparent">
<div class="flex-auto overflow-y-auto" cdkScrollable>
<div class="min-h-400 max-h-400 border-2 border-dashed border-gray-300 rounded-2xl py-2 pl-4 pr-6 sm:py-4 md:pl-6 md:pr-8">
<div class="min-h-180 border-2 border-dashed border-gray-300 rounded-2xl py-2 pl-4 pr-6 sm:py-4 md:pl-6 md:pr-8 mx-auto"
style="max-width: 780px;">
<mat-tab-group mat-align-tabs="center">
<mat-tab label="Product Information">
<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;">
<mat-label style="margin-right: 20px;">
<strong>Product Name </strong>
</mat-label>
<mat-form-field appearance="fill">
<input matInput style="margin-left:10px;" type="text">
</mat-form-field>
</div>
<div style="display:flex;">
<mat-label style="margin-right: 20px;">
<strong>Product Category</strong>
</mat-label>
<mat-form-field appearance="fill">
<mat-select required>
<mat-option value="low">Lowdsadsadsadsadsadsa</mat-option>
<mat-option value="standard">Standardasdsadasdas</mat-option>
<mat-option value="high">Highsadsadsadas</mat-option>
</mat-select>
</mat-form-field>
</div>
<div style="display: flex;">
<mat-label style="margin-right: 48px;">
<strong>Original Price </strong>
</mat-label>
<mat-form-field appearance="fill">
<span matPrefix>GH¢ &nbsp;</span>
<input matInput style="margin-left:10px;" type="number">
</mat-form-field>
</div>
<div class="columns" style="display: flex;justify-content:space-around;">
<div class="column">
<div>
<strong>Product Description :</strong>
</div>
</div>
</div>
<div class="column">
<quill-editor [modules]="quillConfig" [(ngModel)]="htmlRequest"
placeholder="Further details">
</quill-editor>
</div>
</div>
</mat-tab>
<mat-tab label="Images">
<div class="py-2 pl-2 pr-2 sm:py-4 md:pl-6 md:pr-8">
<div class="flex justify-between">
<div>
<div class="font-medium tracking-tight text-secondary">Add up to 10 high
quality images of the product
</div>
<div class="font-medium tracking-tight text-secondary">0 out of
<strong>10</strong>
</div>
</div>
<input style="display: none" type="file" (change)="onFileSelected($event)"
#fileInput accept="image/*" capture="user">
<button style="background-color: blue;color:white;margin-right:20px;" mat-button
(click)="fileInput.click()" [disabled]="productImages.length > 9">
<mat-icon [svgIcon]="'heroicons_outline:plus'" style="color: white;">
</mat-icon>
</button>
</div>
<div class="imageList">
<div class="imageHolder" *ngFor="let images of productImages">
<img src="{{images.imageSRC}}" class="productImage">
<button class="deleteelement" style="border:none;"
(click)="removeImage(images)">
<mat-icon [svgIcon]="'heroicons_outline:trash'" style="color: red;">
</mat-icon>
</button>
</div>
</div>
</div>
</mat-tab>
</mat-tab-group>
<div class="row" style="display: flex;justify-content:space-around;width:100%;">
<button style="background-color: green;color:white;margin-right:20px;" mat-button (click)="submit()">Add
Product</button>
<button style="background-color: firebrick;color:white;margin-left:10px;" mat-button
cdkFocusInitial>Cancel</button>
</div>
</div>
</div>
</div>

78
src/app/pages/admin/Products/NewProduct/new-product.component.scss

@ -0,0 +1,78 @@
.columns {
display: flex;
flex-direction: row;
flex-wrap: wrap;
width: 100%;
}
.column {
flex: 50%;
font-size: medium;
}
.tileHead {
font-size: 17px;
color: #003445;
font-family: Roboto, "Helvetica Neue", sans-serif;
line-height: 3rem;
}
.ticketLine > a:hover {
color: cornflowerblue;
text-decoration: underline;
}
.ticketLine > a {
color: cornflowerblue;
font-style: italic;
}
::ng-deep {
@media screen and (min-width: 992px) {
.ql-container .ql-editor {
min-height: 160px;
max-height: 270px;
height: 270px;
}
}
}
.imageList {
display: flex;
flex-wrap: wrap;
}
.imageHolder {
flex-grow: 1;
width: 22%;
max-width: 180px;
height: 180px;
margin: 10px;
border: 3px solid #003445;
padding: 5px;
position: relative;
}
.productImage{
max-width: 130px;
margin: 10px;
}
.deleteelement {
position: absolute;
top: 5px;
right: 5px;
}
@media screen and (max-width: 992px) {
.imageHolder {
flex-grow: 1;
width: 22%;
max-width: 180px;
min-width: 180px;
height: 180px;
margin: 10px;
border: 3px solid #003445;
padding: 5px;
position: relative;
}
}

124
src/app/pages/admin/Products/NewProduct/new-product.component.ts

@ -1,15 +1,133 @@
import { Component, OnInit } from '@angular/core';
import { Component, OnDestroy, OnInit } from '@angular/core';
import { tesoMediaWatcherService } from '@teso/services/media-watcher';
import { ProductUpload } from 'app/models/generalModel';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { ConfirmBoxEvokeService } from '@costlydeveloper/ngx-awesome-popup';
const modules = {
toolbar: [
['bold', 'italic', 'underline', 'strike'],
['blockquote', 'code-block'],
[{ 'header': 1 }, { 'header': 2 }],
[{ 'list': 'ordered' }, { 'list': 'bullet' }],
[{ 'script': 'sub' }, { 'script': 'super' }],
[{ 'indent': '-1' }, { 'indent': '+1' }],
[{ 'direction': 'rtl' }],
[{ 'size': ['small', false, 'large', 'huge'] }],
[{ 'header': [1, 2, 3, 4, 5, 6, false] }],
[{ 'color': [] }, { 'background': [] }],
[{ 'font': [] }],
[{ 'align': [] }],
['clean'],
]
};
@Component({
selector: 'app-new-product',
templateUrl: './new-product.component.html',
styleUrls: ['./new-product.component.scss']
})
export class NewProductComponent implements OnInit {
export class NewProductComponent implements OnInit, OnDestroy {
constructor() { }
htmlRequest: string;
transferType: string;
placeHolder: string = "";
selected: any;
isScreenSmall: boolean;
productImages: ProductUpload[] = [];
private _unsubscribeAll: Subject<any> = new Subject<any>();
constructor(private _tesoMediaWatcherService: tesoMediaWatcherService, private confirmBoxEvokeService: ConfirmBoxEvokeService) { }
ngOnInit(): void {
this._tesoMediaWatcherService.onMediaChange$
.pipe(takeUntil(this._unsubscribeAll))
.subscribe(({ matchingAliases }) => {
// Check if the screen is small
this.isScreenSmall = !matchingAliases.includes('md');
});
}
quillConfig = modules;
items = [
{ id: 1, name: 'Python' },
{ id: 2, name: 'Node Js' },
{ id: 3, name: 'Java' },
{ id: 4, name: 'PHP', disabled: true },
{ id: 5, name: 'Django' },
{ id: 6, name: 'Angular' },
{ id: 7, name: 'Vue' },
{ id: 8, name: 'ReactJs' },
];
ngOnDestroy(): void {
// Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.complete();
}
// readURL(event: Event): void {
// if (event.target.files && event.target.files[0]) {
// const file = event.target.files[0];
// const reader = new FileReader();
// reader.onload = e => this.imageSrc = reader.result;
// reader.readAsDataURL(file);
// }
// }
onFileSelected(event) {
const file: File = event.target.files[0];
if (file.type.includes("image")) {
console.log(file)
if (this.productImages.length == 0) {
var productImage: ProductUpload = {
file: file,
highlight: true,
imageSRC: "",
};
let reader = new FileReader();
reader.onload = (event: any) => {
productImage.imageSRC = event.target.result;
}
reader.readAsDataURL(file);
this.productImages.push(productImage);
} else {
var productImage: ProductUpload = {
file: file,
highlight: false,
imageSRC: "",
};
let reader = new FileReader();
reader.onload = (event: any) => {
productImage.imageSRC = event.target.result;
}
reader.readAsDataURL(file);
this.productImages.push(productImage);
}
} else {
this.confirmBoxEvokeService.danger("Incompatible File", "Only images can be added", "OK").subscribe();
}
}
removeImage(item: ProductUpload) {
this.productImages = this.productImages.filter((e) => e != item);
}
submit() {
if (this.productImages.length == 0) {
this.confirmBoxEvokeService.warning("Add Product Images", "To add a new product, you must add at least one image to support it", "OK").subscribe();
} else {
}
}
}

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

@ -3,7 +3,7 @@
<div class="flex flex-wrap w-full max-w-screen-xl mx-auto p-6 md:p-8">
<!-- Title and action buttons -->
<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;" *ngIf="!isScreenSmall">
<div>
<div class="text-3xl font-semibold tracking-tight leading-8">Products</div>
<div class="font-medium tracking-tight text-secondary">Here is a list of all products in your Teso (850)
@ -11,10 +11,9 @@
</div>
<div class="flex flex-shrink-0 items-center mt-6 sm:mt-0 sm:ml-4">
<button class="hidden sm:inline-flex ml-3" mat-flat-button [color]="'primary'" style="margin-right: 10px;" (click)="createProduct()">
<mat-icon
class="icon-size-5"
[svgIcon]="'heroicons_solid:plus'"></mat-icon>
<button class="ml-3" mat-flat-button [color]="'primary'" style="margin-right: 10px;"
(click)="createProduct()">
<mat-icon class="icon-size-5" [svgIcon]="'heroicons_solid:plus'"></mat-icon>
<span class="ml-2">Add Product</span>
</button>
<!-- Search -->
@ -25,20 +24,40 @@
</div>
</div>
<div class="flex items-center justify-between w-full" style="margin-bottom: 20px;" *ngIf="isScreenSmall">
<div>
<div class="text-3xl font-semibold tracking-tight leading-8">Products</div>
<div class="font-medium tracking-tight text-secondary">(850) products
</div>
</div>
<div class="flex-col items-center justify-end mt-6 ml-10">
<button mat-flat-button [color]="'primary'" (click)="createProduct()" style="margin-bottom: 10px;">
<mat-icon class="icon-size-5" [svgIcon]="'heroicons_solid:plus'"></mat-icon>
<span class="ml-2">Add Product</span>
</button>
<!-- Search -->
<mat-form-field class="teso-mat-dense teso-mat-no-subscript teso-mat-rounded ">
<mat-icon class="icon-size-5" matPrefix [svgIcon]="'heroicons_solid:search'"></mat-icon>
<input matInput [autocomplete]="'off'" [placeholder]="'Search'">
</mat-form-field>
</div>
</div>
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6 w-full min-w-0">
<ng-container *ngFor="let member of data.teamMembers">
<ng-container *ngFor="let product of data.teamproducts">
<!-- <div class="flex flex-col flex-auto items-center bg-card shadow rounded-2xl overflow-hidden"> -->
<div class="product">
<div class="product-content">
<div class="product-img">
<img [src]="member.avatar" alt="product image" style="max-height:200px;">
<img [src]="product.image" alt="product image" style="max-height:200px;">
</div>
<div class="product-btns">
<button type="button" class="btn-cart" (click)="editProduct(member.id)">
<button type="button" class="btn-cart" (click)="editProduct(product.id)">
Edit Product
</button>
<button type="button" class="btn-buy">
<button type="button" class="btn-buy" (click)="generateCoupon(product)">
Generate Coupons
</button>
</div>
@ -48,7 +67,10 @@
<div class="product-info">
<div class="product-info-top">
<h2 class="sm-title">@product.CategoryID</h2>
<button class="btn2" style="border:none;"><i class="fa fa-trash"></i></button>
<button class="btn2" style="border:none;" (click)="deleteProduct(product)">
<mat-icon [svgIcon]="'heroicons_outline:trash'" style="color: #1e293b;">
</mat-icon>
</button>
</div>
<a href="#" class="product-name">@product.ProductName</a>
<p class="product-price"><span></span></p>
@ -59,7 +81,9 @@
</div>
</ng-container>
</div>
<mat-paginator class="sticky sm:absolute sm:inset-x-0 sm:bottom-0 border-b sm:border-t sm:border-b-0 z-10 bg-gray-50 dark:bg-transparent" [pageSizeOptions]="[5, 10, 25, 100]" [showFirstLastButtons]="true"></mat-paginator>
<mat-paginator
class="sticky sm:absolute sm:inset-x-0 sm:bottom-0 border-b sm:border-t sm:border-b-0 z-10 bg-gray-50 dark:bg-transparent"
[pageSizeOptions]="[5, 10, 25, 100]" [showFirstLastButtons]="true"></mat-paginator>
<ng-template #noProducts>
<div class="p-8 sm:p-16 border-t text-4xl font-semibold tracking-tight text-center">There are no products!
</div>

175
src/app/pages/admin/Products/ProductList/product-list.component.ts

@ -1,102 +1,153 @@
import { Component, OnInit, ViewChild } from '@angular/core';
import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { MatPaginator } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
import { Router } from '@angular/router';
import { ConfirmBoxEvokeService } from '@costlydeveloper/ngx-awesome-popup';
import { tesoMediaWatcherService } from '@teso/services/media-watcher';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { AdminCouponDialogComponent } from '../AdminCouponDialog/admin-coupon-dialog.component';
import { CouponDialogComponent } from '../CouponDialog/coupon-dialog.component';
@Component({
selector: 'app-product-list',
templateUrl: './product-list.component.html',
styleUrls: ['./product-list.component.scss']
})
export class ProductListComponent implements OnInit {
data: { teamMembers: { id: string; avatar: string; name: string; email: string; phone: string; title: string; }[]; };
export class ProductListComponent implements OnInit, OnDestroy {
data: { teamproducts: { id: string; image: string; name: string; email: string; phone: string; title: string; }[]; };
@ViewChild(MatPaginator) private _paginator: MatPaginator;
@ViewChild(MatSort) private _sort: MatSort;
constructor(private router: Router,) {
isScreenSmall: boolean;
private _unsubscribeAll: Subject<any> = new Subject<any>();
constructor(private router: Router, private _tesoMediaWatcherService: tesoMediaWatcherService,
public dialog: MatDialog, private confirmBoxEvokeService: ConfirmBoxEvokeService) { }
ngOnDestroy(): void {
// Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.complete();
}
ngOnInit(): void {
// Subscribe to media changes
this._tesoMediaWatcherService.onMediaChange$
.pipe(takeUntil(this._unsubscribeAll))
.subscribe(({ matchingAliases }) => {
// Check if the screen is small
this.isScreenSmall = !matchingAliases.includes('md');
});
this.data = {
teamMembers : [
teamproducts: [
{
id : '2bfa2be5-7688-48d5-b5ac-dc0d9ac97f14',
avatar: 'assets/images/brands/bagset.png',
name : 'Nadia Mcknight',
email : 'nadiamcknight@mail.com',
phone : '+1-943-511-2203',
title : 'Project Director'
id: '2bfa2be5-7688-48d5-b5ac-dc0d9ac97f14',
image: 'assets/images/brands/bagset.png',
name: 'Nadia Mcknight',
email: 'nadiamcknight@mail.com',
phone: '+1-943-511-2203',
title: 'Project Director'
},
{
id : '77a4383b-b5a5-4943-bc46-04c3431d1566',
avatar: 'assets/images/brands/bagset.png',
name : 'Best Blackburn',
email : 'blackburn.best@beadzza.me',
phone : '+1-814-498-3701',
title : 'Senior Developer'
id: '77a4383b-b5a5-4943-bc46-04c3431d1566',
image: 'assets/images/brands/bagset.png',
name: 'Best Blackburn',
email: 'blackburn.best@beadzza.me',
phone: '+1-814-498-3701',
title: 'Senior Developer'
},
{
id : '8bb0f597-673a-47ca-8c77-2f83219cb9af',
avatar: 'assets/images/brands/bagset.png',
name : 'Duncan Carver',
email : 'duncancarver@mail.info',
phone : '+1-968-547-2111',
title : 'Senior Developer'
id: '8bb0f597-673a-47ca-8c77-2f83219cb9af',
image: 'assets/images/brands/bagset.png',
name: 'Duncan Carver',
email: 'duncancarver@mail.info',
phone: '+1-968-547-2111',
title: 'Senior Developer'
},
{
id : 'c318e31f-1d74-49c5-8dae-2bc5805e2fdb',
avatar: 'assets/images/brands/bagset.png',
name : 'Martin Richards',
email : 'martinrichards@mail.biz',
phone : '+1-902-500-2668',
title : 'Junior Developer'
id: 'c318e31f-1d74-49c5-8dae-2bc5805e2fdb',
image: 'assets/images/brands/bagset.png',
name: 'Martin Richards',
email: 'martinrichards@mail.biz',
phone: '+1-902-500-2668',
title: 'Junior Developer'
},
{
id : '0a8bc517-631a-4a93-aacc-000fa2e8294c',
avatar: 'assets/images/brands/bagset.png',
name : 'Candice Munoz',
email : 'candicemunoz@mail.co.uk',
phone : '+1-838-562-2769',
title : 'Lead Designer'
id: '0a8bc517-631a-4a93-aacc-000fa2e8294c',
image: 'assets/images/brands/bagset.png',
name: 'Candice Munoz',
email: 'candicemunoz@mail.co.uk',
phone: '+1-838-562-2769',
title: 'Lead Designer'
},
{
id : 'a4c9945a-757b-40b0-8942-d20e0543cabd',
avatar: 'assets/images/brands/bagset.png',
name : 'Vickie Mosley',
email : 'vickiemosley@mail.net',
phone : '+1-939-555-3054',
title : 'Designer'
id: 'a4c9945a-757b-40b0-8942-d20e0543cabd',
image: 'assets/images/brands/bagset.png',
name: 'Vickie Mosley',
email: 'vickiemosley@mail.net',
phone: '+1-939-555-3054',
title: 'Designer'
},
{
id : 'b8258ccf-48b5-46a2-9c95-e0bd7580c645',
avatar: 'assets/images/brands/bagset.png',
name : 'Tina Harris',
email : 'tinaharris@mail.ca',
phone : '+1-933-464-2431',
title : 'Designer'
id: 'b8258ccf-48b5-46a2-9c95-e0bd7580c645',
image: 'assets/images/brands/bagset.png',
name: 'Tina Harris',
email: 'tinaharris@mail.ca',
phone: '+1-933-464-2431',
title: 'Designer'
},
{
id : 'f004ea79-98fc-436c-9ba5-6cfe32fe583d',
avatar: 'assets/images/brands/bagset.png',
name : 'Holt Manning',
email : 'holtmanning@mail.org',
phone : '+1-822-531-2600',
title : 'Marketing Manager'
id: 'f004ea79-98fc-436c-9ba5-6cfe32fe583d',
image: 'assets/images/brands/bagset.png',
name: 'Holt Manning',
email: 'holtmanning@mail.org',
phone: '+1-822-531-2600',
title: 'Marketing Manager'
},
{
id : '8b69fe2d-d7cc-4a3d-983d-559173e37d37',
avatar: 'assets/images/brands/bagset.png',
name : 'Misty Ramsey',
email : 'mistyramsey@mail.us',
phone : '+1-990-457-2106',
title : 'Consultant'
id: '8b69fe2d-d7cc-4a3d-983d-559173e37d37',
image: 'assets/images/brands/bagset.png',
name: 'Misty Ramsey',
email: 'mistyramsey@mail.us',
phone: '+1-990-457-2106',
title: 'Consultant'
}
]
]
}
}
createProduct() {
this.router.navigate(['products/create-product']);
}
editProduct(productID:any) {
this.router.navigate(['products/'+productID]);
editProduct(productID: any) {
this.router.navigate(['products/' + productID]);
}
generateCoupon(product: any) {
if (!true) {
const dialogRef = this.dialog.open(CouponDialogComponent, {
disableClose: true,
hasBackdrop: true,
data: { product: product },
});
} else {
const dialogRef = this.dialog.open(AdminCouponDialogComponent, {
disableClose: true,
hasBackdrop: true,
data: { product: product },
});
}
}
deleteProduct(product: any) {
this.confirmBoxEvokeService.danger("Delete Product",
"Are you sure you would like to delete this product from your inventory ?",
"Yes", "No")
.subscribe((response: any) => {
console.log(response)
if (response.clickedButtonID === "yes") {
this.data.teamproducts = this.data.teamproducts.filter((e) => e == product);
}
});
}
}

25
src/app/pages/admin/Products/products.component.spec.ts

@ -1,25 +0,0 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ProductsComponent } from './products.component';
describe('ProductsComponent', () => {
let component: ProductsComponent;
let fixture: ComponentFixture<ProductsComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ ProductsComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(ProductsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

18
src/app/pages/admin/Products/products.module.ts

@ -23,17 +23,28 @@ import { NewProductComponent } from './NewProduct/new-product.component';
import { ProductListComponent } from './ProductList/product-list.component';
import { MatSidenavModule } from '@angular/material/sidenav';
import { EditProductComponent } from './EditProduct/edit-product.component';
import { QuillModule } from 'ngx-quill';
import { MatTabsModule } from '@angular/material/tabs';
import { SanitizerUrlPipe } from 'app/pipes/urlsanitizer.pipe';
import { CouponDialogComponent } from './CouponDialog/coupon-dialog.component';
import { MatDialogModule } from '@angular/material/dialog';
import { AdminCouponDialogComponent } from './AdminCouponDialog/admin-coupon-dialog.component';
import { BusinessLookUpComponent } from './BusinessLookUp/business-look-up.component';
@NgModule({
declarations: [
ProductsComponent,
NewProductComponent,
ProductListComponent,
EditProductComponent
EditProductComponent,
SanitizerUrlPipe,
CouponDialogComponent,
AdminCouponDialogComponent,
BusinessLookUpComponent
],
imports: [
RouterModule.forChild(productsRoutes),
QuillModule.forRoot(),
MatButtonModule,
MatButtonToggleModule,
MatDividerModule,
@ -46,7 +57,8 @@ import { EditProductComponent } from './EditProduct/edit-product.component';
MatSidenavModule,
MatSortModule,
MatTableModule,
// MatTabsModule,
MatTabsModule,
MatDialogModule,
TranslocoModule,
SharedModule,
MatPaginatorModule,

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

@ -1,99 +1,105 @@
<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="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 *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">
<!-- Logo -->
<div class="w-20">
<img src="assets/images/logo.png">
</div>
<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>Don't have an account?</div>
<a class="ml-1 text-primary-500 hover:underline" [routerLink]="['/sign-up']">Sign up
</a>
</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>
<!-- 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 -->
<form class="mt-8" [formGroup]="signInForm" #signInNgForm="ngForm">
<!-- Sign in form -->
<!-- Email field -->
<mat-form-field class="w-full">
<mat-label>Email address</mat-label>
<input id="email" matInput [formControlName]="'email'">
<mat-error *ngIf="signInForm.get('email').hasError('required')">
Email address is required
</mat-error>
<mat-error *ngIf="signInForm.get('email').hasError('email')">
Please enter a valid email address
</mat-error>
</mat-form-field>
<mat-form-field appearance="outline">
<!-- Password field -->
<mat-form-field class="w-full">
<mat-label>Password</mat-label>
<input id="password" matInput type="password" [formControlName]="'password'" #passwordField>
<button mat-icon-button type="button" (click)="passwordField.type === 'password' ? passwordField.type = 'text' : passwordField.type = 'password'" matSuffix>
<mat-icon
class="icon-size-5"
*ngIf="passwordField.type === 'password'"
[svgIcon]="'heroicons_solid:eye'"></mat-icon>
<mat-icon
class="icon-size-5"
*ngIf="passwordField.type === 'text'"
[svgIcon]="'heroicons_solid:eye-off'"></mat-icon>
</button>
<mat-error>
Password is required
</mat-error>
<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-1.5">
<mat-checkbox [color]="'primary'" [formControlName]="'rememberMe'">
<div class="inline-flex items-end justify-between w-full mt-3.5">
<mat-checkbox [color]="'primary'">
Remember me
</mat-checkbox>
<a class="text-md font-medium text-primary-500 hover:underline" [routerLink]="['/forgot-password']">Forgot password?
</a>
</div>
<!-- Submit button -->
<button class="teso-mat-button-large w-full mt-6" mat-flat-button [color]="'primary'" [disabled]="signInForm.disabled" (click)="signIn()">
<span *ngIf="!signInForm.disabled">
<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>
<!-- <mat-progress-spinner
*ngIf="signInForm.disabled"
[diameter]="24"
[mode]="'indeterminate'"></mat-progress-spinner> -->
</button>
</div>
<!-- Separator -->
<div class="flex items-center mt-8">
<div class="flex-auto mt-px border-t"></div>
<div class="mx-2 text-secondary">Or continue with</div>
<div class="flex-auto mt-px border-t"></div>
</div>
</form>
</div>
</div>
<div id="welcomeBoard" class="relative hidden md:flex flex-auto items-center justify-center w-1/2 h-full p-16 lg:px-28 overflow-hidden bg-gray-800 dark:border-l">
<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="z-10 relative w-full max-w-2xl">
<div class="text-7xl font-bold leading-none text-gray-100 welcomeText">
<div>Welcome to</div>
<div>our community</div>
</div>
<div class="mt-6 text-lg tracking-tight leading-6 text-gray-400 welcomeText">
Teso is an exclusive marketing company with specialized mechanics to boost sales and provide undoubtedly the best of services and user experiences to it’s users.
</div>
<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>

62
src/app/pages/auth/sign-in/sign-in.component.ts

@ -3,26 +3,28 @@ import { FormBuilder, FormGroup, NgForm, Validators } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import { tesoAnimations } from '@teso/animations';
import { tesoAlertType } from '@teso/components/alert';
import { tesoMediaWatcherService } from '@teso/services/media-watcher';
import { AuthService } from 'app/core/auth/auth.service';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
@Component({
selector : 'auth-sign-in',
templateUrl : './sign-in.component.html',
styleUrls : ['./sign-in.component.css'],
selector: 'auth-sign-in',
templateUrl: './sign-in.component.html',
styleUrls: ['./sign-in.component.css'],
encapsulation: ViewEncapsulation.Emulated,
animations : tesoAnimations
animations: tesoAnimations
})
export class AuthSignInComponent implements OnInit
{
@ViewChild('signInNgForm') signInNgForm: NgForm;
export class AuthSignInComponent implements OnInit {
alert: { type: tesoAlertType; message: string } = {
type : 'success',
type: 'success',
message: ''
};
signInForm: FormGroup;
isScreenSmall: boolean;
showAlert: boolean = false;
private _unsubscribeAll: Subject<any> = new Subject<any>();
/**
* Constructor
*/
@ -30,9 +32,9 @@ export class AuthSignInComponent implements OnInit
private _activatedRoute: ActivatedRoute,
private _authService: AuthService,
private _formBuilder: FormBuilder,
private _router: Router
)
{
private _router: Router,
private _tesoMediaWatcherService: tesoMediaWatcherService
) {
}
// -----------------------------------------------------------------------------------------------------
@ -42,13 +44,14 @@ export class AuthSignInComponent implements OnInit
/**
* On init
*/
ngOnInit(): void
{
ngOnInit(): void {
// Create the form
this.signInForm = this._formBuilder.group({
email : ['support@tesoapp.com', [Validators.required, Validators.email]],
password : ['admin', Validators.required],
rememberMe: ['']
this._tesoMediaWatcherService.onMediaChange$
.pipe(takeUntil(this._unsubscribeAll))
.subscribe(({ matchingAliases }) => {
// Check if the screen is small
this.isScreenSmall = !matchingAliases.includes('md');
});
}
@ -59,22 +62,20 @@ export class AuthSignInComponent implements OnInit
/**
* Sign in
*/
signIn(): void
{
signIn(): void {
// Return if the form is invalid
if ( this.signInForm.invalid )
{
return;
}
var signInForm = this._formBuilder.group({
email: ['support@tesoapp.com', [Validators.required, Validators.email]],
password: ['admin', Validators.required],
rememberMe: ['']
});
// Disable the form
this.signInForm.disable();
// Hide the alert
this.showAlert = false;
// Sign in
this._authService.signIn(this.signInForm.value)
this._authService.signIn(signInForm.value)
.subscribe(
() => {
@ -91,14 +92,13 @@ export class AuthSignInComponent implements OnInit
(response) => {
// Re-enable the form
this.signInForm.enable();
signInForm.enable();
// Reset the form
this.signInNgForm.resetForm();
// Set the alert
this.alert = {
type : 'error',
type: 'error',
message: 'Wrong email or password'
};

4
src/app/pages/auth/sign-in/sign-in.module.ts

@ -11,6 +11,8 @@ import { tesoAlertModule } from '@teso/components/alert';
import { SharedModule } from 'app/shared/shared.module';
import { AuthSignInComponent } from 'app/pages/auth/sign-in/sign-in.component';
import { authSignInRoutes } from 'app/pages/auth/sign-in/sign-in.routing';
import { NgxMatIntlTelInputModule } from 'ngx-mat-intl-tel-input';
import {MatCardModule} from '@angular/material/card';
@NgModule({
declarations: [
@ -20,6 +22,8 @@ import { authSignInRoutes } from 'app/pages/auth/sign-in/sign-in.routing';
RouterModule.forChild(authSignInRoutes),
MatButtonModule,
MatCheckboxModule,
MatCardModule,
NgxMatIntlTelInputModule,
MatFormFieldModule,
MatIconModule,
MatInputModule,

16
src/app/pipes/urlsanitizer.pipe.ts

@ -0,0 +1,16 @@
import { Pipe, PipeTransform } from "@angular/core";
import { DomSanitizer, SafeUrl } from "@angular/platform-browser";
@Pipe({
name: 'sanitizeUrl'
})
export class SanitizerUrlPipe implements PipeTransform {
constructor(
private sanitize: DomSanitizer
) { }
transform(value: string): SafeUrl {
return this.sanitize.bypassSecurityTrustUrl(value);
}
}

84
src/assets/styles/splash-screen.css

@ -1,84 +0,0 @@
body teso-splash-screen {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #111827;
color: #F9FAFB;
z-index: 999999;
pointer-events: none;
opacity: 1;
visibility: visible;
transition: opacity 400ms cubic-bezier(0.4, 0, 0.2, 1);
}
body teso-splash-screen img {
width: 120px;
max-width: 120px;
}
body teso-splash-screen .spinner {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 40px;
width: 56px;
}
body teso-splash-screen .spinner>div {
width: 12px;
height: 12px;
background-color: #1E96F7;
border-radius: 100%;
display: inline-block;
-webkit-animation: teso-bouncedelay 1s infinite ease-in-out both;
animation: teso-bouncedelay 1s infinite ease-in-out both;
}
body teso-splash-screen .spinner .bounce1 {
-webkit-animation-delay: -0.32s;
animation-delay: -0.32s;
}
body teso-splash-screen .spinner .bounce2 {
-webkit-animation-delay: -0.16s;
animation-delay: -0.16s;
}
@-webkit-keyframes teso-bouncedelay {
0%,
80%,
100% {
-webkit-transform: scale(0)
}
40% {
-webkit-transform: scale(1.0)
}
}
@keyframes teso-bouncedelay {
0%,
80%,
100% {
-webkit-transform: scale(0);
transform: scale(0);
}
40% {
-webkit-transform: scale(1.0);
transform: scale(1.0);
}
}
body:not(.teso-splash-screen-hidden) {
overflow: hidden;
}
body.teso-splash-screen-hidden teso-splash-screen {
visibility: hidden;
opacity: 0;
}

4
src/index.html

@ -22,10 +22,6 @@
<!-- Icon fonts -->
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<!-- Splash screen styles -->
<link href="assets/styles/splash-screen.css" rel="stylesheet">
</head>
<body>

3
src/styles/styles.scss

@ -3,4 +3,5 @@
/* @ Import/write your custom styles here.
/* @ Styles from this file will override 'vendors.scss' and teso's base styles.
/* ----------------------------------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------------------------------------- */
@import "node_modules/@costlydeveloper/ngx-awesome-popup/styles/theme";
Loading…
Cancel
Save