diff --git a/config/custom.webpack.config.js b/config/custom.webpack.config.js
index 4709f2c..e6d19be 100644
--- a/config/custom.webpack.config.js
+++ b/config/custom.webpack.config.js
@@ -9,5 +9,10 @@ module.exports = (config, options) => {
fs: 'empty'
};
+ config.resolve.alias = {
+ ...config.resolve.alias,
+ '@ucap/lg-scss': path.resolve(__dirname, '..', 'src/assets/scss')
+ };
+
return config;
};
diff --git a/package-lock.json b/package-lock.json
index 28c74fb..b161522 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -25,9 +25,9 @@
"dev": true
},
"source-map-support": {
- "version": "0.5.17",
- "resolved": "https://nexus.loafle.net/repository/npm-all/source-map-support/-/source-map-support-0.5.17.tgz",
- "integrity": "sha512-bwdKOBZ5L0gFRh4KOxNap/J/MpvX9Yxsq9lFDx65s3o7F/NiHy7JRaGIS8MwW6tZPAq9UXE207Il0cfcb5yu/Q==",
+ "version": "0.5.19",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/source-map-support/-/source-map-support-0.5.19.tgz",
+ "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
"dev": true,
"requires": {
"buffer-from": "^1.0.0",
@@ -35,9 +35,9 @@
}
},
"ts-node": {
- "version": "8.9.0",
- "resolved": "https://nexus.loafle.net/repository/npm-all/ts-node/-/ts-node-8.9.0.tgz",
- "integrity": "sha512-rwkXfOs9zmoHrV8xE++dmNd6ZIS+nmHHCxcV53ekGJrxFLMbp+pizpPS07ARvhwneCIECPppOwbZHvw9sQtU4w==",
+ "version": "8.10.1",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/ts-node/-/ts-node-8.10.1.tgz",
+ "integrity": "sha512-bdNz1L4ekHiJul6SHtZWs1ujEKERJnHs4HxN7rjTyyVOFf3HaJ6sLqe6aPG62XTzAB/63pKRh5jTSWL0D7bsvw==",
"dev": true,
"requires": {
"arg": "^4.1.0",
@@ -50,12 +50,12 @@
}
},
"@angular-devkit/architect": {
- "version": "0.901.1",
- "resolved": "https://nexus.loafle.net/repository/npm-all/@angular-devkit/architect/-/architect-0.901.1.tgz",
- "integrity": "sha512-foWDAurMfBDYLAJxHpTFkJBub1c2A8+eWHbBjgqIHmT8xadnE7t8nSA9XDl+k/kIoWw/qFU+6IttPirudYc/vw==",
+ "version": "0.901.7",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/@angular-devkit/architect/-/architect-0.901.7.tgz",
+ "integrity": "sha512-yW/PUEqle55QihOFbmeNXaVTodhfeXkteoFDUpz+YpX3xiQDXDtNbIJSzKOQTojtBKdSMKMvZkQLr+RAa7/1EA==",
"dev": true,
"requires": {
- "@angular-devkit/core": "9.1.1",
+ "@angular-devkit/core": "9.1.7",
"rxjs": "6.5.4"
},
"dependencies": {
@@ -324,9 +324,9 @@
}
},
"@angular-devkit/core": {
- "version": "9.1.1",
- "resolved": "https://nexus.loafle.net/repository/npm-all/@angular-devkit/core/-/core-9.1.1.tgz",
- "integrity": "sha512-57MNew2u1QwVb69jxZyhXgdW9kqcGyWyRy2ui/hWCkWLg7RumWtyypmdTs89FNExB4HqtXlQ2eO3JZxfs7QR3w==",
+ "version": "9.1.7",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/@angular-devkit/core/-/core-9.1.7.tgz",
+ "integrity": "sha512-guvolu9Cl+qYMTtedLZD9wCqustJjdqzJ2psD2C1Sr1LrX9T0mprmDldR/YnhsitThveJEb6sM/0EvqWxoSvKw==",
"dev": true,
"requires": {
"ajv": "6.12.0",
@@ -421,9 +421,9 @@
"integrity": "sha512-74gY7onajmmnksy5E0/32bFv3B9NuWxV64kqD15YjGrh8AWe1BHt5enQI+rJ2tO8m2DKnwZsctis6k0Kcy+YKQ=="
},
"@angular/cdk": {
- "version": "9.2.1",
- "resolved": "https://nexus.loafle.net/repository/npm-all/@angular/cdk/-/cdk-9.2.1.tgz",
- "integrity": "sha512-aSG1UNPszkSnpNuDCNd7ZgT29oQ8vqHPmoqjvJI0JkEv3i6uEs5tRuhWl3TK39wDNuwdlq0AY47XTa/0Ppb5RQ==",
+ "version": "9.2.4",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/@angular/cdk/-/cdk-9.2.4.tgz",
+ "integrity": "sha512-iw2+qHMXHYVC6K/fttHeNHIieSKiTEodVutZoOEcBu9rmRTGbLB26V/CRsfIRmA1RBk+uFYWc6UQZnMC3RdnJQ==",
"requires": {
"parse5": "^5.0.0"
}
@@ -715,9 +715,9 @@
"integrity": "sha512-E9XZH5Dl+9MWG3MDC6wrKllhA8Rljpz66HOIeqKv2fHPed8kzuJZU3WJWLtbhDAXFwtGTyTZ4c82ZLSmqwTorg=="
},
"@angular/flex-layout": {
- "version": "9.0.0-beta.29",
- "resolved": "https://nexus.loafle.net/repository/npm-all/@angular/flex-layout/-/flex-layout-9.0.0-beta.29.tgz",
- "integrity": "sha512-93sxR+kYfYMOdnlWL0Q77FZ428gg8XnBu0YZm6GsCdkw/vLggIT/G1ZAqHlCPIODt6pxmCJ5KXh4ShvniIYDsA=="
+ "version": "9.0.0-beta.31",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/@angular/flex-layout/-/flex-layout-9.0.0-beta.31.tgz",
+ "integrity": "sha512-g94u2mecDl87ORvFRuOBshV/S/ETE4bybClU2e1xXKWNG+rhRHchChneHSonc29ZLyROTjHhmAtKOYojL92uLA=="
},
"@angular/forms": {
"version": "9.0.7",
@@ -731,14 +731,14 @@
"dev": true
},
"@angular/material": {
- "version": "9.2.1",
- "resolved": "https://nexus.loafle.net/repository/npm-all/@angular/material/-/material-9.2.1.tgz",
- "integrity": "sha512-nqn/0Eg04DxwnkRGSM1xnmGgtfHYOBcEPbFeTu8c1qAbjFEozd6tpw4y6dQrCCL/JLNIRQPsxsUsVnKeWDF/4Q=="
+ "version": "9.2.4",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/@angular/material/-/material-9.2.4.tgz",
+ "integrity": "sha512-LkoTXE6B0slvMhvfZDdPWaz4yaYLkaAp5VSPunI9pxGsPxzqEV9e210wC1/sjG/76Nk8Ep7/2z9XKac8Q9bMwA=="
},
"@angular/material-moment-adapter": {
- "version": "9.2.1",
- "resolved": "https://nexus.loafle.net/repository/npm-all/@angular/material-moment-adapter/-/material-moment-adapter-9.2.1.tgz",
- "integrity": "sha512-i3tbL3DJQspO23cPAgW6pAvztuv2D8tkGhxl+bTUlq8FgGBqKrOa0T/maN1fik46AXZojowUVZoCG7EvV2T7nQ=="
+ "version": "9.2.4",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/@angular/material-moment-adapter/-/material-moment-adapter-9.2.4.tgz",
+ "integrity": "sha512-V5xkL+YUec3nDGRaJB72mJTUtdUvGaG9WCQEdr45viDWFGjQaEpS6msuScBLp0PwsN8Wt0n69eZg0ULgxPBa5g=="
},
"@angular/platform-browser": {
"version": "9.0.7",
@@ -1001,15 +1001,15 @@
}
},
"@babel/helper-replace-supers": {
- "version": "7.8.6",
- "resolved": "https://nexus.loafle.net/repository/npm-all/@babel/helper-replace-supers/-/helper-replace-supers-7.8.6.tgz",
- "integrity": "sha512-PeMArdA4Sv/Wf4zXwBKPqVj7n9UF/xg6slNRtZW84FM7JpE1CbG8B612FyM4cxrf4fMAMGO0kR7voy1ForHHFA==",
+ "version": "7.9.6",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/@babel/helper-replace-supers/-/helper-replace-supers-7.9.6.tgz",
+ "integrity": "sha512-qX+chbxkbArLyCImk3bWV+jB5gTNU/rsze+JlcF6Nf8tVTigPJSI1o1oBow/9Resa1yehUO9lIipsmu9oG4RzA==",
"dev": true,
"requires": {
"@babel/helper-member-expression-to-functions": "^7.8.3",
"@babel/helper-optimise-call-expression": "^7.8.3",
- "@babel/traverse": "^7.8.6",
- "@babel/types": "^7.8.6"
+ "@babel/traverse": "^7.9.6",
+ "@babel/types": "^7.9.6"
}
},
"@babel/helper-simple-access": {
@@ -1050,14 +1050,14 @@
}
},
"@babel/helpers": {
- "version": "7.9.2",
- "resolved": "https://nexus.loafle.net/repository/npm-all/@babel/helpers/-/helpers-7.9.2.tgz",
- "integrity": "sha512-JwLvzlXVPjO8eU9c/wF9/zOIN7X6h8DYf7mG4CiFRZRvZNKEF5dQ3H3V+ASkHoIB3mWhatgl5ONhyqHRI6MppA==",
+ "version": "7.9.6",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/@babel/helpers/-/helpers-7.9.6.tgz",
+ "integrity": "sha512-tI4bUbldloLcHWoRUMAj4g1bF313M/o6fBKhIsb3QnGVPwRm9JsNf/gqMkQ7zjqReABiffPV6RWj7hEglID5Iw==",
"dev": true,
"requires": {
"@babel/template": "^7.8.3",
- "@babel/traverse": "^7.9.0",
- "@babel/types": "^7.9.0"
+ "@babel/traverse": "^7.9.6",
+ "@babel/types": "^7.9.6"
}
},
"@babel/highlight": {
@@ -1072,9 +1072,9 @@
}
},
"@babel/parser": {
- "version": "7.9.4",
- "resolved": "https://nexus.loafle.net/repository/npm-all/@babel/parser/-/parser-7.9.4.tgz",
- "integrity": "sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA==",
+ "version": "7.9.6",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/@babel/parser/-/parser-7.9.6.tgz",
+ "integrity": "sha512-AoeIEJn8vt+d/6+PXDRPaksYhnlbMIiejioBZvvMQsOjW/JYK6k/0dKnvvP3EhK5GfMBWDPtrxRtegWdAcdq9Q==",
"dev": true
},
"@babel/plugin-proposal-async-generator-functions": {
@@ -1109,9 +1109,9 @@
}
},
"@babel/plugin-proposal-object-rest-spread": {
- "version": "7.9.5",
- "resolved": "https://nexus.loafle.net/repository/npm-all/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.9.5.tgz",
- "integrity": "sha512-VP2oXvAf7KCYTthbUHwBlewbl1Iq059f6seJGsxMizaCdgHIeczOr7FBqELhSqfkIl04Fi8okzWzl63UKbQmmg==",
+ "version": "7.9.6",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.9.6.tgz",
+ "integrity": "sha512-Ga6/fhGqA9Hj+y6whNpPv8psyaK5xzrQwSPsGPloVkvmH+PqW1ixdnfJ9uIO06OjQNYol3PMnfmJ8vfZtkzF+A==",
"dev": true,
"requires": {
"@babel/helper-plugin-utils": "^7.8.3",
@@ -1333,38 +1333,38 @@
}
},
"@babel/plugin-transform-modules-amd": {
- "version": "7.9.0",
- "resolved": "https://nexus.loafle.net/repository/npm-all/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.9.0.tgz",
- "integrity": "sha512-vZgDDF003B14O8zJy0XXLnPH4sg+9X5hFBBGN1V+B2rgrB+J2xIypSN6Rk9imB2hSTHQi5OHLrFWsZab1GMk+Q==",
+ "version": "7.9.6",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.9.6.tgz",
+ "integrity": "sha512-zoT0kgC3EixAyIAU+9vfaUVKTv9IxBDSabgHoUCBP6FqEJ+iNiN7ip7NBKcYqbfUDfuC2mFCbM7vbu4qJgOnDw==",
"dev": true,
"requires": {
"@babel/helper-module-transforms": "^7.9.0",
"@babel/helper-plugin-utils": "^7.8.3",
- "babel-plugin-dynamic-import-node": "^2.3.0"
+ "babel-plugin-dynamic-import-node": "^2.3.3"
}
},
"@babel/plugin-transform-modules-commonjs": {
- "version": "7.9.0",
- "resolved": "https://nexus.loafle.net/repository/npm-all/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.9.0.tgz",
- "integrity": "sha512-qzlCrLnKqio4SlgJ6FMMLBe4bySNis8DFn1VkGmOcxG9gqEyPIOzeQrA//u0HAKrWpJlpZbZMPB1n/OPa4+n8g==",
+ "version": "7.9.6",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.9.6.tgz",
+ "integrity": "sha512-7H25fSlLcn+iYimmsNe3uK1at79IE6SKW9q0/QeEHTMC9MdOZ+4bA+T1VFB5fgOqBWoqlifXRzYD0JPdmIrgSQ==",
"dev": true,
"requires": {
"@babel/helper-module-transforms": "^7.9.0",
"@babel/helper-plugin-utils": "^7.8.3",
"@babel/helper-simple-access": "^7.8.3",
- "babel-plugin-dynamic-import-node": "^2.3.0"
+ "babel-plugin-dynamic-import-node": "^2.3.3"
}
},
"@babel/plugin-transform-modules-systemjs": {
- "version": "7.9.0",
- "resolved": "https://nexus.loafle.net/repository/npm-all/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.9.0.tgz",
- "integrity": "sha512-FsiAv/nao/ud2ZWy4wFacoLOm5uxl0ExSQ7ErvP7jpoihLR6Cq90ilOFyX9UXct3rbtKsAiZ9kFt5XGfPe/5SQ==",
+ "version": "7.9.6",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.9.6.tgz",
+ "integrity": "sha512-NW5XQuW3N2tTHim8e1b7qGy7s0kZ2OH3m5octc49K1SdAKGxYxeIx7hiIz05kS1R2R+hOWcsr1eYwcGhrdHsrg==",
"dev": true,
"requires": {
"@babel/helper-hoist-variables": "^7.8.3",
"@babel/helper-module-transforms": "^7.9.0",
"@babel/helper-plugin-utils": "^7.8.3",
- "babel-plugin-dynamic-import-node": "^2.3.0"
+ "babel-plugin-dynamic-import-node": "^2.3.3"
}
},
"@babel/plugin-transform-modules-umd": {
@@ -1567,9 +1567,9 @@
}
},
"@babel/runtime": {
- "version": "7.9.2",
- "resolved": "https://nexus.loafle.net/repository/npm-all/@babel/runtime/-/runtime-7.9.2.tgz",
- "integrity": "sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q==",
+ "version": "7.9.6",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/@babel/runtime/-/runtime-7.9.6.tgz",
+ "integrity": "sha512-64AF1xY3OAkFHqOb9s4jpgk1Mm5vDZ4L3acHvAml+53nO1XbXLuDodsVpO4OIUsmemlUHMxNdYMNJmsvOwLrvQ==",
"requires": {
"regenerator-runtime": "^0.13.4"
}
@@ -1586,29 +1586,29 @@
}
},
"@babel/traverse": {
- "version": "7.9.5",
- "resolved": "https://nexus.loafle.net/repository/npm-all/@babel/traverse/-/traverse-7.9.5.tgz",
- "integrity": "sha512-c4gH3jsvSuGUezlP6rzSJ6jf8fYjLj3hsMZRx/nX0h+fmHN0w+ekubRrHPqnMec0meycA2nwCsJ7dC8IPem2FQ==",
+ "version": "7.9.6",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/@babel/traverse/-/traverse-7.9.6.tgz",
+ "integrity": "sha512-b3rAHSjbxy6VEAvlxM8OV/0X4XrG72zoxme6q1MOoe2vd0bEc+TwayhuC1+Dfgqh1QEG+pj7atQqvUprHIccsg==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.8.3",
- "@babel/generator": "^7.9.5",
+ "@babel/generator": "^7.9.6",
"@babel/helper-function-name": "^7.9.5",
"@babel/helper-split-export-declaration": "^7.8.3",
- "@babel/parser": "^7.9.0",
- "@babel/types": "^7.9.5",
+ "@babel/parser": "^7.9.6",
+ "@babel/types": "^7.9.6",
"debug": "^4.1.0",
"globals": "^11.1.0",
"lodash": "^4.17.13"
},
"dependencies": {
"@babel/generator": {
- "version": "7.9.5",
- "resolved": "https://nexus.loafle.net/repository/npm-all/@babel/generator/-/generator-7.9.5.tgz",
- "integrity": "sha512-GbNIxVB3ZJe3tLeDm1HSn2AhuD/mVcyLDpgtLXa5tplmWrJdF/elxB56XNqCuD6szyNkDi6wuoKXln3QeBmCHQ==",
+ "version": "7.9.6",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/@babel/generator/-/generator-7.9.6.tgz",
+ "integrity": "sha512-+htwWKJbH2bL72HRluF8zumBxzuX0ZZUFl3JLNyoUjM/Ho8wnVpPXM6aUz8cfKDqQ/h7zHqKt4xzJteUosckqQ==",
"dev": true,
"requires": {
- "@babel/types": "^7.9.5",
+ "@babel/types": "^7.9.6",
"jsesc": "^2.5.1",
"lodash": "^4.17.13",
"source-map": "^0.5.0"
@@ -1638,9 +1638,9 @@
}
},
"@babel/types": {
- "version": "7.9.5",
- "resolved": "https://nexus.loafle.net/repository/npm-all/@babel/types/-/types-7.9.5.tgz",
- "integrity": "sha512-XjnvNqenk818r5zMaba+sLQjnbda31UfUURv3ei0qPQw4u+j2jMyJ5b11y8ZHYTRSI3NnInQkkkRT4fLqqPdHg==",
+ "version": "7.9.6",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/@babel/types/-/types-7.9.6.tgz",
+ "integrity": "sha512-qxXzvBO//jO9ZnoasKF1uJzHd2+M6Q2ZPIVfnFps8JJvXy0ZBbwbNOmE6SGIY5XOY6d1Bo5lb9d9RJ8nv3WSeA==",
"dev": true,
"requires": {
"@babel/helper-validator-identifier": "^7.9.5",
@@ -1655,29 +1655,29 @@
"dev": true
},
"@ngrx/effects": {
- "version": "9.1.0",
- "resolved": "https://nexus.loafle.net/repository/npm-all/@ngrx/effects/-/effects-9.1.0.tgz",
- "integrity": "sha512-DPMtp/2YTrvuxMqaehQ39FeoNODuMcRB6xDImvrnMFPIkUcP3mDHnIg6UB5c2JvfHlZZClKR+Fb+RE2nuVub8w=="
+ "version": "9.1.2",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/@ngrx/effects/-/effects-9.1.2.tgz",
+ "integrity": "sha512-H9jbGUzP5izk9Ap8BQJicO1+xheyDyHBbvv6b1NkaRHpDizhPOSBjoFWExFfsejXo0dafaIsu6aI+y+Fp+LSsg=="
},
"@ngrx/entity": {
- "version": "9.1.0",
- "resolved": "https://nexus.loafle.net/repository/npm-all/@ngrx/entity/-/entity-9.1.0.tgz",
- "integrity": "sha512-MrjdJeJIaUpPogxzK7S/z5Cc/6XWyyQfUgkXrTS20JZyX4amjVZYL3oU3FF3JhXO9ZkhaBFi07IDPEtvyYl/Mg=="
+ "version": "9.1.2",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/@ngrx/entity/-/entity-9.1.2.tgz",
+ "integrity": "sha512-V6sN/W2rLZFFMlzML3AcHYrA6h9mRMpSVqpsMF4ZLhubmQWP4BG24vyJX8ExtH46OU7ick8aiYqrNzF4rJMiag=="
},
"@ngrx/router-store": {
- "version": "9.1.0",
- "resolved": "https://nexus.loafle.net/repository/npm-all/@ngrx/router-store/-/router-store-9.1.0.tgz",
- "integrity": "sha512-zdIBUsfyrrA64mUqajHMARSwttOAOJ5FSyVJLHQfBlz9/vil6Kby1KhVLEG7mGmkz799YYlIM135QsrJhgf7wA=="
+ "version": "9.1.2",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/@ngrx/router-store/-/router-store-9.1.2.tgz",
+ "integrity": "sha512-zqAm7fOdJ34dY2Tlts2YV9MDcDP2CqB+hLEytjo/bOIkUo/lQA1R5VAUI+Z8+tUlmPqSVIJkzsGuCxO6XnLEjQ=="
},
"@ngrx/store": {
- "version": "9.1.0",
- "resolved": "https://nexus.loafle.net/repository/npm-all/@ngrx/store/-/store-9.1.0.tgz",
- "integrity": "sha512-Ah2nApPZXTvKl7ybaFMIFwEdMg8b7PvIKATigbDTUftAsFxked2+T7blNv6aCgSfEqbOVMBTYPk7OsLCYZoz4Q=="
+ "version": "9.1.2",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/@ngrx/store/-/store-9.1.2.tgz",
+ "integrity": "sha512-FQXFonF8hSGJDqgLaoWHy2mkeJwVdoa3jLoT1YpkJWxsFMG4U0T6JYG4VrtuymDgo9XwWBBJqIiNpdTgHhofSQ=="
},
"@ngrx/store-devtools": {
- "version": "9.1.0",
- "resolved": "https://nexus.loafle.net/repository/npm-all/@ngrx/store-devtools/-/store-devtools-9.1.0.tgz",
- "integrity": "sha512-KDvuNBxfTgDHjESiouEoPXq8GCUYbHnA+jOs8MzzwVDmxNLeZuDy3amaVzKv9Q5nncI6mNGcXOzTKWzapET8Sw==",
+ "version": "9.1.2",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/@ngrx/store-devtools/-/store-devtools-9.1.2.tgz",
+ "integrity": "sha512-i9gwj9FlUVrei9yMwwFx15RV0JWOI73cjbjKhZ2lUWmCF6bfZeuPYZmEs3/80L8r3R9nZWnBoX9xxQslte65iw==",
"dev": true
},
"@ngtools/webpack": {
@@ -1979,15 +1979,15 @@
"dev": true
},
"@types/node": {
- "version": "12.12.36",
- "resolved": "https://nexus.loafle.net/repository/npm-all/@types/node/-/node-12.12.36.tgz",
- "integrity": "sha512-hmmypvyO/uTLFYCYu6Hlb3ydeJ11vXRxg8/WJ0E3wvwmPO0y47VqnfmXFVuWlysO0Zyj+je1Y33rQeuYkZ51GQ==",
+ "version": "12.12.42",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/@types/node/-/node-12.12.42.tgz",
+ "integrity": "sha512-R/9QdYFLL9dE9l5cWWzWIZByVGFd7lk7JVOJ7KD+E1SJ4gni7XJRLz9QTjyYQiHIqEAgku9VgxdLjMlhhUaAFg==",
"dev": true
},
"@types/q": {
- "version": "1.5.2",
- "resolved": "https://nexus.loafle.net/repository/npm-all/@types/q/-/q-1.5.2.tgz",
- "integrity": "sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==",
+ "version": "1.5.4",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/@types/q/-/q-1.5.4.tgz",
+ "integrity": "sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==",
"dev": true
},
"@types/selenium-webdriver": {
@@ -2057,9 +2057,9 @@
"integrity": "sha512-/9w5uTgDkTV4nzMlyPzAyJCfR9E3VqnBQGYdVlyk8NFlVN3WOEzf8FdyRNfvUqyc4DAQFr3CDl06nL5pmua4hQ=="
},
"@ucap/core": {
- "version": "0.0.7",
- "resolved": "https://nexus.loafle.net/repository/npm-all/@ucap/core/-/core-0.0.7.tgz",
- "integrity": "sha512-pJkU0u21r7E7RCUTcoKWjbq0fg8FHM6/GuBSeAkC1Zwuc7AHu06njh2oc/7KE38kGQY0+Kjz6T8tJXfS/bLTGQ=="
+ "version": "0.0.8",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/@ucap/core/-/core-0.0.8.tgz",
+ "integrity": "sha512-e0BvZ4NHvqQhz0B4BKkUstFjvkvDkrKEcmAbSkDuzxS5KA1AfkXVYkn3eaIVQhYqWLXoyZwu3TSihS0scDitoA=="
},
"@ucap/logger": {
"version": "0.0.12",
@@ -2212,49 +2212,59 @@
"integrity": "sha512-0s5OdVsZ5Vi2X/aN6lOubPp4qS+b1GKWyNQ8gJlct9+tdzE5SZXfdQygBH1txImDnRC9jxZx5chiCwGqcbk6YA=="
},
"@ucap/ng-store-authentication": {
- "version": "0.0.10",
- "resolved": "https://nexus.loafle.net/repository/npm-all/@ucap/ng-store-authentication/-/ng-store-authentication-0.0.10.tgz",
- "integrity": "sha512-71Jzg9oYSQ89nShT3F64Mfy4edAbmc/vH/0pbPtT0S4zVH/ZMiC11xnocDQTzAKKtP1kXdNGJt/wv1ZbjMNUQQ=="
+ "version": "0.0.11",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/@ucap/ng-store-authentication/-/ng-store-authentication-0.0.11.tgz",
+ "integrity": "sha512-YwLMW+GIR3Rs7LaP+1xOH9KLI5jlpZx8oS7Zl32m6Wbym4ModIcGh21rRrqFhNXky4s9zl+ziaTRpFNCKJRgng=="
},
"@ucap/ng-store-chat": {
- "version": "0.0.5",
- "resolved": "https://nexus.loafle.net/repository/npm-all/@ucap/ng-store-chat/-/ng-store-chat-0.0.5.tgz",
- "integrity": "sha512-MuEZLs9jnL+eJ5CcHcvBFDuSyPhzWmy4xXBc7MpbCf+gaxZTzZv3NRFnHuB8ECCG/uIW2FE25VwvKCd/gJrY4Q=="
+ "version": "0.0.13",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/@ucap/ng-store-chat/-/ng-store-chat-0.0.13.tgz",
+ "integrity": "sha512-o+BCCSMxneUenRHEW47sSY22+Zt3lyr202Lg4bub9OVRbW5CVohHez8H+JwK+w+Lf8KbqG32V1ZjKLGclTpboA=="
},
"@ucap/ng-store-group": {
- "version": "0.0.6",
- "resolved": "https://nexus.loafle.net/repository/npm-all/@ucap/ng-store-group/-/ng-store-group-0.0.6.tgz",
- "integrity": "sha512-lqTUbHz6xi20yQ1nNeMJRY5/vSuWUbvXd7CawoR5z9pWtEOSUvFUh8bI7OK3X1XACP/V1U+2aBgX8MfR1qRVfg=="
+ "version": "0.0.14",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/@ucap/ng-store-group/-/ng-store-group-0.0.14.tgz",
+ "integrity": "sha512-sUmdHO7TD5B33DMAoEnelvqbLXTsWPnK2HC8XQ0FdlfGyUtf3kGpwS4BxduUi5wiZckR3hfuBdpCShIhf/qmeQ=="
},
"@ucap/ng-store-organization": {
- "version": "0.0.4",
- "resolved": "https://nexus.loafle.net/repository/npm-all/@ucap/ng-store-organization/-/ng-store-organization-0.0.4.tgz",
- "integrity": "sha512-m8WJ2aKZuTYz0JKr2sGAyrP83PK1EIa4vzn2nQCouIfA92kD3jYopy6x3jOgdDaK2vxYcaEJqPiKmTG3fhcLGA=="
+ "version": "0.0.8",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/@ucap/ng-store-organization/-/ng-store-organization-0.0.8.tgz",
+ "integrity": "sha512-vWWyPukVWeUPOxXkdWPN9s36V4EuMVuR8vPfOfKwnh09TxV8Efl19jIYYtNo92L1H/lF0OKfaz5eR9Aw76v+FA=="
},
"@ucap/ng-ui": {
- "version": "0.0.4",
- "resolved": "https://nexus.loafle.net/repository/npm-all/@ucap/ng-ui/-/ng-ui-0.0.4.tgz",
- "integrity": "sha512-LJjNjE426sbGOo8rc7KNMN+yI4yhqcuqgHwKPbjy7Of/y9VaFFbNZhIerlYA9U3lQf2X/qQmZ2Mwc5ct40iGVA=="
+ "version": "0.0.19",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/@ucap/ng-ui/-/ng-ui-0.0.19.tgz",
+ "integrity": "sha512-UuZSzWM4tBR+e5Z/1PFdNenHS00Pn4K7dTafIicG29YjHE5sTXXRqjDCVrKNxJQoMSEPWjUj7qnTgYwP2U83Vg=="
},
"@ucap/ng-ui-authentication": {
- "version": "0.0.16",
- "resolved": "https://nexus.loafle.net/repository/npm-all/@ucap/ng-ui-authentication/-/ng-ui-authentication-0.0.16.tgz",
- "integrity": "sha512-P0ZIx60oYuwRc4MlXaJwEpCtolrxbjDvCMrJF3zq6FQnKTxo5cRAoOPX8UGkVxkoDRFj94XIL7MZKPE/eMT0jQ=="
+ "version": "0.0.24",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/@ucap/ng-ui-authentication/-/ng-ui-authentication-0.0.24.tgz",
+ "integrity": "sha512-6QMJ8dieTnbPANsBzg2Ll3HH5q6Bzl2iSM19yHq8Ct7XOmElrYqrEZmxbDyYO+aCXIAwd2t7vu+rTsHfz3XOQg=="
+ },
+ "@ucap/ng-ui-chat": {
+ "version": "0.0.9",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/@ucap/ng-ui-chat/-/ng-ui-chat-0.0.9.tgz",
+ "integrity": "sha512-6qvzcTuylkxVjsqajsLW15laOyOskxVMy238/Ju1yYvwCRyHygwS1i67APoG5tv+SWu+l38f9uWIqzfy7WYHkQ=="
},
"@ucap/ng-ui-group": {
+ "version": "0.0.33",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/@ucap/ng-ui-group/-/ng-ui-group-0.0.33.tgz",
+ "integrity": "sha512-c//Jq00drbMGE3Cgwlh19ScXllGERX2eMVWkVjm311Y8HN9oBBT6Aq2uCM23/76P866oNrhecVDfDHgGYzPRjA=="
+ },
+ "@ucap/ng-ui-material": {
"version": "0.0.4",
- "resolved": "https://nexus.loafle.net/repository/npm-all/@ucap/ng-ui-group/-/ng-ui-group-0.0.4.tgz",
- "integrity": "sha512-uRQGD9wWmEexu8sxEWQyb0207vzVaqT48CPgX5xUsKKf2B1G/wIe1aXZS/JkSAAmPr8t/1rn0EapSpMetj28eA=="
+ "resolved": "https://nexus.loafle.net/repository/npm-all/@ucap/ng-ui-material/-/ng-ui-material-0.0.4.tgz",
+ "integrity": "sha512-ySPULAbP+nQ65hBG2VWZ2H5Hr7muuTGGNXs6A+S3lsxLaW452wM3GNyUBhvUopr8LaSsoOPpp4nK1JeC0fG6pA=="
},
"@ucap/ng-ui-organization": {
- "version": "0.0.2",
- "resolved": "https://nexus.loafle.net/repository/npm-all/@ucap/ng-ui-organization/-/ng-ui-organization-0.0.2.tgz",
- "integrity": "sha512-YiuoK/P/rVLJ++aTlUqioX+sQ0Q6xJR/W0P9wLx6vBaw7let+emzXH+yJWDcLwCZTeXBJrXJe54slW1bqMARSw=="
+ "version": "0.0.55",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/@ucap/ng-ui-organization/-/ng-ui-organization-0.0.55.tgz",
+ "integrity": "sha512-vfpKd3fbd+I0Od8aB2nIFfjuI7wj3Ziu/uiTEmZxKwZy7uZrNYm59BPbctKW3AQsQ4UtnLofhlBbAA7e9pT80Q=="
},
"@ucap/ng-ui-skin-default": {
"version": "0.0.1",
"resolved": "https://nexus.loafle.net/repository/npm-all/@ucap/ng-ui-skin-default/-/ng-ui-skin-default-0.0.1.tgz",
- "integrity": "sha512-lX5Idywy0jspDFDFHy9y0Wz048ReIM0/+6melCfDzMOeIb3UVf8U6Ata10ivZzkVHmISQOtukUG1P2IAb1NbYA=="
+ "integrity": "sha512-+lHYAzbnyyWh6hf+Ui7vP/ibyGJXuDUO++82jOiOsnPMCl17hkCCag8vQcB/aVgl0iHmebiPshnsL3CptJfeAg=="
},
"@ucap/ng-web-socket": {
"version": "0.0.2",
@@ -2292,9 +2302,9 @@
"integrity": "sha512-RR74BKQaMIVqPo6TUYxFTUrIVc2ZSrsZXtereqymk24UKfL3eJ/8Oa7Gi7CYqAUPUDV815g4iTilXWr+Ok201w=="
},
"@ucap/protocol-file": {
- "version": "0.0.4",
- "resolved": "https://nexus.loafle.net/repository/npm-all/@ucap/protocol-file/-/protocol-file-0.0.4.tgz",
- "integrity": "sha512-oyne/wiKsEsLzJY9VJUO85AwLtgfq0Dt7RyFLThoqquz6O7lCuWszdtvggAJTgn+XM+nF4hczw2Qm2jwjjCZgQ=="
+ "version": "0.0.5",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/@ucap/protocol-file/-/protocol-file-0.0.5.tgz",
+ "integrity": "sha512-3XRwtlpcrm2oZeckoOzzAUcqADPCGbgdEb4psfNnphTbGX9nYaBUTpWuLYwD3tVe7Wg4fytaHemcAu8yVZUANw=="
},
"@ucap/protocol-group": {
"version": "0.0.5",
@@ -2302,9 +2312,9 @@
"integrity": "sha512-3e35omfU61Q9dnbFPqbXIKkQ+2ph6CHD8oNFC0lH9a2rTS5G1EMeeFk/7f963+K3elWdj+OvWTdmbe2cd4PhRw=="
},
"@ucap/protocol-info": {
- "version": "0.0.5",
- "resolved": "https://nexus.loafle.net/repository/npm-all/@ucap/protocol-info/-/protocol-info-0.0.5.tgz",
- "integrity": "sha512-C2+cJrxdUNLVt3E1a54tCpTmEYE16sUGNvdJGUPywbvLAhjWf71ECjGr16bTMcPr/PxmzjZBu9M2Of39sqngOQ=="
+ "version": "0.0.6",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/@ucap/protocol-info/-/protocol-info-0.0.6.tgz",
+ "integrity": "sha512-qpt0jfmHDyaMGyADzaDMKbbkfD04yEC0u4KDyoMdjnTi0RXA6cilDRGr9TW/bezB9OxS40yNLK6REfh7aSmcUA=="
},
"@ucap/protocol-inner": {
"version": "0.0.4",
@@ -2351,15 +2361,20 @@
"resolved": "https://nexus.loafle.net/repository/npm-all/@ucap/protocol-umg/-/protocol-umg-0.0.5.tgz",
"integrity": "sha512-qxBog4wSit25HEq5a2pBtVE4NPQoqwmTeFuCqZBERc1x6Bw1zoA2aCVQ6yRFCNBguTyH0NQMSBFq6rnXwNrgBg=="
},
+ "@ucap/ui-scss": {
+ "version": "0.0.4",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/@ucap/ui-scss/-/ui-scss-0.0.4.tgz",
+ "integrity": "sha512-n7npexk0wkoLuPm2d4qaM8IgG3qskJVSLgWfr9UQ7sGjNOQp7UI8TP7NYmoPHObDB8i7Nzn2RP/CL0sawgvoLg=="
+ },
"@ucap/web-socket": {
"version": "0.0.10",
"resolved": "https://nexus.loafle.net/repository/npm-all/@ucap/web-socket/-/web-socket-0.0.10.tgz",
"integrity": "sha512-FQYi3D73nZBONb/gYBrpdkFfsofJ6BKqdtxMwPAv5Gh17OzDgIL0rG2GOX0NmjKf034cQqDmCo9Xo9iwk5tv9Q=="
},
"@ucap/web-storage": {
- "version": "0.0.7",
- "resolved": "https://nexus.loafle.net/repository/npm-all/@ucap/web-storage/-/web-storage-0.0.7.tgz",
- "integrity": "sha512-RHe3nyBOgF//46GwPQb96jXbeLlh5w+DJjM6hQDQIuwi+5vKtU6cEqYTExDscXNNiBBYFj2alYLbJBwoRNtjkA=="
+ "version": "0.0.9",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/@ucap/web-storage/-/web-storage-0.0.9.tgz",
+ "integrity": "sha512-pzGoIWhAMiu8QtHeyIbQgxUOIZBUeM8Of44PDMnwc5baHBXba4CpKnTcCAHKQIR8A0XzUBkN+yAnUKEXetel5w=="
},
"@webassemblyjs/ast": {
"version": "1.8.5",
@@ -2576,9 +2591,9 @@
}
},
"acorn": {
- "version": "7.1.1",
- "resolved": "https://nexus.loafle.net/repository/npm-all/acorn/-/acorn-7.1.1.tgz",
- "integrity": "sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==",
+ "version": "7.2.0",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/acorn/-/acorn-7.2.0.tgz",
+ "integrity": "sha512-apwXVmYVpQ34m/i71vrApRrRKCWQnZZF1+npOD0WV5xZFfwWOmKGQ2RWlfdy9vWITsenisM8M0Qeq8agcFHNiQ==",
"dev": true
},
"acorn-walk": {
@@ -2829,6 +2844,14 @@
"bn.js": "^4.0.0",
"inherits": "^2.0.1",
"minimalistic-assert": "^1.0.0"
+ },
+ "dependencies": {
+ "bn.js": {
+ "version": "4.11.9",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/bn.js/-/bn.js-4.11.9.tgz",
+ "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==",
+ "dev": true
+ }
}
},
"assert": {
@@ -2950,9 +2973,9 @@
"dev": true
},
"aws4": {
- "version": "1.9.1",
- "resolved": "https://nexus.loafle.net/repository/npm-all/aws4/-/aws4-1.9.1.tgz",
- "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==",
+ "version": "1.10.0",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/aws4/-/aws4-1.10.0.tgz",
+ "integrity": "sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA==",
"dev": true
},
"axios": {
@@ -2998,9 +3021,9 @@
}
},
"babel-plugin-dynamic-import-node": {
- "version": "2.3.2",
- "resolved": "https://nexus.loafle.net/repository/npm-all/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.2.tgz",
- "integrity": "sha512-yvczAMjbc73xira9yTyF1XnEmkX8QwlUhmxuhimeMUeAaA6s7busTPRVDzhVG7eeBdNcRiZ/mAwFrJ9It4vQcg==",
+ "version": "2.3.3",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz",
+ "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==",
"dev": true,
"requires": {
"object.assign": "^4.1.0"
@@ -3161,9 +3184,9 @@
"dev": true
},
"bn.js": {
- "version": "4.11.8",
- "resolved": "https://nexus.loafle.net/repository/npm-all/bn.js/-/bn.js-4.11.8.tgz",
- "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==",
+ "version": "5.1.2",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/bn.js/-/bn.js-5.1.2.tgz",
+ "integrity": "sha512-40rZaf3bUNKTVYu9sIeeEGOg7g14Yvnj9kH7b50EiwX0Q7A6umbvfI5tvHaOERH0XigqKkfLkFQxzb4e6CIXnA==",
"dev": true
},
"body-parser": {
@@ -3297,21 +3320,50 @@
"requires": {
"bn.js": "^4.1.0",
"randombytes": "^2.0.1"
+ },
+ "dependencies": {
+ "bn.js": {
+ "version": "4.11.9",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/bn.js/-/bn.js-4.11.9.tgz",
+ "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==",
+ "dev": true
+ }
}
},
"browserify-sign": {
- "version": "4.0.4",
- "resolved": "https://nexus.loafle.net/repository/npm-all/browserify-sign/-/browserify-sign-4.0.4.tgz",
- "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=",
+ "version": "4.2.0",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/browserify-sign/-/browserify-sign-4.2.0.tgz",
+ "integrity": "sha512-hEZC1KEeYuoHRqhGhTy6gWrpJA3ZDjFWv0DE61643ZnOXAKJb3u7yWcrU0mMc9SwAqK1n7myPGndkp0dFG7NFA==",
"dev": true,
"requires": {
- "bn.js": "^4.1.1",
- "browserify-rsa": "^4.0.0",
- "create-hash": "^1.1.0",
- "create-hmac": "^1.1.2",
- "elliptic": "^6.0.0",
- "inherits": "^2.0.1",
- "parse-asn1": "^5.0.0"
+ "bn.js": "^5.1.1",
+ "browserify-rsa": "^4.0.1",
+ "create-hash": "^1.2.0",
+ "create-hmac": "^1.1.7",
+ "elliptic": "^6.5.2",
+ "inherits": "^2.0.4",
+ "parse-asn1": "^5.1.5",
+ "readable-stream": "^3.6.0",
+ "safe-buffer": "^5.2.0"
+ },
+ "dependencies": {
+ "readable-stream": {
+ "version": "3.6.0",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/readable-stream/-/readable-stream-3.6.0.tgz",
+ "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ }
+ },
+ "safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "dev": true
+ }
}
},
"browserify-zlib": {
@@ -3522,9 +3574,9 @@
}
},
"caniuse-lite": {
- "version": "1.0.30001045",
- "resolved": "https://nexus.loafle.net/repository/npm-all/caniuse-lite/-/caniuse-lite-1.0.30001045.tgz",
- "integrity": "sha512-Y8o2Iz1KPcD6FjySbk1sPpvJqchgxk/iow0DABpGyzA1UeQAuxh63Xh0Enj5/BrsYbXtCN32JmR4ZxQTCQ6E6A==",
+ "version": "1.0.30001064",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/caniuse-lite/-/caniuse-lite-1.0.30001064.tgz",
+ "integrity": "sha512-hdBcQMFvJIrOhkpAZiRXz04Cmetwc9NekeuNl0qZfHOugxOhJKxsjF1RmISMPFjIF4PPx1reliIzbfN42EiQ5A==",
"dev": true
},
"canonical-path": {
@@ -3563,9 +3615,9 @@
"dev": true
},
"chokidar": {
- "version": "3.3.1",
- "resolved": "https://nexus.loafle.net/repository/npm-all/chokidar/-/chokidar-3.3.1.tgz",
- "integrity": "sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg==",
+ "version": "3.4.0",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/chokidar/-/chokidar-3.4.0.tgz",
+ "integrity": "sha512-aXAaho2VJtisB/1fg1+3nlLJqGOuewTzQpd/Tz0yTg2R0e4IGtshYvtjowyEumcBv2z+y4+kc75Mz7j5xJskcQ==",
"dev": true,
"requires": {
"anymatch": "~3.1.1",
@@ -3575,7 +3627,7 @@
"is-binary-path": "~2.1.0",
"is-glob": "~4.0.1",
"normalize-path": "~3.0.0",
- "readdirp": "~3.3.0"
+ "readdirp": "~3.4.0"
},
"dependencies": {
"glob-parent": {
@@ -4189,6 +4241,14 @@
"requires": {
"bn.js": "^4.1.0",
"elliptic": "^6.0.0"
+ },
+ "dependencies": {
+ "bn.js": {
+ "version": "4.11.9",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/bn.js/-/bn.js-4.11.9.tgz",
+ "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==",
+ "dev": true
+ }
}
},
"create-hash": {
@@ -4733,9 +4793,9 @@
"dev": true
},
"detect-browser": {
- "version": "5.0.0",
- "resolved": "https://nexus.loafle.net/repository/npm-all/detect-browser/-/detect-browser-5.0.0.tgz",
- "integrity": "sha512-jUaq/SAT9YMlmhq/8w7gnqqr8AMcc7iYG1eAp7vP/7xn2eLtlcnEmxOkh2PmTg2Q+jVSUO3XD4sZ/IldbGg3dA=="
+ "version": "5.1.0",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/detect-browser/-/detect-browser-5.1.0.tgz",
+ "integrity": "sha512-WKa9p+/MNwmTiS+V2AS6eGxic+807qvnV3hC+4z2GTY+F42h1n8AynVTMMc4EJBC32qMs6yjOTpeDEQQt/AVqQ=="
},
"detect-node": {
"version": "2.0.4",
@@ -4774,6 +4834,14 @@
"bn.js": "^4.1.0",
"miller-rabin": "^4.0.0",
"randombytes": "^2.0.0"
+ },
+ "dependencies": {
+ "bn.js": {
+ "version": "4.11.9",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/bn.js/-/bn.js-4.11.9.tgz",
+ "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==",
+ "dev": true
+ }
}
},
"dir-glob": {
@@ -4912,9 +4980,9 @@
"dev": true
},
"electron-to-chromium": {
- "version": "1.3.414",
- "resolved": "https://nexus.loafle.net/repository/npm-all/electron-to-chromium/-/electron-to-chromium-1.3.414.tgz",
- "integrity": "sha512-UfxhIvED++qLwWrAq9uYVcqF8FdeV9sU2S7qhiHYFODxzXRrd1GZRl/PjITHsTEejgibcWDraD8TQqoHb1aCBQ==",
+ "version": "1.3.451",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/electron-to-chromium/-/electron-to-chromium-1.3.451.tgz",
+ "integrity": "sha512-2fvco0F2bBIgqzO8GRP0Jt/91pdrf9KfZ5FsmkYkjERmIJG585cFeFZV4+CO6oTmU3HmCTgfcZuEa7kW8VUh3A==",
"dev": true
},
"elliptic": {
@@ -4930,6 +4998,14 @@
"inherits": "^2.0.1",
"minimalistic-assert": "^1.0.0",
"minimalistic-crypto-utils": "^1.0.0"
+ },
+ "dependencies": {
+ "bn.js": {
+ "version": "4.11.9",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/bn.js/-/bn.js-4.11.9.tgz",
+ "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==",
+ "dev": true
+ }
}
},
"emoji-regex": {
@@ -5070,9 +5146,9 @@
"dev": true
},
"entities": {
- "version": "2.0.0",
- "resolved": "https://nexus.loafle.net/repository/npm-all/entities/-/entities-2.0.0.tgz",
- "integrity": "sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==",
+ "version": "2.0.2",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/entities/-/entities-2.0.2.tgz",
+ "integrity": "sha512-dmD3AvJQBUjKpcNkoqr+x+IF0SdRtPz9Vk0uTy4yWqga9ibB6s4v++QFWNohjiUGoMlF552ZvNyXDxz5iW0qmw==",
"dev": true
},
"err-code": {
@@ -5193,9 +5269,9 @@
"dev": true
},
"eventemitter3": {
- "version": "4.0.0",
- "resolved": "https://nexus.loafle.net/repository/npm-all/eventemitter3/-/eventemitter3-4.0.0.tgz",
- "integrity": "sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg==",
+ "version": "4.0.4",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/eventemitter3/-/eventemitter3-4.0.4.tgz",
+ "integrity": "sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ==",
"dev": true
},
"events": {
@@ -5533,9 +5609,9 @@
}
},
"file-type": {
- "version": "14.1.4",
- "resolved": "https://nexus.loafle.net/repository/npm-all/file-type/-/file-type-14.1.4.tgz",
- "integrity": "sha512-1n6FczX8n73Y/cLjTiMboeTGHfm/i2AWk2oQE7m9a/G5YTCZHCEHEGr32thhLm3iQNUYzTKVQUcYcNHtOLwqgQ==",
+ "version": "14.5.0",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/file-type/-/file-type-14.5.0.tgz",
+ "integrity": "sha512-hIxIT/8DPClkKbC+IEoZvcQ5aBhsivh4aWzLMvmkp9Uabzey7gFNNPmTOwp8O/b2DkJ8a4FkFMkyFzkyRVsJXg==",
"requires": {
"readable-web-to-node-stream": "^2.0.0",
"strtok3": "^6.0.0",
@@ -5625,9 +5701,9 @@
}
},
"make-dir": {
- "version": "3.0.2",
- "resolved": "https://nexus.loafle.net/repository/npm-all/make-dir/-/make-dir-3.0.2.tgz",
- "integrity": "sha512-rYKABKutXa6vXTXhoV18cBE7PaewPXHe/Bdq4v+ZLMhxbWApkFFplT0LcbMW+6BbjnQXzZ/sAvSE/JdguApG5w==",
+ "version": "3.1.0",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/make-dir/-/make-dir-3.1.0.tgz",
+ "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
"dev": true,
"requires": {
"semver": "^6.0.0"
@@ -5936,9 +6012,9 @@
}
},
"graceful-fs": {
- "version": "4.2.3",
- "resolved": "https://nexus.loafle.net/repository/npm-all/graceful-fs/-/graceful-fs-4.2.3.tgz",
- "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==",
+ "version": "4.2.4",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/graceful-fs/-/graceful-fs-4.2.4.tgz",
+ "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
"dev": true
},
"gzip-size": {
@@ -6079,13 +6155,33 @@
}
},
"hash-base": {
- "version": "3.0.4",
- "resolved": "https://nexus.loafle.net/repository/npm-all/hash-base/-/hash-base-3.0.4.tgz",
- "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=",
+ "version": "3.1.0",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/hash-base/-/hash-base-3.1.0.tgz",
+ "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==",
"dev": true,
"requires": {
- "inherits": "^2.0.1",
- "safe-buffer": "^5.0.1"
+ "inherits": "^2.0.4",
+ "readable-stream": "^3.6.0",
+ "safe-buffer": "^5.2.0"
+ },
+ "dependencies": {
+ "readable-stream": {
+ "version": "3.6.0",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/readable-stream/-/readable-stream-3.6.0.tgz",
+ "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ }
+ },
+ "safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "dev": true
+ }
}
},
"hash.js": {
@@ -6206,15 +6302,15 @@
}
},
"http-parser-js": {
- "version": "0.4.10",
- "resolved": "https://nexus.loafle.net/repository/npm-all/http-parser-js/-/http-parser-js-0.4.10.tgz",
- "integrity": "sha1-ksnBN0w1CF912zWexWzCV8u5P6Q=",
+ "version": "0.5.2",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/http-parser-js/-/http-parser-js-0.5.2.tgz",
+ "integrity": "sha512-opCO9ASqg5Wy2FNo7A0sxy71yGbbkJJXLdgMK04Tcypw9jr2MgWbyubb0+WdmDmGnFflO7fRbqbaihh/ENDlRQ==",
"dev": true
},
"http-proxy": {
- "version": "1.18.0",
- "resolved": "https://nexus.loafle.net/repository/npm-all/http-proxy/-/http-proxy-1.18.0.tgz",
- "integrity": "sha512-84I2iJM/n1d4Hdgc6y2+qY5mDaz2PUVjlg9znE9byl+q0uC3DeByqBGReQu5tpLK0TAqTIXScRUV+dg7+bUPpQ==",
+ "version": "1.18.1",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/http-proxy/-/http-proxy-1.18.1.tgz",
+ "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==",
"dev": true,
"requires": {
"eventemitter3": "^4.0.0",
@@ -6281,17 +6377,17 @@
}
},
"i18next": {
- "version": "19.4.3",
- "resolved": "https://nexus.loafle.net/repository/npm-all/i18next/-/i18next-19.4.3.tgz",
- "integrity": "sha512-mqjfrYnvoqh9s2yVnzUjMfELCOoRuiKbRYzICZozaHYZMpUx17MpWDCW6zC+E42CaveIet/Zh07j22GAo4CFqQ==",
+ "version": "19.4.4",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/i18next/-/i18next-19.4.4.tgz",
+ "integrity": "sha512-ofaHtdsDdX3A5nYur1HWblB7J4hIcjr2ACdnwTAJgc8hTfPbyzZfGX0hVkKpI3vzDIgO6Uzc4v1ffW2W6gG6zw==",
"requires": {
"@babel/runtime": "^7.3.1"
}
},
"i18next-browser-languagedetector": {
- "version": "4.1.1",
- "resolved": "https://nexus.loafle.net/repository/npm-all/i18next-browser-languagedetector/-/i18next-browser-languagedetector-4.1.1.tgz",
- "integrity": "sha512-akv0zurR/2KU7s1qaWkirY9FEEOT1TNsQaezEg8+1BLLQre7vylqb7tYoUgYqP/0/BEzXJgnoQnj+sh5xYFMhg==",
+ "version": "4.2.0",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/i18next-browser-languagedetector/-/i18next-browser-languagedetector-4.2.0.tgz",
+ "integrity": "sha512-qRSCBWgDUSqVQb3sTxkDC+ImYLhF+wB387Y1RpOcJvyex+V3abi+W83n4Awy+dx719AOBbKTy97FjrUGrAhbyw==",
"requires": {
"@babel/runtime": "^7.5.5"
}
@@ -6692,6 +6788,12 @@
"integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=",
"dev": true
},
+ "is-docker": {
+ "version": "2.0.0",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/is-docker/-/is-docker-2.0.0.tgz",
+ "integrity": "sha512-pJEdRugimx4fBMra5z2/5iRdZ63OhYV0vr0Dwm5+xtW4D1FvRkB8hamMIhnWfyJeDdyr/aa7BDyNbtG38VxgoQ==",
+ "dev": true
+ },
"is-extendable": {
"version": "0.1.1",
"resolved": "https://nexus.loafle.net/repository/npm-all/is-extendable/-/is-extendable-0.1.1.tgz",
@@ -6776,12 +6878,6 @@
"isobject": "^3.0.1"
}
},
- "is-promise": {
- "version": "2.1.0",
- "resolved": "https://nexus.loafle.net/repository/npm-all/is-promise/-/is-promise-2.1.0.tgz",
- "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=",
- "dev": true
- },
"is-regex": {
"version": "1.0.5",
"resolved": "https://nexus.loafle.net/repository/npm-all/is-regex/-/is-regex-1.0.5.tgz",
@@ -6833,10 +6929,13 @@
"dev": true
},
"is-wsl": {
- "version": "2.1.1",
- "resolved": "https://nexus.loafle.net/repository/npm-all/is-wsl/-/is-wsl-2.1.1.tgz",
- "integrity": "sha512-umZHcSrwlDHo2TGMXv0DZ8dIUGunZ2Iv68YZnrmCiBPkZ4aaOhtv7pXJKeki9k3qJ3RJr0cDyitcl5wEH3AYog==",
- "dev": true
+ "version": "2.2.0",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/is-wsl/-/is-wsl-2.2.0.tgz",
+ "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
+ "dev": true,
+ "requires": {
+ "is-docker": "^2.0.0"
+ }
},
"isarray": {
"version": "1.0.0",
@@ -6872,9 +6971,9 @@
"dev": true
},
"istanbul-api": {
- "version": "2.1.6",
- "resolved": "https://nexus.loafle.net/repository/npm-all/istanbul-api/-/istanbul-api-2.1.6.tgz",
- "integrity": "sha512-x0Eicp6KsShG1k1rMgBAi/1GgY7kFGEBwQpw3PXGEmu+rBcBNhqU8g2DgY9mlepAsLPzrzrbqSgCGANnki4POA==",
+ "version": "2.1.7",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/istanbul-api/-/istanbul-api-2.1.7.tgz",
+ "integrity": "sha512-LYTOa2UrYFyJ/aSczZi/6lBykVMjCCvUmT64gOe+jPZFy4w6FYfPGqFT2IiQ2BxVHHDOvCD7qrIXb0EOh4uGWw==",
"dev": true,
"requires": {
"async": "^2.6.2",
@@ -6885,7 +6984,7 @@
"istanbul-lib-instrument": "^3.3.0",
"istanbul-lib-report": "^2.0.8",
"istanbul-lib-source-maps": "^3.0.6",
- "istanbul-reports": "^2.2.4",
+ "istanbul-reports": "^2.2.5",
"js-yaml": "^3.13.1",
"make-dir": "^2.1.0",
"minimatch": "^3.0.4",
@@ -6931,15 +7030,12 @@
}
},
"istanbul-lib-instrument": {
- "version": "4.0.1",
- "resolved": "https://nexus.loafle.net/repository/npm-all/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.1.tgz",
- "integrity": "sha512-imIchxnodll7pvQBYOqUu88EufLCU56LMeFPZZM/fJZ1irYcYdqroaV+ACK1Ila8ls09iEYArp+nqyC6lW1Vfg==",
+ "version": "4.0.3",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz",
+ "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==",
"dev": true,
"requires": {
"@babel/core": "^7.7.5",
- "@babel/parser": "^7.7.5",
- "@babel/template": "^7.7.4",
- "@babel/traverse": "^7.7.4",
"@istanbuljs/schema": "^0.1.2",
"istanbul-lib-coverage": "^3.0.0",
"semver": "^6.3.0"
@@ -7240,9 +7336,9 @@
},
"dependencies": {
"mime": {
- "version": "2.4.4",
- "resolved": "https://nexus.loafle.net/repository/npm-all/mime/-/mime-2.4.4.tgz",
- "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==",
+ "version": "2.4.5",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/mime/-/mime-2.4.5.tgz",
+ "integrity": "sha512-3hQhEUF027BuxZjQA3s7rIv/7VCQPa27hN9u9g87sEkWaKwQPuXOkVKtOeiyUrnWqTDiOs8Ed2rwg733mB0R5w==",
"dev": true
},
"rimraf": {
@@ -7291,9 +7387,9 @@
}
},
"karma-jasmine-html-reporter": {
- "version": "1.5.3",
- "resolved": "https://nexus.loafle.net/repository/npm-all/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-1.5.3.tgz",
- "integrity": "sha512-ci0VrjuCaFj+9d1tYlTE3KIPUCp0rz874zWWU3JgCMqGIyw5ke+BXWFPOAGAqUdCJcrMwneyvp1zFXA74MiPUA==",
+ "version": "1.5.4",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-1.5.4.tgz",
+ "integrity": "sha512-PtilRLno5O6wH3lDihRnz0Ba8oSn0YUJqKjjux1peoYGwo0AQqrWRbdWk/RLzcGlb+onTyXAnHl6M+Hu3UxG/Q==",
"dev": true
},
"karma-source-map-support": {
@@ -7364,9 +7460,9 @@
}
},
"libphonenumber-js": {
- "version": "1.7.50",
- "resolved": "https://nexus.loafle.net/repository/npm-all/libphonenumber-js/-/libphonenumber-js-1.7.50.tgz",
- "integrity": "sha512-FmdA2WvwdTgu1X05zBnAE+3UAA09o3hFxEaqR0J+x7tGPAt1AD7Dj54L58PTJodrFBve/AIThFtC/UGqfSLbBw==",
+ "version": "1.7.51",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/libphonenumber-js/-/libphonenumber-js-1.7.51.tgz",
+ "integrity": "sha512-YrmFi5rLnSMKp2tM0FkjIHGAlHyUo8pRP8u4DeMseX+gXDY96q7sgfy/Ty54nv0BqObYKzPJBUq4LMMNESjfBA==",
"requires": {
"minimist": "^1.2.5",
"xml2js": "^0.4.17"
@@ -7841,6 +7937,14 @@
"requires": {
"bn.js": "^4.0.0",
"brorand": "^1.0.1"
+ },
+ "dependencies": {
+ "bn.js": {
+ "version": "4.11.9",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/bn.js/-/bn.js-4.11.9.tgz",
+ "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==",
+ "dev": true
+ }
}
},
"mime": {
@@ -7850,18 +7954,18 @@
"dev": true
},
"mime-db": {
- "version": "1.43.0",
- "resolved": "https://nexus.loafle.net/repository/npm-all/mime-db/-/mime-db-1.43.0.tgz",
- "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==",
+ "version": "1.44.0",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/mime-db/-/mime-db-1.44.0.tgz",
+ "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==",
"dev": true
},
"mime-types": {
- "version": "2.1.26",
- "resolved": "https://nexus.loafle.net/repository/npm-all/mime-types/-/mime-types-2.1.26.tgz",
- "integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==",
+ "version": "2.1.27",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/mime-types/-/mime-types-2.1.27.tgz",
+ "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==",
"dev": true,
"requires": {
- "mime-db": "1.43.0"
+ "mime-db": "1.44.0"
}
},
"mimic-fn": {
@@ -7923,9 +8027,9 @@
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
},
"minipass": {
- "version": "3.1.1",
- "resolved": "https://nexus.loafle.net/repository/npm-all/minipass/-/minipass-3.1.1.tgz",
- "integrity": "sha512-UFqVihv6PQgwj8/yTGvl9kPz7xIAY+R5z6XYjRInD3Gk3qx6QGSD6zEcpeG4Dy/lQnv1J6zv8ejV90hyYIKf3w==",
+ "version": "3.1.3",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/minipass/-/minipass-3.1.3.tgz",
+ "integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==",
"dev": true,
"requires": {
"yallist": "^4.0.0"
@@ -7950,9 +8054,9 @@
}
},
"minipass-pipeline": {
- "version": "1.2.2",
- "resolved": "https://nexus.loafle.net/repository/npm-all/minipass-pipeline/-/minipass-pipeline-1.2.2.tgz",
- "integrity": "sha512-3JS5A2DKhD2g0Gg8x3yamO0pj7YeKGwVlDS90pF++kxptwx/F+B//roxf9SqYil5tQo65bijy+dAuAFZmYOouA==",
+ "version": "1.2.3",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/minipass-pipeline/-/minipass-pipeline-1.2.3.tgz",
+ "integrity": "sha512-cFOknTvng5vqnwOpDsZTWhNll6Jf8o2x+/diplafmxpuIymAjzoOolZG0VvQf3V2HgqzJNhnuKHYp2BqDgz8IQ==",
"dev": true,
"requires": {
"minipass": "^3.0.0"
@@ -8034,14 +8138,14 @@
}
},
"moment": {
- "version": "2.24.0",
- "resolved": "https://nexus.loafle.net/repository/npm-all/moment/-/moment-2.24.0.tgz",
- "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg=="
+ "version": "2.26.0",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/moment/-/moment-2.26.0.tgz",
+ "integrity": "sha512-oIixUO+OamkUkwjhAVE18rAMfRJNsNe/Stid/gwHSOfHrOtw9EhAY2AHvdKZ/k/MggcYELFCJz/Sn2pL8b8JMw=="
},
"moment-timezone": {
- "version": "0.5.28",
- "resolved": "https://nexus.loafle.net/repository/npm-all/moment-timezone/-/moment-timezone-0.5.28.tgz",
- "integrity": "sha512-TDJkZvAyKIVWg5EtVqRzU97w0Rb0YVbfpqyjgu6GwXCAohVRqwZjf4fOzDE6p1Ch98Sro/8hQQi65WDXW5STPw==",
+ "version": "0.5.31",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/moment-timezone/-/moment-timezone-0.5.31.tgz",
+ "integrity": "sha512-+GgHNg8xRhMXfEbv81iDtrVeTcWt0kWmTEY1XQK14dICTXnWJnT0dxdlPspwqF3keKMVPXwayEsk1DI0AA/jdA==",
"requires": {
"moment": ">= 2.9.0"
}
@@ -8210,9 +8314,9 @@
}
},
"node-releases": {
- "version": "1.1.53",
- "resolved": "https://nexus.loafle.net/repository/npm-all/node-releases/-/node-releases-1.1.53.tgz",
- "integrity": "sha512-wp8zyQVwef2hpZ/dJH7SfSrIPD6YoJz6BDQDpGEkcA0s3LpAQoxBIYmfIq6QAhC1DhwsyCgTaTTcONwX8qzCuQ==",
+ "version": "1.1.56",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/node-releases/-/node-releases-1.1.56.tgz",
+ "integrity": "sha512-EVo605FhWLygH8a64TjgpjyHYOihkxECwX1bHHr8tETJKWEiWS2YJjPbvsX2jFjnjTNEgBCmk9mLjKG1Mf11cw==",
"dev": true
},
"normalize-package-data": {
@@ -8331,9 +8435,9 @@
}
},
"npm-registry-fetch": {
- "version": "4.0.3",
- "resolved": "https://nexus.loafle.net/repository/npm-all/npm-registry-fetch/-/npm-registry-fetch-4.0.3.tgz",
- "integrity": "sha512-WGvUx0lkKFhu9MbiGFuT9nG2NpfQ+4dCJwRwwtK2HK5izJEvwDxMeUyqbuMS7N/OkpVCqDorV6rO5E4V9F8lJw==",
+ "version": "4.0.4",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/npm-registry-fetch/-/npm-registry-fetch-4.0.4.tgz",
+ "integrity": "sha512-6jb34hX/iYNQebqWUHtU8YF6Cjb1H6ouTFPClYsyiW6lpFkljTpdeftm53rRojtja1rKAvKNIIiTS5Sjpw4wsA==",
"dev": true,
"requires": {
"JSONStream": "^1.3.4",
@@ -8346,9 +8450,9 @@
},
"dependencies": {
"safe-buffer": {
- "version": "5.2.0",
- "resolved": "https://nexus.loafle.net/repository/npm-all/safe-buffer/-/safe-buffer-5.2.0.tgz",
- "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==",
+ "version": "5.2.1",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"dev": true
}
}
@@ -9036,9 +9140,9 @@
}
},
"pino": {
- "version": "6.2.0",
- "resolved": "https://nexus.loafle.net/repository/npm-all/pino/-/pino-6.2.0.tgz",
- "integrity": "sha512-UzrsiT5Wyscw7dxHa8Ec8G2kY45mwFk7rrZhMkCMg8s9F8VWDVj+WFcaSIKproTDyxlqerMaHw+11jlNXgeiCg==",
+ "version": "6.2.1",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/pino/-/pino-6.2.1.tgz",
+ "integrity": "sha512-5F5A+G25Ex2rMOBEe3XYGyLSF4dikQZsFvPojwsqnDBX+rfg7+kw9s5i7pHuVAJImekjwb+MR9jQyHWPLENlvQ==",
"requires": {
"fast-redact": "^2.0.0",
"fast-safe-stringify": "^2.0.7",
@@ -9117,9 +9221,9 @@
}
},
"portfinder": {
- "version": "1.0.25",
- "resolved": "https://nexus.loafle.net/repository/npm-all/portfinder/-/portfinder-1.0.25.tgz",
- "integrity": "sha512-6ElJnHBbxVA1XSLgBp7G1FiCkQdlqGzuF7DswL5tcea+E8UpuvPU7beVAjjRwCioTS9ZluNbu+ZyRvgTsmqEBg==",
+ "version": "1.0.26",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/portfinder/-/portfinder-1.0.26.tgz",
+ "integrity": "sha512-Xi7mKxJHHMI3rIUrnm/jjUgwhbYMkp/XKEcZX3aG4BrumLpq3nmoQMX+ClYnDZnZ/New7IatC1no5RX0zo1vXQ==",
"dev": true,
"requires": {
"async": "^2.6.2",
@@ -9190,9 +9294,9 @@
},
"dependencies": {
"postcss": {
- "version": "7.0.27",
- "resolved": "https://nexus.loafle.net/repository/npm-all/postcss/-/postcss-7.0.27.tgz",
- "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==",
+ "version": "7.0.30",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/postcss/-/postcss-7.0.30.tgz",
+ "integrity": "sha512-nu/0m+NtIzoubO+xdAlwZl/u5S5vi/y6BCsoL8D+8IxsD3XvBS8X4YEADNIVXKVuQvduiucnRv+vPIqj56EGMQ==",
"dev": true,
"requires": {
"chalk": "^2.4.2",
@@ -9723,9 +9827,9 @@
}
},
"postcss-value-parser": {
- "version": "4.0.3",
- "resolved": "https://nexus.loafle.net/repository/npm-all/postcss-value-parser/-/postcss-value-parser-4.0.3.tgz",
- "integrity": "sha512-N7h4pG+Nnu5BEIzyeaaIYWs0LI5XC40OrRh5L60z0QjFsqGWcHcbkBvpe1WYpcIS9yQ8sOi/vIPt1ejQCrMVrg==",
+ "version": "4.1.0",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz",
+ "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==",
"dev": true
},
"prepend-http": {
@@ -10005,6 +10109,14 @@
"parse-asn1": "^5.0.0",
"randombytes": "^2.0.1",
"safe-buffer": "^5.1.2"
+ },
+ "dependencies": {
+ "bn.js": {
+ "version": "4.11.9",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/bn.js/-/bn.js-4.11.9.tgz",
+ "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==",
+ "dev": true
+ }
}
},
"pump": {
@@ -10243,12 +10355,12 @@
}
},
"readdirp": {
- "version": "3.3.0",
- "resolved": "https://nexus.loafle.net/repository/npm-all/readdirp/-/readdirp-3.3.0.tgz",
- "integrity": "sha512-zz0pAkSPOXXm1viEwygWIPSPkcBYjW1xU5j/JBh5t9bGCJwa6f9+BJa6VaB2g+b55yVrmXzqkyLf4xaWYM0IkQ==",
+ "version": "3.4.0",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/readdirp/-/readdirp-3.4.0.tgz",
+ "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==",
"dev": true,
"requires": {
- "picomatch": "^2.0.7"
+ "picomatch": "^2.2.1"
}
},
"reflect-metadata": {
@@ -10414,9 +10526,9 @@
"integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg=="
},
"resolve": {
- "version": "1.16.1",
- "resolved": "https://nexus.loafle.net/repository/npm-all/resolve/-/resolve-1.16.1.tgz",
- "integrity": "sha512-rmAglCSqWWMrrBv/XM6sW0NuRFiKViw/W4d9EbC4pt+49H8JwHy+mcGmALTEg504AUDcLTvb1T2q3E9AnmY+ig==",
+ "version": "1.17.0",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/resolve/-/resolve-1.17.0.tgz",
+ "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==",
"dev": true,
"requires": {
"path-parse": "^1.0.6"
@@ -10514,13 +10626,10 @@
}
},
"run-async": {
- "version": "2.4.0",
- "resolved": "https://nexus.loafle.net/repository/npm-all/run-async/-/run-async-2.4.0.tgz",
- "integrity": "sha512-xJTbh/d7Lm7SBhc1tNvTpeCHaEzoyxPrqNlvSdMfBTYwaY++UJFyXUOxAtsRUXjlqOfj8luNaR9vjCh4KeV+pg==",
- "dev": true,
- "requires": {
- "is-promise": "^2.1.0"
- }
+ "version": "2.4.1",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/run-async/-/run-async-2.4.1.tgz",
+ "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==",
+ "dev": true
},
"run-queue": {
"version": "1.0.3",
@@ -11298,9 +11407,9 @@
"dev": true
},
"spdx-correct": {
- "version": "3.1.0",
- "resolved": "https://nexus.loafle.net/repository/npm-all/spdx-correct/-/spdx-correct-3.1.0.tgz",
- "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==",
+ "version": "3.1.1",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/spdx-correct/-/spdx-correct-3.1.1.tgz",
+ "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==",
"dev": true,
"requires": {
"spdx-expression-parse": "^3.0.0",
@@ -11314,9 +11423,9 @@
"dev": true
},
"spdx-expression-parse": {
- "version": "3.0.0",
- "resolved": "https://nexus.loafle.net/repository/npm-all/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz",
- "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==",
+ "version": "3.0.1",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
+ "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
"dev": true,
"requires": {
"spdx-exceptions": "^2.1.0",
@@ -11916,9 +12025,9 @@
"dev": true
},
"jest-worker": {
- "version": "25.4.0",
- "resolved": "https://nexus.loafle.net/repository/npm-all/jest-worker/-/jest-worker-25.4.0.tgz",
- "integrity": "sha512-ghAs/1FtfYpMmYQ0AHqxV62XPvKdUDIBBApMZfly+E9JEmYh2K45G0R5dWxx986RN12pRCxsViwQVtGl+N4whw==",
+ "version": "25.5.0",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/jest-worker/-/jest-worker-25.5.0.tgz",
+ "integrity": "sha512-/dsSmUkIy5EBGfv/IjjqmFxrNAUpBERfGs1oHROyD7yxjG/w+t0GOJDX8O1k32ySmd7+a5IhnJU2qQFcJ4n1vw==",
"dev": true,
"requires": {
"merge-stream": "^2.0.0",
@@ -11935,9 +12044,9 @@
}
},
"make-dir": {
- "version": "3.0.2",
- "resolved": "https://nexus.loafle.net/repository/npm-all/make-dir/-/make-dir-3.0.2.tgz",
- "integrity": "sha512-rYKABKutXa6vXTXhoV18cBE7PaewPXHe/Bdq4v+ZLMhxbWApkFFplT0LcbMW+6BbjnQXzZ/sAvSE/JdguApG5w==",
+ "version": "3.1.0",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/make-dir/-/make-dir-3.1.0.tgz",
+ "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
"dev": true,
"requires": {
"semver": "^6.0.0"
@@ -12165,9 +12274,9 @@
}
},
"tslib": {
- "version": "1.11.1",
- "resolved": "https://nexus.loafle.net/repository/npm-all/tslib/-/tslib-1.11.1.tgz",
- "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA=="
+ "version": "1.13.0",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/tslib/-/tslib-1.13.0.tgz",
+ "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q=="
},
"tslint": {
"version": "5.18.0",
@@ -12609,14 +12718,25 @@
"dev": true
},
"watchpack": {
- "version": "1.6.1",
- "resolved": "https://nexus.loafle.net/repository/npm-all/watchpack/-/watchpack-1.6.1.tgz",
- "integrity": "sha512-+IF9hfUFOrYOOaKyfaI7h7dquUIOgyEMoQMLA7OP5FxegKA2+XdXThAZ9TU2kucfhDH7rfMHs1oPYziVGWRnZA==",
+ "version": "1.7.2",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/watchpack/-/watchpack-1.7.2.tgz",
+ "integrity": "sha512-ymVbbQP40MFTp+cNMvpyBpBtygHnPzPkHqoIwRRj/0B8KhqQwV8LaKjtbaxF2lK4vl8zN9wCxS46IFCU5K4W0g==",
"dev": true,
"requires": {
- "chokidar": "^2.1.8",
+ "chokidar": "^3.4.0",
"graceful-fs": "^4.1.2",
- "neo-async": "^2.5.0"
+ "neo-async": "^2.5.0",
+ "watchpack-chokidar2": "^2.0.0"
+ }
+ },
+ "watchpack-chokidar2": {
+ "version": "2.0.0",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/watchpack-chokidar2/-/watchpack-chokidar2-2.0.0.tgz",
+ "integrity": "sha512-9TyfOyN/zLUbA288wZ8IsMZ+6cbzvsNyEzSBp6e/zkifi6xxbl8SmQ/CxQq32k8NNqrdVEVUVSEf56L4rQ/ZxA==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "chokidar": "^2.1.8"
},
"dependencies": {
"anymatch": {
@@ -12624,6 +12744,7 @@
"resolved": "https://nexus.loafle.net/repository/npm-all/anymatch/-/anymatch-2.0.0.tgz",
"integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
"dev": true,
+ "optional": true,
"requires": {
"micromatch": "^3.1.4",
"normalize-path": "^2.1.1"
@@ -12634,6 +12755,7 @@
"resolved": "https://nexus.loafle.net/repository/npm-all/normalize-path/-/normalize-path-2.1.1.tgz",
"integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
"dev": true,
+ "optional": true,
"requires": {
"remove-trailing-separator": "^1.0.1"
}
@@ -12644,13 +12766,15 @@
"version": "1.13.1",
"resolved": "https://nexus.loafle.net/repository/npm-all/binary-extensions/-/binary-extensions-1.13.1.tgz",
"integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==",
- "dev": true
+ "dev": true,
+ "optional": true
},
"braces": {
"version": "2.3.2",
"resolved": "https://nexus.loafle.net/repository/npm-all/braces/-/braces-2.3.2.tgz",
"integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
"dev": true,
+ "optional": true,
"requires": {
"arr-flatten": "^1.1.0",
"array-unique": "^0.3.2",
@@ -12669,6 +12793,7 @@
"resolved": "https://nexus.loafle.net/repository/npm-all/chokidar/-/chokidar-2.1.8.tgz",
"integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==",
"dev": true,
+ "optional": true,
"requires": {
"anymatch": "^2.0.0",
"async-each": "^1.0.1",
@@ -12682,557 +12807,6 @@
"path-is-absolute": "^1.0.0",
"readdirp": "^2.2.1",
"upath": "^1.1.1"
- },
- "dependencies": {
- "fsevents": {
- "version": "1.2.12",
- "resolved": "https://nexus.loafle.net/repository/npm-all/fsevents/-/fsevents-1.2.12.tgz",
- "integrity": "sha512-Ggd/Ktt7E7I8pxZRbGIs7vwqAPscSESMrCSkx2FtWeqmheJgCo2R74fTsZFCifr0VTPwqRpPv17+6b8Zp7th0Q==",
- "dev": true,
- "optional": true,
- "requires": {
- "node-pre-gyp": "*"
- },
- "dependencies": {
- "abbrev": {
- "version": "1.1.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "ansi-regex": {
- "version": "2.1.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "aproba": {
- "version": "1.2.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "are-we-there-yet": {
- "version": "1.1.5",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "delegates": "^1.0.0",
- "readable-stream": "^2.0.6"
- }
- },
- "balanced-match": {
- "version": "1.0.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "brace-expansion": {
- "version": "1.1.11",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "chownr": {
- "version": "1.1.4",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "code-point-at": {
- "version": "1.1.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "concat-map": {
- "version": "0.0.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "console-control-strings": {
- "version": "1.1.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "core-util-is": {
- "version": "1.0.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "debug": {
- "version": "3.2.6",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "ms": "^2.1.1"
- }
- },
- "deep-extend": {
- "version": "0.6.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "delegates": {
- "version": "1.0.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "detect-libc": {
- "version": "1.0.3",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "fs-minipass": {
- "version": "1.2.7",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "minipass": "^2.6.0"
- }
- },
- "fs.realpath": {
- "version": "1.0.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "gauge": {
- "version": "2.7.4",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "aproba": "^1.0.3",
- "console-control-strings": "^1.0.0",
- "has-unicode": "^2.0.0",
- "object-assign": "^4.1.0",
- "signal-exit": "^3.0.0",
- "string-width": "^1.0.1",
- "strip-ansi": "^3.0.1",
- "wide-align": "^1.1.0"
- }
- },
- "glob": {
- "version": "7.1.6",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^3.0.4",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
- }
- },
- "has-unicode": {
- "version": "2.0.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "iconv-lite": {
- "version": "0.4.24",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "safer-buffer": ">= 2.1.2 < 3"
- }
- },
- "ignore-walk": {
- "version": "3.0.3",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "minimatch": "^3.0.4"
- }
- },
- "inflight": {
- "version": "1.0.6",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "once": "^1.3.0",
- "wrappy": "1"
- }
- },
- "inherits": {
- "version": "2.0.4",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "ini": {
- "version": "1.3.5",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "is-fullwidth-code-point": {
- "version": "1.0.0",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "number-is-nan": "^1.0.0"
- }
- },
- "isarray": {
- "version": "1.0.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "minimatch": {
- "version": "3.0.4",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "brace-expansion": "^1.1.7"
- }
- },
- "minimist": {
- "version": "1.2.5",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "minipass": {
- "version": "2.9.0",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "safe-buffer": "^5.1.2",
- "yallist": "^3.0.0"
- }
- },
- "minizlib": {
- "version": "1.3.3",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "minipass": "^2.9.0"
- }
- },
- "mkdirp": {
- "version": "0.5.3",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "minimist": "^1.2.5"
- }
- },
- "ms": {
- "version": "2.1.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "needle": {
- "version": "2.3.3",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "debug": "^3.2.6",
- "iconv-lite": "^0.4.4",
- "sax": "^1.2.4"
- }
- },
- "node-pre-gyp": {
- "version": "0.14.0",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "detect-libc": "^1.0.2",
- "mkdirp": "^0.5.1",
- "needle": "^2.2.1",
- "nopt": "^4.0.1",
- "npm-packlist": "^1.1.6",
- "npmlog": "^4.0.2",
- "rc": "^1.2.7",
- "rimraf": "^2.6.1",
- "semver": "^5.3.0",
- "tar": "^4.4.2"
- }
- },
- "nopt": {
- "version": "4.0.3",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "abbrev": "1",
- "osenv": "^0.1.4"
- }
- },
- "npm-bundled": {
- "version": "1.1.1",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "npm-normalize-package-bin": "^1.0.1"
- }
- },
- "npm-normalize-package-bin": {
- "version": "1.0.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "npm-packlist": {
- "version": "1.4.8",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "ignore-walk": "^3.0.1",
- "npm-bundled": "^1.0.1",
- "npm-normalize-package-bin": "^1.0.1"
- }
- },
- "npmlog": {
- "version": "4.1.2",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "are-we-there-yet": "~1.1.2",
- "console-control-strings": "~1.1.0",
- "gauge": "~2.7.3",
- "set-blocking": "~2.0.0"
- }
- },
- "number-is-nan": {
- "version": "1.0.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "object-assign": {
- "version": "4.1.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "once": {
- "version": "1.4.0",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "wrappy": "1"
- }
- },
- "os-homedir": {
- "version": "1.0.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "os-tmpdir": {
- "version": "1.0.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "osenv": {
- "version": "0.1.5",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "os-homedir": "^1.0.0",
- "os-tmpdir": "^1.0.0"
- }
- },
- "path-is-absolute": {
- "version": "1.0.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "process-nextick-args": {
- "version": "2.0.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "rc": {
- "version": "1.2.8",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "deep-extend": "^0.6.0",
- "ini": "~1.3.0",
- "minimist": "^1.2.0",
- "strip-json-comments": "~2.0.1"
- }
- },
- "readable-stream": {
- "version": "2.3.7",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "core-util-is": "~1.0.0",
- "inherits": "~2.0.3",
- "isarray": "~1.0.0",
- "process-nextick-args": "~2.0.0",
- "safe-buffer": "~5.1.1",
- "string_decoder": "~1.1.1",
- "util-deprecate": "~1.0.1"
- }
- },
- "rimraf": {
- "version": "2.7.1",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "glob": "^7.1.3"
- }
- },
- "safe-buffer": {
- "version": "5.1.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "safer-buffer": {
- "version": "2.1.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "sax": {
- "version": "1.2.4",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "semver": {
- "version": "5.7.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "set-blocking": {
- "version": "2.0.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "signal-exit": {
- "version": "3.0.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "string-width": {
- "version": "1.0.2",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "code-point-at": "^1.0.0",
- "is-fullwidth-code-point": "^1.0.0",
- "strip-ansi": "^3.0.0"
- }
- },
- "string_decoder": {
- "version": "1.1.1",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "safe-buffer": "~5.1.0"
- }
- },
- "strip-ansi": {
- "version": "3.0.1",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "ansi-regex": "^2.0.0"
- }
- },
- "strip-json-comments": {
- "version": "2.0.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "tar": {
- "version": "4.4.13",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "chownr": "^1.1.1",
- "fs-minipass": "^1.2.5",
- "minipass": "^2.8.6",
- "minizlib": "^1.2.1",
- "mkdirp": "^0.5.0",
- "safe-buffer": "^5.1.2",
- "yallist": "^3.0.3"
- }
- },
- "util-deprecate": {
- "version": "1.0.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "wide-align": {
- "version": "1.1.3",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "string-width": "^1.0.2 || 2"
- }
- },
- "wrappy": {
- "version": "1.0.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "yallist": {
- "version": "3.1.1",
- "bundled": true,
- "dev": true,
- "optional": true
- }
- }
- }
}
},
"extend-shallow": {
@@ -13240,6 +12814,7 @@
"resolved": "https://nexus.loafle.net/repository/npm-all/extend-shallow/-/extend-shallow-2.0.1.tgz",
"integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
"dev": true,
+ "optional": true,
"requires": {
"is-extendable": "^0.1.0"
}
@@ -13249,6 +12824,7 @@
"resolved": "https://nexus.loafle.net/repository/npm-all/fill-range/-/fill-range-4.0.0.tgz",
"integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
"dev": true,
+ "optional": true,
"requires": {
"extend-shallow": "^2.0.1",
"is-number": "^3.0.0",
@@ -13256,12 +12832,19 @@
"to-regex-range": "^2.1.0"
}
},
- "fsevents": {},
+ "fsevents": {
+ "version": "1.2.13",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/fsevents/-/fsevents-1.2.13.tgz",
+ "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
+ "dev": true,
+ "optional": true
+ },
"is-binary-path": {
"version": "1.0.1",
"resolved": "https://nexus.loafle.net/repository/npm-all/is-binary-path/-/is-binary-path-1.0.1.tgz",
"integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=",
"dev": true,
+ "optional": true,
"requires": {
"binary-extensions": "^1.0.0"
}
@@ -13271,6 +12854,7 @@
"resolved": "https://nexus.loafle.net/repository/npm-all/is-number/-/is-number-3.0.0.tgz",
"integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
"dev": true,
+ "optional": true,
"requires": {
"kind-of": "^3.0.2"
}
@@ -13280,6 +12864,7 @@
"resolved": "https://nexus.loafle.net/repository/npm-all/kind-of/-/kind-of-3.2.2.tgz",
"integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
"dev": true,
+ "optional": true,
"requires": {
"is-buffer": "^1.1.5"
}
@@ -13289,6 +12874,7 @@
"resolved": "https://nexus.loafle.net/repository/npm-all/readdirp/-/readdirp-2.2.1.tgz",
"integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==",
"dev": true,
+ "optional": true,
"requires": {
"graceful-fs": "^4.1.11",
"micromatch": "^3.1.10",
@@ -13300,6 +12886,7 @@
"resolved": "https://nexus.loafle.net/repository/npm-all/to-regex-range/-/to-regex-range-2.1.1.tgz",
"integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
"dev": true,
+ "optional": true,
"requires": {
"is-number": "^3.0.0",
"repeat-string": "^1.6.1"
@@ -13471,9 +13058,9 @@
}
},
"webpack-bundle-analyzer": {
- "version": "3.7.0",
- "resolved": "https://nexus.loafle.net/repository/npm-all/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.7.0.tgz",
- "integrity": "sha512-mETdjZ30a3Yf+NTB/wqTgACK7rAYQl5uxKK0WVTNmF0sM3Uv8s3R58YZMW7Rhu0Lk2Rmuhdj5dcH5Q76zCDVdA==",
+ "version": "3.8.0",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.8.0.tgz",
+ "integrity": "sha512-PODQhAYVEourCcOuU+NiYI7WdR8QyELZGgPvB1y2tjbUpbmcQOt5Q7jEK+ttd5se0KSBKD9SXHCEozS++Wllmw==",
"dev": true,
"requires": {
"acorn": "^7.1.1",
@@ -13515,9 +13102,9 @@
}
},
"mime": {
- "version": "2.4.4",
- "resolved": "https://nexus.loafle.net/repository/npm-all/mime/-/mime-2.4.4.tgz",
- "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==",
+ "version": "2.4.5",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/mime/-/mime-2.4.5.tgz",
+ "integrity": "sha512-3hQhEUF027BuxZjQA3s7rIv/7VCQPa27hN9u9g87sEkWaKwQPuXOkVKtOeiyUrnWqTDiOs8Ed2rwg733mB0R5w==",
"dev": true
}
}
@@ -13626,557 +13213,6 @@
"path-is-absolute": "^1.0.0",
"readdirp": "^2.2.1",
"upath": "^1.1.1"
- },
- "dependencies": {
- "fsevents": {
- "version": "1.2.12",
- "resolved": "https://nexus.loafle.net/repository/npm-all/fsevents/-/fsevents-1.2.12.tgz",
- "integrity": "sha512-Ggd/Ktt7E7I8pxZRbGIs7vwqAPscSESMrCSkx2FtWeqmheJgCo2R74fTsZFCifr0VTPwqRpPv17+6b8Zp7th0Q==",
- "dev": true,
- "optional": true,
- "requires": {
- "node-pre-gyp": "*"
- },
- "dependencies": {
- "abbrev": {
- "version": "1.1.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "ansi-regex": {
- "version": "2.1.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "aproba": {
- "version": "1.2.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "are-we-there-yet": {
- "version": "1.1.5",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "delegates": "^1.0.0",
- "readable-stream": "^2.0.6"
- }
- },
- "balanced-match": {
- "version": "1.0.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "brace-expansion": {
- "version": "1.1.11",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "chownr": {
- "version": "1.1.4",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "code-point-at": {
- "version": "1.1.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "concat-map": {
- "version": "0.0.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "console-control-strings": {
- "version": "1.1.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "core-util-is": {
- "version": "1.0.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "debug": {
- "version": "3.2.6",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "ms": "^2.1.1"
- }
- },
- "deep-extend": {
- "version": "0.6.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "delegates": {
- "version": "1.0.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "detect-libc": {
- "version": "1.0.3",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "fs-minipass": {
- "version": "1.2.7",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "minipass": "^2.6.0"
- }
- },
- "fs.realpath": {
- "version": "1.0.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "gauge": {
- "version": "2.7.4",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "aproba": "^1.0.3",
- "console-control-strings": "^1.0.0",
- "has-unicode": "^2.0.0",
- "object-assign": "^4.1.0",
- "signal-exit": "^3.0.0",
- "string-width": "^1.0.1",
- "strip-ansi": "^3.0.1",
- "wide-align": "^1.1.0"
- }
- },
- "glob": {
- "version": "7.1.6",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^3.0.4",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
- }
- },
- "has-unicode": {
- "version": "2.0.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "iconv-lite": {
- "version": "0.4.24",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "safer-buffer": ">= 2.1.2 < 3"
- }
- },
- "ignore-walk": {
- "version": "3.0.3",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "minimatch": "^3.0.4"
- }
- },
- "inflight": {
- "version": "1.0.6",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "once": "^1.3.0",
- "wrappy": "1"
- }
- },
- "inherits": {
- "version": "2.0.4",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "ini": {
- "version": "1.3.5",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "is-fullwidth-code-point": {
- "version": "1.0.0",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "number-is-nan": "^1.0.0"
- }
- },
- "isarray": {
- "version": "1.0.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "minimatch": {
- "version": "3.0.4",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "brace-expansion": "^1.1.7"
- }
- },
- "minimist": {
- "version": "1.2.5",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "minipass": {
- "version": "2.9.0",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "safe-buffer": "^5.1.2",
- "yallist": "^3.0.0"
- }
- },
- "minizlib": {
- "version": "1.3.3",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "minipass": "^2.9.0"
- }
- },
- "mkdirp": {
- "version": "0.5.3",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "minimist": "^1.2.5"
- }
- },
- "ms": {
- "version": "2.1.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "needle": {
- "version": "2.3.3",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "debug": "^3.2.6",
- "iconv-lite": "^0.4.4",
- "sax": "^1.2.4"
- }
- },
- "node-pre-gyp": {
- "version": "0.14.0",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "detect-libc": "^1.0.2",
- "mkdirp": "^0.5.1",
- "needle": "^2.2.1",
- "nopt": "^4.0.1",
- "npm-packlist": "^1.1.6",
- "npmlog": "^4.0.2",
- "rc": "^1.2.7",
- "rimraf": "^2.6.1",
- "semver": "^5.3.0",
- "tar": "^4.4.2"
- }
- },
- "nopt": {
- "version": "4.0.3",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "abbrev": "1",
- "osenv": "^0.1.4"
- }
- },
- "npm-bundled": {
- "version": "1.1.1",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "npm-normalize-package-bin": "^1.0.1"
- }
- },
- "npm-normalize-package-bin": {
- "version": "1.0.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "npm-packlist": {
- "version": "1.4.8",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "ignore-walk": "^3.0.1",
- "npm-bundled": "^1.0.1",
- "npm-normalize-package-bin": "^1.0.1"
- }
- },
- "npmlog": {
- "version": "4.1.2",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "are-we-there-yet": "~1.1.2",
- "console-control-strings": "~1.1.0",
- "gauge": "~2.7.3",
- "set-blocking": "~2.0.0"
- }
- },
- "number-is-nan": {
- "version": "1.0.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "object-assign": {
- "version": "4.1.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "once": {
- "version": "1.4.0",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "wrappy": "1"
- }
- },
- "os-homedir": {
- "version": "1.0.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "os-tmpdir": {
- "version": "1.0.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "osenv": {
- "version": "0.1.5",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "os-homedir": "^1.0.0",
- "os-tmpdir": "^1.0.0"
- }
- },
- "path-is-absolute": {
- "version": "1.0.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "process-nextick-args": {
- "version": "2.0.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "rc": {
- "version": "1.2.8",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "deep-extend": "^0.6.0",
- "ini": "~1.3.0",
- "minimist": "^1.2.0",
- "strip-json-comments": "~2.0.1"
- }
- },
- "readable-stream": {
- "version": "2.3.7",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "core-util-is": "~1.0.0",
- "inherits": "~2.0.3",
- "isarray": "~1.0.0",
- "process-nextick-args": "~2.0.0",
- "safe-buffer": "~5.1.1",
- "string_decoder": "~1.1.1",
- "util-deprecate": "~1.0.1"
- }
- },
- "rimraf": {
- "version": "2.7.1",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "glob": "^7.1.3"
- }
- },
- "safe-buffer": {
- "version": "5.1.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "safer-buffer": {
- "version": "2.1.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "sax": {
- "version": "1.2.4",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "semver": {
- "version": "5.7.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "set-blocking": {
- "version": "2.0.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "signal-exit": {
- "version": "3.0.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "string-width": {
- "version": "1.0.2",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "code-point-at": "^1.0.0",
- "is-fullwidth-code-point": "^1.0.0",
- "strip-ansi": "^3.0.0"
- }
- },
- "string_decoder": {
- "version": "1.1.1",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "safe-buffer": "~5.1.0"
- }
- },
- "strip-ansi": {
- "version": "3.0.1",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "ansi-regex": "^2.0.0"
- }
- },
- "strip-json-comments": {
- "version": "2.0.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "tar": {
- "version": "4.4.13",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "chownr": "^1.1.1",
- "fs-minipass": "^1.2.5",
- "minipass": "^2.8.6",
- "minizlib": "^1.2.1",
- "mkdirp": "^0.5.0",
- "safe-buffer": "^5.1.2",
- "yallist": "^3.0.3"
- }
- },
- "util-deprecate": {
- "version": "1.0.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "wide-align": {
- "version": "1.1.3",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "string-width": "^1.0.2 || 2"
- }
- },
- "wrappy": {
- "version": "1.0.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "yallist": {
- "version": "3.1.1",
- "bundled": true,
- "dev": true,
- "optional": true
- }
- }
- }
}
},
"debug": {
@@ -14209,7 +13245,13 @@
"to-regex-range": "^2.1.0"
}
},
- "fsevents": {},
+ "fsevents": {
+ "version": "1.2.13",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/fsevents/-/fsevents-1.2.13.tgz",
+ "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
+ "dev": true,
+ "optional": true
+ },
"is-absolute-url": {
"version": "3.0.3",
"resolved": "https://nexus.loafle.net/repository/npm-all/is-absolute-url/-/is-absolute-url-3.0.3.tgz",
@@ -14328,12 +13370,12 @@
}
},
"websocket-driver": {
- "version": "0.7.3",
- "resolved": "https://nexus.loafle.net/repository/npm-all/websocket-driver/-/websocket-driver-0.7.3.tgz",
- "integrity": "sha512-bpxWlvbbB459Mlipc5GBzzZwhoZgGEZLuqPaR0INBGnPAY1vdBX6hPnoFXiw+3yWxDuHyQjO2oXTMyS8A5haFg==",
+ "version": "0.7.4",
+ "resolved": "https://nexus.loafle.net/repository/npm-all/websocket-driver/-/websocket-driver-0.7.4.tgz",
+ "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==",
"dev": true,
"requires": {
- "http-parser-js": ">=0.4.0 <0.4.11",
+ "http-parser-js": ">=0.5.1",
"safe-buffer": ">=5.1.0",
"websocket-extensions": ">=0.1.1"
}
diff --git a/package.json b/package.json
index 98a723a..d465b20 100644
--- a/package.json
+++ b/package.json
@@ -68,16 +68,18 @@
"@ucap/ng-protocol-status": "~0.0.3",
"@ucap/ng-protocol-sync": "~0.0.3",
"@ucap/ng-protocol-umg": "~0.0.3",
- "@ucap/ng-store-authentication": "~0.0.10",
- "@ucap/ng-store-chat": "~0.0.5",
- "@ucap/ng-store-group": "~0.0.6",
- "@ucap/ng-store-organization": "~0.0.4",
+ "@ucap/ng-store-authentication": "~0.0.11",
+ "@ucap/ng-store-chat": "~0.0.13",
+ "@ucap/ng-store-group": "~0.0.14",
+ "@ucap/ng-store-organization": "~0.0.8",
"@ucap/ng-web-socket": "~0.0.2",
"@ucap/ng-web-storage": "~0.0.3",
- "@ucap/ng-ui": "~0.0.4",
- "@ucap/ng-ui-organization": "~0.0.2",
- "@ucap/ng-ui-authentication": "~0.0.16",
- "@ucap/ng-ui-group": "~0.0.4",
+ "@ucap/ng-ui": "~0.0.19",
+ "@ucap/ng-ui-organization": "~0.0.55",
+ "@ucap/ng-ui-authentication": "~0.0.24",
+ "@ucap/ng-ui-group": "~0.0.33",
+ "@ucap/ng-ui-chat": "~0.0.9",
+ "@ucap/ng-ui-material": "~0.0.4",
"@ucap/ng-ui-skin-default": "~0.0.1",
"@ucap/pi": "~0.0.5",
"@ucap/protocol": "~0.0.17",
@@ -86,7 +88,7 @@
"@ucap/protocol-event": "~0.0.5",
"@ucap/protocol-file": "~0.0.4",
"@ucap/protocol-group": "~0.0.5",
- "@ucap/protocol-info": "~0.0.5",
+ "@ucap/protocol-info": "~0.0.6",
"@ucap/protocol-inner": "~0.0.4",
"@ucap/protocol-option": "~0.0.7",
"@ucap/protocol-ping": "~0.0.6",
@@ -96,8 +98,9 @@
"@ucap/protocol-status": "~0.0.5",
"@ucap/protocol-sync": "~0.0.4",
"@ucap/protocol-umg": "~0.0.5",
+ "@ucap/ui-scss": "~0.0.4",
"@ucap/web-socket": "~0.0.10",
- "@ucap/web-storage": "~0.0.7",
+ "@ucap/web-storage": "~0.0.9",
"autolinker": "^3.13.0",
"axios": "^0.19.2",
"classlist.js": "^1.1.20150312",
diff --git a/src/app/app-provider.module.ts b/src/app/app-provider.module.ts
index 81926a9..f163b09 100644
--- a/src/app/app-provider.module.ts
+++ b/src/app/app-provider.module.ts
@@ -14,10 +14,16 @@ import { AppSessionResolver } from './resolvers/app-session.resolver';
import { AppAuthenticationService } from './services/app-authentication.service';
import { AppNativeService } from './services/app-native.service';
import { AppService } from './services/app.service';
+import { AppChatService } from './services/app-chat.service';
const GUARDS = [AppAuthenticationGuard];
const RESOLVERS = [AppSessionResolver];
-const SERVICES = [AppService, AppAuthenticationService, AppNativeService];
+const SERVICES = [
+ AppService,
+ AppAuthenticationService,
+ AppNativeService,
+ AppChatService
+];
const axiosFactory = () => {
const i = axios.create();
diff --git a/src/app/app.component.html b/src/app/app.component.html
index 934ff74..44e6390 100644
--- a/src/app/app.component.html
+++ b/src/app/app.component.html
@@ -1,2 +1,3 @@
-
-
+
+
+
diff --git a/src/app/app.component.scss b/src/app/app.component.scss
index fb4d464..efa0beb 100644
--- a/src/app/app.component.scss
+++ b/src/app/app.component.scss
@@ -1,4 +1,9 @@
:host {
width: 100%;
- height: auto !important;
+ height: 100%;
+
+ .app-container {
+ width: 100%;
+ height: 100%;
+ }
}
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 6475b7e..d3d55e2 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -1,7 +1,9 @@
+import { NgModule } from '@angular/core';
+
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
-import { NgModule } from '@angular/core';
+import { FlexLayoutModule } from '@angular/flex-layout';
import { StoreModule } from '@ngrx/store';
import { EffectsModule } from '@ngrx/effects';
@@ -65,6 +67,8 @@ import { environment } from '@environments';
BrowserModule,
BrowserAnimationsModule,
+ FlexLayoutModule,
+
LoggerModule.forRoot({}),
CommonApiModule.forRoot(environment.commonApiModuleConfig),
@@ -100,7 +104,12 @@ import { environment } from '@environments';
OrganizationStoreModule.forRoot({}),
AuthenticationStoreModule.forRoot({}),
GroupStoreModule.forRoot({ useMyDeptGroup: true, fixedGroupSeqs: [] }),
- ChatStoreModule.forRoot({}),
+ ChatStoreModule.forRoot({
+ eventRequestInitCount:
+ environment.productConfig.chat.eventRequestInitCount,
+ eventRequestDefaultCount:
+ environment.productConfig.chat.eventRequestDefaultCount
+ }),
OrganizationUiModule.forRoot({}),
diff --git a/src/app/app.theme.scss b/src/app/app.theme.scss
index 6cc3d03..eaac348 100644
--- a/src/app/app.theme.scss
+++ b/src/app/app.theme.scss
@@ -45,3 +45,11 @@ $lgRed-app-theme: mat-light-theme(
@include components-theme($lgRed-app-theme);
*/
@include ucap-material-theme($lgRed-app-theme);
+
+$ucap-ui-lg-red-theme: mat-light-theme(
+ $lgRed-app-primary,
+ $lgRed-app-accent,
+ $lgRed-app-warn
+);
+
+@include ucap-ui-theme($ucap-ui-lg-red-theme, $typography);
diff --git a/src/app/layouts/components/default-dialog.layout.component.html b/src/app/layouts/components/default-dialog.layout.component.html
new file mode 100644
index 0000000..21954be
--- /dev/null
+++ b/src/app/layouts/components/default-dialog.layout.component.html
@@ -0,0 +1,32 @@
+
diff --git a/src/app/layouts/components/default-dialog.layout.component.scss b/src/app/layouts/components/default-dialog.layout.component.scss
new file mode 100644
index 0000000..ef469a7
--- /dev/null
+++ b/src/app/layouts/components/default-dialog.layout.component.scss
@@ -0,0 +1,25 @@
+.layout-container {
+ width: 100%;
+ height: 100%;
+
+ .layout-header {
+ .layout-header-content {
+ width: 100%;
+ height: 100%;
+ }
+ }
+ .layout-body {
+ overflow: auto;
+
+ .layout-body-content {
+ width: 100%;
+ height: 100%;
+ }
+ }
+ .layout-action {
+ .layout-action-content {
+ width: 100%;
+ height: 100%;
+ }
+ }
+}
diff --git a/src/app/layouts/components/default-dialog.layout.component.spec.ts b/src/app/layouts/components/default-dialog.layout.component.spec.ts
new file mode 100644
index 0000000..f6efe75
--- /dev/null
+++ b/src/app/layouts/components/default-dialog.layout.component.spec.ts
@@ -0,0 +1,26 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { By } from '@angular/platform-browser';
+import { DebugElement } from '@angular/core';
+
+import { DefaultDialogLayoutComponent } from './default-dialog.layout.component';
+
+describe('app::layouts::DefaultDialogLayoutComponent', () => {
+ let component: DefaultDialogLayoutComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [DefaultDialogLayoutComponent]
+ }).compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(DefaultDialogLayoutComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/layouts/components/default-dialog.layout.component.ts b/src/app/layouts/components/default-dialog.layout.component.ts
new file mode 100644
index 0000000..b37bae1
--- /dev/null
+++ b/src/app/layouts/components/default-dialog.layout.component.ts
@@ -0,0 +1,50 @@
+import { Subject } from 'rxjs';
+
+import {
+ Component,
+ OnInit,
+ OnDestroy,
+ AfterViewInit,
+ ChangeDetectionStrategy,
+ ChangeDetectorRef,
+ Inject,
+ ViewChild,
+ ComponentFactoryResolver,
+ ViewContainerRef,
+ ComponentRef,
+ Input,
+ EventEmitter,
+ Output
+} from '@angular/core';
+
+@Component({
+ selector: 'app-layouts-default-dialog',
+ templateUrl: './default-dialog.layout.component.html',
+ styleUrls: ['./default-dialog.layout.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class DefaultDialogLayoutComponent implements OnInit, OnDestroy {
+ @Input()
+ disableClose = false;
+
+ @Output()
+ closed = new EventEmitter();
+
+ constructor(private changeDetectorRef: ChangeDetectorRef) {}
+
+ private ngOnDestroySubject: Subject;
+
+ ngOnInit(): void {
+ this.ngOnDestroySubject = new Subject();
+ }
+
+ ngOnDestroy(): void {
+ if (!!this.ngOnDestroySubject) {
+ this.ngOnDestroySubject.complete();
+ }
+ }
+
+ onClickClose(event: MouseEvent): void {
+ this.closed.emit(event);
+ }
+}
diff --git a/src/app/layouts/components/default.layout.component.html b/src/app/layouts/components/default.layout.component.html
index 2615bc8..1ae90fa 100644
--- a/src/app/layouts/components/default.layout.component.html
+++ b/src/app/layouts/components/default.layout.component.html
@@ -1,107 +1,271 @@
-
-
-
-
-
-
-
diff --git a/src/app/layouts/components/default.layout.component.scss b/src/app/layouts/components/default.layout.component.scss
index 4891a9d..2ee8d09 100644
--- a/src/app/layouts/components/default.layout.component.scss
+++ b/src/app/layouts/components/default.layout.component.scss
@@ -1,22 +1,303 @@
+@import '~@ucap/lg-scss/mixins';
+
+:host {
+ width: 100%;
+ height: 100%;
+}
+
.layout-container {
- display: flex;
+ width: 100%;
+ height: 100%;
- .navi-container {
- width: 70px;
- }
-
- .content-container {
- .left-sidenav {
+ .navitab-page {
+ //GNB /////////////////////////////////////
+ .gnb {
+ background-color: $gray-ref0;
+ width: 60px;
+ height: 100%;
display: flex;
flex-direction: column;
- width: 370px;
- height: 100%;
- max-width: 90%;
- overflow: hidden;
+ justify-content: space-between;
+ align-items: center;
+ border-right: 1px solid rgba(204, 204, 204, 0.8);
+ .mat-gnb-toolbar {
+ flex-basis: 64px;
+ .img-logo {
+ margin: 9px 0 5px;
+ }
+ }
+ .left-container {
+ display: flex;
+ width: calc(100% - 28px);
+ height: 100%;
+ }
+ .global-menu {
+ width: 100%;
+ background-color: $gray-ref0;
+ flex-grow: 1;
+ }
+ .btn-homepage-area {
+ flex-flow: column-reverse;
+ position: relative;
+ button {
+ padding: 30px 0 12px;
+ &::before {
+ content: '';
+ width: 20px;
+ height: 20px;
+ background-image: url(../../../assets/images/ico/btn_gnb_hompage.svg);
+ display: block;
+ position: absolute;
+ top: 9px;
+ left: calc(50% - 10px);
+ }
+ em {
+ font-style: normal;
+ font-size: 8px;
+ color: $gray-re6;
+ display: block;
+ line-height: 9px;
+ font-weight: 600;
+ }
+ }
+ }
+ ::ng-deep .global-menu {
+ //display: flex;
+ //flex-direction: row;
+ .mat-tab-header {
+ border-bottom: none !important;
+ width: 100%;
+ }
+ .mat-tab-label-container {
+ .mat-tab-list {
+ .mat-tab-labels {
+ display: flex;
+ flex-flow: column;
+ justify-content: space-around;
+ height: 272px;
+ border-bottom: none;
+
+ .mat-tab-label {
+ width: 100%;
+ height: 32px;
+ padding: 0;
+ min-width: 0 !important;
+ .mat-tab-label-content {
+ .icon-item {
+ display: inline-flex;
+ width: 32px;
+ height: 32px;
+ border-radius: 50%;
+ justify-content: center;
+ align-items: center;
+ //transform: scale(0.9);
+ transition: transform 0.3s cubic-bezier(0.4, 0, 0, 1);
+
+ svg {
+ //width: 24px;
+ //height: 24px;
+ stroke: $gray-re9;
+ stroke-width: 2;
+ stroke-linecap: square;
+ stroke-linejoin: miter;
+ fill: none;
+ g {
+ icon_gnb_organiztion_g32 {
+ .prefix__cls-1,
+ .prefix__cls-4 {
+ fill: none;
+ }
+ .prefix__cls-1 {
+ stroke: #999;
+ stroke-width: 2px;
+ }
+ .prefix__cls-3 {
+ stroke: none;
+ }
+ }
+ icon_gnb_message_g32 {
+ .prefix__cls-1 {
+ fill: none;
+ stroke: #999;
+ stroke-width: 2px;
+ stroke-linejoin: round;
+ }
+ }
+ }
+ }
+ .mat-badge-content {
+ right: -9px !important;
+ border: 1px solid #ffbf2a;
+ //width: 24px;
+ //height: 24px;
+ box-sizing: content-box;
+ top: -10px !important;
+ }
+ }
+ }
+ &.mat-tab-label-active {
+ opacity: 0;
+ svg {
+ stroke: #fff !important;
+ g {
+ prefix_23,
+ icon_gnb_chat_g32,
+ icon_gnb_call_g32 {
+ path {
+ &:nth-child(2) {
+ fill: #fff !important;
+ }
+ }
+ }
+ icon_gnb_organiztion_g32 {
+ .prefix__cls-1 {
+ stroke: #fff !important;
+ }
+ path {
+ &:nth-last-of-type(2) {
+ stroke: #fff !important;
+ }
+ }
+ }
+ icon_gnb_message_g32 {
+ .prefix__cls-1 {
+ stroke: #fff !important;
+ }
+ path {
+ &:nth-child(3) {
+ stroke: #fff !important;
+ }
+ }
+ }
+ }
+ }
+ }
+ &[aria-selected='true'] {
+ opacity: 1;
+ .mat-tab-label-content {
+ .icon-item {
+ transform: scale(1);
+ }
+ }
+ }
+ }
+ }
+ .mat-ink-bar {
+ opacity: 0;
+ }
+ }
+ }
+ .mat-tab-body-wrapper {
+ .mat-tab-body {
+ height: 100%;
+ width: 100%;
+ }
+ }
+ }
+ }
+ /////////////////////////////////////GNB //
+ }
+
+ .content-page {
+ width: calc(100% - 60px);
+ height: 100%;
+
+ .content-body {
+ width: 100%;
+ height: calc(100% - 38px);
+
+ mat-sidenav-container {
+ width: 100%;
+ height: 100%;
+
+ .left-sidenav {
+ width: 370px;
+ max-width: 90%;
+
+ .left-sidenav-container {
+ width: 100%;
+ height: 100%;
+ .top-bar {
+ font: {
+ size: 13px;
+ color: $gray-re70;
+ }
+ line-height: 15px;
+ padding: 25px 0 0 17px;
+ }
+ }
+ }
+
+ mat-sidenav-content {
+ .content-sidenav-container {
+ width: 100%;
+ height: 100%;
+
+ .content-sidenav-body {
+ overflow: auto;
+ }
+ }
+ }
+ }
}
- .content-drawer {
- flex: 0 0 auto;
+ .content-statusbar {
+ width: 100%;
+ height: 100%;
+ //Footer ////////////////////////
+ .footer {
+ display: flex;
+ justify-content: space-between;
+ width: 100%;
+ height: 38px;
+ border-top: 1px solid $line-color-gray01;
+ background-color: $white;
+ .foot-info {
+ display: flex;
+ flex-grow: 1;
+ align-items: center;
+ font-size: 12px;
+ color: $gray-re70;
+ @include font-family($font-light);
+ font-weight: 600;
+ &.version-info {
+ .var-txt {
+ padding-left: 8px;
+ color: $gray-re70;
+ &::before {
+ content: '';
+ width: 1px;
+ height: 10px;
+ display: inline-block;
+ background-color: #d4d4d4;
+ margin-right: 8px;
+ }
+ &:first-of-type {
+ &::before {
+ width: 0;
+ }
+ }
+ &.new-var {
+ color: $brown;
+ }
+ }
+ }
+ &.help-info {
+ flex-flow: row-reverse;
+ padding-right: 20px;
+ p {
+ margin: 0;
+ span {
+ color: $lipstick;
+ }
+ em {
+ margin-left: 10px;
+ }
+ }
+ }
+ }
+ }
+ //////////////////////// Footer//
}
}
}
diff --git a/src/app/layouts/components/default.layout.component.ts b/src/app/layouts/components/default.layout.component.ts
index cb6138d..bb098cc 100644
--- a/src/app/layouts/components/default.layout.component.ts
+++ b/src/app/layouts/components/default.layout.component.ts
@@ -26,6 +26,12 @@ export class DefaultLayoutComponent implements OnInit, OnDestroy {
@ViewChild('leftSidenav', { static: true })
leftSidenav: MatSidenav;
+ showStatusbar = true;
+
+ /** FAB */
+ fabButtonShow = true;
+ fabButtons: { icon: string; tooltip?: string; divisionType?: string }[];
+
private windowSizeSubscription: Subscription;
constructor(
@@ -50,6 +56,8 @@ export class DefaultLayoutComponent implements OnInit, OnDestroy {
});
this.setTabGroup(this.router.url);
+
+ this.setFabInitial(NAVS[0]);
}
ngOnDestroy(): void {
@@ -70,6 +78,7 @@ export class DefaultLayoutComponent implements OnInit, OnDestroy {
NAVS[event.index],
{ outlets: { content: 'index' } }
]);
+ this.setFabInitial(NAVS[event.index]);
}
onClickToggleLeftSidenav() {
@@ -81,8 +90,107 @@ export class DefaultLayoutComponent implements OnInit, OnDestroy {
}
private setTabGroup(url: string) {
- this.navTabGroup.selectedIndex = NAVS.findIndex((v) =>
- url.startsWith(`/${v}`)
- );
+ if (!!this.navTabGroup) {
+ this.navTabGroup.selectedIndex = NAVS.findIndex((v) =>
+ url.startsWith(`/${v}`)
+ );
+ }
+ }
+
+ setFabInitial(type: string) {
+ switch (type) {
+ case 'group':
+ {
+ this.fabButtonShow = true;
+ this.fabButtons = [
+ {
+ icon: 'add',
+ tooltip: '그룹 추가',
+ divisionType: 'GROUP_NEW_ADD'
+ }
+ ];
+ }
+ break;
+ case 'chat':
+ {
+ this.fabButtonShow = true;
+ this.fabButtons = [
+ {
+ icon: 'chat',
+ tooltip: '대화 추가',
+ divisionType: 'CAHT_NEW_ADD'
+ }
+ ];
+
+ // if (environment.productConfig.CommonSetting.useTimerRoom) {
+ // this.fabButtons.push({
+ // icon: 'timer',
+ // tooltip: this.translateService.instant('chat.newTimerChat'),
+ // divisionType: 'CHAT_NEW_TIMER_ADD'
+ // });
+ // }
+ }
+ break;
+ case 'organization':
+ {
+ this.fabButtonShow = false;
+ }
+ break;
+ case 'message':
+ {
+ this.fabButtonShow = true;
+ this.fabButtons = [
+ {
+ icon: 'add',
+ tooltip: '쪽지 추가',
+ divisionType: 'MESSAGE_NEW'
+ }
+ ];
+ }
+ break;
+ // case MainMenu.Call:
+ // {
+ // this.fabButtonShow = false;
+ // }
+ // break;
+
+ default: {
+ this.fabButtonShow = false;
+ }
+ }
+ }
+
+ onClickFab(params: { btn: any }) {
+ const btn = params.btn as {
+ icon: string;
+ tooltip?: string;
+ divisionType?: string;
+ };
+
+ switch (btn.divisionType) {
+ case 'GROUP_NEW_ADD':
+ {
+ this.logService.debug('GROUP_NEW_ADD');
+ }
+ break;
+ case 'CAHT_NEW_ADD':
+ {
+ this.logService.debug('CAHT_NEW_ADD');
+ }
+ break;
+ case 'CHAT_NEW_TIMER_ADD':
+ {
+ // if (environment.productConfig.CommonSetting.useTimerRoom) {
+ // this.onClickNewChat('TIMER');
+ // }
+ }
+ break;
+
+ case 'MESSAGE_NEW':
+ {
+ this.logService.debug('MESSAGE_NEW');
+ }
+ break;
+ }
}
}
diff --git a/src/app/layouts/components/index.ts b/src/app/layouts/components/index.ts
index ee3f5da..78d1e3a 100644
--- a/src/app/layouts/components/index.ts
+++ b/src/app/layouts/components/index.ts
@@ -3,8 +3,15 @@ import { TopBarComponent } from './top-bar.component';
import { DefaultLayoutComponent } from './default.layout.component';
import { NoNaviLayoutComponent } from './no-navi.layout.component';
+import { DefaultDialogLayoutComponent } from './default-dialog.layout.component';
+import { SelectorLayoutComponent } from './selector.layout.component';
+
export const COMPONENTS = [
TopBarComponent,
DefaultLayoutComponent,
- NoNaviLayoutComponent
+ NoNaviLayoutComponent,
+
+ DefaultDialogLayoutComponent,
+
+ SelectorLayoutComponent
];
diff --git a/src/app/layouts/components/no-navi.layout.component.html b/src/app/layouts/components/no-navi.layout.component.html
index 0680b43..91a8275 100644
--- a/src/app/layouts/components/no-navi.layout.component.html
+++ b/src/app/layouts/components/no-navi.layout.component.html
@@ -1 +1,8 @@
-
+
diff --git a/src/app/layouts/components/no-navi.layout.component.scss b/src/app/layouts/components/no-navi.layout.component.scss
index e69de29..7d4bc06 100644
--- a/src/app/layouts/components/no-navi.layout.component.scss
+++ b/src/app/layouts/components/no-navi.layout.component.scss
@@ -0,0 +1,2 @@
+.layout-container {
+}
diff --git a/src/app/layouts/components/no-navi.layout.component.ts b/src/app/layouts/components/no-navi.layout.component.ts
index 4663b32..7d186c0 100644
--- a/src/app/layouts/components/no-navi.layout.component.ts
+++ b/src/app/layouts/components/no-navi.layout.component.ts
@@ -6,5 +6,7 @@ import { Component } from '@angular/core';
styleUrls: ['./no-navi.layout.component.scss']
})
export class NoNaviLayoutComponent {
+ showTopbar = true;
+
constructor() {}
}
diff --git a/src/app/layouts/components/selector.layout.component.html b/src/app/layouts/components/selector.layout.component.html
new file mode 100644
index 0000000..4651501
--- /dev/null
+++ b/src/app/layouts/components/selector.layout.component.html
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/app/layouts/components/selector.layout.component.scss b/src/app/layouts/components/selector.layout.component.scss
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/layouts/components/selector.layout.component.spec.ts b/src/app/layouts/components/selector.layout.component.spec.ts
new file mode 100644
index 0000000..dda5e1c
--- /dev/null
+++ b/src/app/layouts/components/selector.layout.component.spec.ts
@@ -0,0 +1,26 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { By } from '@angular/platform-browser';
+import { DebugElement } from '@angular/core';
+
+import { SelectorLayoutComponent } from './selector.layout.component';
+
+describe('ucap::ui-group::SelectorLayoutComponent', () => {
+ let component: SelectorLayoutComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [SelectorLayoutComponent]
+ }).compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(SelectorLayoutComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/layouts/components/selector.layout.component.ts b/src/app/layouts/components/selector.layout.component.ts
new file mode 100644
index 0000000..5d3104a
--- /dev/null
+++ b/src/app/layouts/components/selector.layout.component.ts
@@ -0,0 +1,22 @@
+import {
+ Component,
+ OnInit,
+ OnDestroy,
+ Input,
+ ChangeDetectionStrategy,
+ ChangeDetectorRef
+} from '@angular/core';
+
+@Component({
+ selector: 'app-layout-selector',
+ templateUrl: './selector.layout.component.html',
+ styleUrls: ['./selector.layout.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class SelectorLayoutComponent implements OnInit, OnDestroy {
+ constructor(private changeDetectorRef: ChangeDetectorRef) {}
+
+ ngOnInit(): void {}
+
+ ngOnDestroy(): void {}
+}
diff --git a/src/app/layouts/components/top-bar.component.html b/src/app/layouts/components/top-bar.component.html
index 519996b..6521605 100644
--- a/src/app/layouts/components/top-bar.component.html
+++ b/src/app/layouts/components/top-bar.component.html
@@ -1,4 +1,4 @@
-
+
-
+
diff --git a/src/app/layouts/components/top-bar.component.scss b/src/app/layouts/components/top-bar.component.scss
index ab8ecee..9ef7b36 100644
--- a/src/app/layouts/components/top-bar.component.scss
+++ b/src/app/layouts/components/top-bar.component.scss
@@ -1,12 +1,6 @@
.title-bar {
width: 100%;
- height: 50px;
- -webkit-user-select: none;
- -webkit-app-region: drag;
- position: fixed;
- right: 0;
- top: 0;
- display: flex;
+ height: 100%;
padding: 0;
- cursor: pointer;
+ background-color: #ffffff;
}
diff --git a/src/app/layouts/layouts.module.ts b/src/app/layouts/layouts.module.ts
index 43eaf73..83663e0 100644
--- a/src/app/layouts/layouts.module.ts
+++ b/src/app/layouts/layouts.module.ts
@@ -5,11 +5,14 @@ import { RouterModule } from '@angular/router';
import { FlexLayoutModule } from '@angular/flex-layout';
+import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
-import { MatTabsModule } from '@angular/material/tabs';
import { MatSidenavModule } from '@angular/material/sidenav';
+import { MatTabsModule } from '@angular/material/tabs';
import { MatToolbarModule } from '@angular/material/toolbar';
+import { PerfectScrollbarModule } from 'ngx-perfect-scrollbar';
+
import { UiModule } from '@ucap/ng-ui';
import { COMPONENTS } from './components';
@@ -18,16 +21,18 @@ import { DIALOGS } from './dialogs';
@NgModule({
imports: [
CommonModule,
-
RouterModule,
FlexLayoutModule,
+ MatButtonModule,
MatIconModule,
- MatTabsModule,
MatSidenavModule,
+ MatTabsModule,
MatToolbarModule,
+ PerfectScrollbarModule,
+
UiModule
],
exports: [...COMPONENTS, ...DIALOGS],
diff --git a/src/app/pages/account/account.page.module.ts b/src/app/pages/account/account.page.module.ts
index 16a5e9c..f7b51bd 100644
--- a/src/app/pages/account/account.page.module.ts
+++ b/src/app/pages/account/account.page.module.ts
@@ -3,6 +3,7 @@ import { CommonModule } from '@angular/common';
import { FlexLayoutModule } from '@angular/flex-layout';
+import { AppAuthenticationModule } from '@app/ucap/authentication/authentication.module';
import { AppAccountSectionModule } from '@app/sections/account/account.section.module';
import { AppAccountRoutingPageModule } from './account-routing.page.module';
@@ -13,6 +14,7 @@ import { COMPONENTS } from './components';
imports: [
CommonModule,
FlexLayoutModule,
+ AppAuthenticationModule,
AppAccountSectionModule,
AppAccountRoutingPageModule
],
diff --git a/src/app/pages/account/components/login.page.component.html b/src/app/pages/account/components/login.page.component.html
index 7050ae7..b5ac468 100644
--- a/src/app/pages/account/components/login.page.component.html
+++ b/src/app/pages/account/components/login.page.component.html
@@ -1,22 +1,3 @@
-
-
diff --git a/src/app/pages/account/components/login.page.component.scss b/src/app/pages/account/components/login.page.component.scss
index 140163e..ac07d68 100644
--- a/src/app/pages/account/components/login.page.component.scss
+++ b/src/app/pages/account/components/login.page.component.scss
@@ -1,8 +1,16 @@
-@import '../../../../assets/scss/components';
+@import '~@ucap/lg-scss/mixins';
+
+$login-bg-w: 100/1920;
+$login-bg-h: 100/1080;
.login-container {
width: 100%;
- min-height: 100vh;
+ height: 100%;
+ overflow: auto;
+ // box-sizing: border-box;
+ // display: flex;
+ // flex-direction: column;
+
background-color: $bg-gray;
background-image: url(../../../../assets/images/bg/bg_login_circle_square01.svg),
url(../../../../assets/images/bg/bg_login_circle_stroke01.svg),
@@ -54,8 +62,4 @@
unquote($login-bg-w * 102 + '%'), unquote($login-bg-w * 130 + '%'),
unquote($login-bg-w * 184 + '%'), unquote($login-bg-w * 370 + '%'),
unquote($login-bg-w * 122 + '%'), unquote($login-bg-w * 75 + '%');
- padding-top: 5%;
- box-sizing: border-box;
- display: flex;
- flex-direction: column;
}
diff --git a/src/app/pages/chat/chat-routing.page.module.ts b/src/app/pages/chat/chat-routing.page.module.ts
index f8e5339..8b8a96a 100644
--- a/src/app/pages/chat/chat-routing.page.module.ts
+++ b/src/app/pages/chat/chat-routing.page.module.ts
@@ -3,6 +3,7 @@ import { Routes, RouterModule } from '@angular/router';
import { IndexPageComponent } from './components/index.page.component';
import { SidenavPageComponent } from './components/sidenav.page.component';
+import { ChatRoomPageComponent } from './components/chat-room.page.component';
const routes: Routes = [
{
@@ -10,6 +11,11 @@ const routes: Routes = [
outlet: 'content',
component: IndexPageComponent
},
+ {
+ path: 'chatroom',
+ outlet: 'content',
+ component: ChatRoomPageComponent
+ },
{
path: '',
component: SidenavPageComponent
diff --git a/src/app/pages/chat/chat.page.module.ts b/src/app/pages/chat/chat.page.module.ts
index bc9dddc..f1fe2d1 100644
--- a/src/app/pages/chat/chat.page.module.ts
+++ b/src/app/pages/chat/chat.page.module.ts
@@ -3,17 +3,35 @@ import { CommonModule } from '@angular/common';
import { FlexLayoutModule } from '@angular/flex-layout';
+import { MatIconModule } from '@angular/material/icon';
+import { MatMenuModule } from '@angular/material/menu';
+import { MatCheckboxModule } from '@angular/material/checkbox';
+import { MatButtonModule } from '@angular/material/button';
+import { MatSidenavModule } from '@angular/material/sidenav';
+
+import { AppChatSectionModule } from '@app/sections/chat/chat.section.module';
+
import { AppChatRoutingPageModule } from './chat-routing.page.module';
-import { IndexPageComponent } from './components/index.page.component';
-import { SidenavPageComponent } from './components/sidenav.page.component';
-
-export const COMPONENTS = [IndexPageComponent, SidenavPageComponent];
-
-export { IndexPageComponent, SidenavPageComponent };
+import { UiModule } from '@ucap/ng-ui';
+import { COMPONENTS } from './components';
@NgModule({
- imports: [CommonModule, FlexLayoutModule, AppChatRoutingPageModule],
+ imports: [
+ CommonModule,
+ FlexLayoutModule,
+
+ MatIconModule,
+ MatMenuModule,
+ MatCheckboxModule,
+ MatButtonModule,
+ MatSidenavModule,
+
+ AppChatSectionModule,
+ AppChatRoutingPageModule,
+
+ UiModule
+ ],
declarations: [...COMPONENTS],
entryComponents: []
})
diff --git a/src/app/pages/chat/components/chat-room.page.component.html b/src/app/pages/chat/components/chat-room.page.component.html
new file mode 100644
index 0000000..ddde23d
--- /dev/null
+++ b/src/app/pages/chat/components/chat-room.page.component.html
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+ Right Sections.
+
+
+
diff --git a/src/app/pages/chat/components/chat-room.page.component.scss b/src/app/pages/chat/components/chat-room.page.component.scss
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/pages/chat/components/chat-room.page.component.spec.ts b/src/app/pages/chat/components/chat-room.page.component.spec.ts
new file mode 100644
index 0000000..2f39782
--- /dev/null
+++ b/src/app/pages/chat/components/chat-room.page.component.spec.ts
@@ -0,0 +1,32 @@
+import { TestBed, async } from '@angular/core/testing';
+import { RouterTestingModule } from '@angular/router/testing';
+import { ChatRoomPageComponent } from './chat-room.page.component';
+
+describe('app::pages::chat::ChatRoomPageComponent', () => {
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ imports: [RouterTestingModule],
+ declarations: [ChatRoomPageComponent]
+ }).compileComponents();
+ }));
+
+ it('should create the app', () => {
+ const fixture = TestBed.createComponent(ChatRoomPageComponent);
+ const app = fixture.componentInstance;
+ expect(app).toBeTruthy();
+ });
+
+ it(`should have as title 'ucap-lg-web'`, () => {
+ const fixture = TestBed.createComponent(ChatRoomPageComponent);
+ const app = fixture.componentInstance;
+ });
+
+ it('should render title', () => {
+ const fixture = TestBed.createComponent(ChatRoomPageComponent);
+ fixture.detectChanges();
+ const compiled = fixture.nativeElement;
+ expect(compiled.querySelector('.content span').textContent).toContain(
+ 'ucap-lg-web app is running!'
+ );
+ });
+});
diff --git a/src/app/pages/chat/components/chat-room.page.component.ts b/src/app/pages/chat/components/chat-room.page.component.ts
new file mode 100644
index 0000000..6205d58
--- /dev/null
+++ b/src/app/pages/chat/components/chat-room.page.component.ts
@@ -0,0 +1,41 @@
+import { Component, ViewChild, OnInit, OnDestroy } from '@angular/core';
+import { ActivatedRoute, Params } from '@angular/router';
+
+import { MatDrawer } from '@angular/material/sidenav';
+
+import { Subscription } from 'rxjs';
+import { QueryParams } from '../types/params.type';
+
+@Component({
+ selector: 'app-pages-chat-room',
+ templateUrl: './chat-room.page.component.html',
+ styleUrls: ['./chat-room.page.component.scss']
+})
+export class ChatRoomPageComponent implements OnInit, OnDestroy {
+ private paramsSubscription: Subscription;
+ isChatSearch = false;
+ roomId: string;
+
+ @ViewChild('chatRightDrawer', { static: false })
+ chatRightDrawer: MatDrawer;
+
+ constructor(private activatedRoute: ActivatedRoute) {}
+
+ ngOnInit(): void {
+ this.paramsSubscription = this.activatedRoute.queryParams.subscribe(
+ (params: Params) => {
+ const seqParam = params[QueryParams.ROOM_ID];
+ this.roomId = !!seqParam ? seqParam : undefined;
+ }
+ );
+ }
+
+ ngOnDestroy(): void {
+ if (!!this.paramsSubscription) {
+ this.paramsSubscription.unsubscribe();
+ }
+ }
+ onRightDrawerToggle(): void {
+ this.chatRightDrawer.toggle();
+ }
+}
diff --git a/src/app/pages/chat/components/index.ts b/src/app/pages/chat/components/index.ts
index c5347e7..f1ef066 100644
--- a/src/app/pages/chat/components/index.ts
+++ b/src/app/pages/chat/components/index.ts
@@ -1,4 +1,9 @@
import { IndexPageComponent } from './index.page.component';
import { SidenavPageComponent } from './sidenav.page.component';
+import { ChatRoomPageComponent } from './chat-room.page.component';
-export const COMPONENTS = [IndexPageComponent, SidenavPageComponent];
+export const COMPONENTS = [
+ IndexPageComponent,
+ SidenavPageComponent,
+ ChatRoomPageComponent
+];
diff --git a/src/app/pages/chat/components/sidenav.page.component.html b/src/app/pages/chat/components/sidenav.page.component.html
index a409ebd..bf10f80 100644
--- a/src/app/pages/chat/components/sidenav.page.component.html
+++ b/src/app/pages/chat/components/sidenav.page.component.html
@@ -1,3 +1,56 @@
-
- sidenav page of chat is works!
+
+
+
+
+
+ {{ this.selectedRoomList.length }}/{{
+ !!this.searchObj.isShowSearch
+ ? this.searchResultList.length
+ : this.roomList.length
+ }}
+
+
+
+ 전체선택
+
+
+
+
diff --git a/src/app/pages/chat/components/sidenav.page.component.ts b/src/app/pages/chat/components/sidenav.page.component.ts
index 3db9893..2181d29 100644
--- a/src/app/pages/chat/components/sidenav.page.component.ts
+++ b/src/app/pages/chat/components/sidenav.page.component.ts
@@ -1,13 +1,175 @@
-import { Component, Inject } from '@angular/core';
-import { Router } from '@angular/router';
+import { Component, ChangeDetectorRef, OnInit, OnDestroy } from '@angular/core';
import { LogService } from '@ucap/ng-logger';
+import { Subject, of } from 'rxjs';
+import { Store, select } from '@ngrx/store';
+import { takeUntil, take, map, catchError } from 'rxjs/operators';
+import { RoomSelector, RoomActions } from '@ucap/ng-store-chat';
+import { RoomInfo, ExitAllRequest } from '@ucap/protocol-room';
+import { MatDialog } from '@angular/material/dialog';
+import {
+ ConfirmDialogComponent,
+ ConfirmDialogData,
+ ConfirmDialogResult
+} from '@ucap/ng-ui';
+import { I18nService } from '@ucap/ng-i18n';
@Component({
selector: 'app-pages-chat-sidenav',
templateUrl: './sidenav.page.component.html',
styleUrls: ['./sidenav.page.component.scss']
})
-export class SidenavPageComponent {
- constructor(private logService: LogService) {}
+export class SidenavPageComponent implements OnInit, OnDestroy {
+ searchObj: any = {
+ isShowSearch: false,
+ searchWord: ''
+ };
+
+ checkable = false;
+
+ roomList: RoomInfo[];
+ selectedRoomList: RoomInfo[] = [];
+ searchResultList: RoomInfo[] = [];
+
+ private ngOnDestroySubject: Subject
;
+
+ constructor(
+ private store: Store,
+ private dialog: MatDialog,
+ private i18nService: I18nService,
+ private logService: LogService,
+ private changeDetectorRef: ChangeDetectorRef
+ ) {}
+
+ ngOnInit(): void {
+ this.ngOnDestroySubject = new Subject();
+
+ this.store
+ .pipe(takeUntil(this.ngOnDestroySubject), select(RoomSelector.rooms))
+ .subscribe((rooms) => {
+ rooms = (rooms || []).filter((info) => info.isJoinRoom);
+ this.roomList = rooms;
+ });
+ }
+
+ ngOnDestroy(): void {
+ if (!!this.ngOnDestroySubject) {
+ this.ngOnDestroySubject.complete();
+ }
+ }
+
+ onToggleChackable(checkable: boolean): void {
+ if (!!checkable) {
+ if (!!this.selectedRoomList && this.selectedRoomList.length > 0) {
+ const dialogRef = this.dialog.open<
+ ConfirmDialogComponent,
+ ConfirmDialogData,
+ ConfirmDialogResult
+ >(ConfirmDialogComponent, {
+ data: {
+ title: this.i18nService.t('room.dialog.titleExitFromRoom'),
+ html: this.i18nService.t('room.dialog.confirmExitFromRoom')
+ }
+ });
+
+ dialogRef
+ .afterClosed()
+ .pipe(take(1))
+ .subscribe((result) => {
+ if (!!result && !!result.choice) {
+ this.store.dispatch(
+ RoomActions.delMulti({
+ req: {
+ roomIds: this.selectedRoomList.map(
+ (roomInfo) => roomInfo.roomId
+ )
+ } as ExitAllRequest
+ })
+ );
+ }
+ });
+ }
+ } else {
+ this.selectedRoomList = [];
+ }
+
+ this.checkable = checkable;
+ }
+
+ getCheckedAllItem(): boolean {
+ const targetRoomList = !!this.searchObj.isShowSearch
+ ? this.searchResultList
+ : this.roomList;
+
+ if (
+ !targetRoomList ||
+ targetRoomList.length === 0 ||
+ targetRoomList.filter(
+ (item) =>
+ !(
+ this.selectedRoomList.filter((info) => info.roomId === item.roomId)
+ .length > 0
+ )
+ ).length > 0
+ ) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ onToggleAllItem(value: boolean): void {
+ if (!!value) {
+ const targetRoomList = !!this.searchObj.isShowSearch
+ ? this.searchResultList
+ : this.roomList;
+
+ this.selectedRoomList = targetRoomList.slice();
+ } else {
+ this.selectedRoomList = [];
+ }
+ this.changeDetectorRef.detectChanges();
+ }
+
+ onToggleItem(event: { checked: boolean; roomInfo: RoomInfo }): void {
+ if (!!event.checked) {
+ if (
+ !this.selectedRoomList.some(
+ (info) => info.roomId === event.roomInfo.roomId
+ )
+ ) {
+ this.selectedRoomList.push(event.roomInfo);
+ }
+ } else {
+ if (!!this.selectedRoomList && this.selectedRoomList.length > 0) {
+ const index = this.selectedRoomList.findIndex(
+ (info) => info.roomId === event.roomInfo.roomId
+ );
+ if (index > -1) {
+ this.selectedRoomList.splice(index, 1);
+ }
+ }
+ }
+ }
+
+ /** Searching */
+ onKeyDownSearch(params: { searchWord: string }) {
+ this.searchObj = {
+ isShowSearch: true,
+ searchWord: params.searchWord
+ };
+ this.changeDetectorRef.detectChanges();
+ }
+ /** Searching cancel */
+ onClickCancel() {
+ this.searchObj = {
+ isShowSearch: false,
+ searchWord: ''
+ };
+ this.changeDetectorRef.detectChanges();
+ }
+
+ onSearchResultList(searchResultList: RoomInfo[]) {
+ this.searchResultList = searchResultList;
+ }
}
diff --git a/src/app/pages/chat/types/params.type.ts b/src/app/pages/chat/types/params.type.ts
new file mode 100644
index 0000000..7885215
--- /dev/null
+++ b/src/app/pages/chat/types/params.type.ts
@@ -0,0 +1,3 @@
+export enum QueryParams {
+ ROOM_ID = 'roomId'
+}
diff --git a/src/app/pages/group/components/index.page.component.html b/src/app/pages/group/components/index.page.component.html
index 8066cea..8f1cb8e 100644
--- a/src/app/pages/group/components/index.page.component.html
+++ b/src/app/pages/group/components/index.page.component.html
@@ -1,3 +1,12 @@
-
- index of group
+
+
Welcome to M-Messenger
+
diff --git a/src/app/pages/group/components/index.page.component.scss b/src/app/pages/group/components/index.page.component.scss
index e69de29..5d54129 100644
--- a/src/app/pages/group/components/index.page.component.scss
+++ b/src/app/pages/group/components/index.page.component.scss
@@ -0,0 +1,29 @@
+@import '~@ucap/lg-scss/mixins';
+
+.index-container {
+ height: 100%;
+ padding: 0 30px 30px;
+ overflow: auto;
+}
+
+.subtitle {
+ display: flex;
+ align-items: center;
+ height: 60px;
+ text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.16);
+ @include font-family($font-light);
+ font-size: 22px;
+ font-weight: 600;
+ line-height: 2.73;
+ color: $gray-re4;
+}
+
+.profile-container {
+ height: 100%;
+ overflow: auto;
+}
+
+.group-info-container {
+ height: 100%;
+ overflow: auto;
+}
diff --git a/src/app/pages/group/components/index.page.component.ts b/src/app/pages/group/components/index.page.component.ts
index fe6c5e3..c0aa8fa 100644
--- a/src/app/pages/group/components/index.page.component.ts
+++ b/src/app/pages/group/components/index.page.component.ts
@@ -21,10 +21,13 @@ export class IndexPageComponent implements OnInit, OnDestroy {
private logService: LogService
) {}
+ userSeq: string;
+
ngOnInit(): void {
this.paramsSubscription = this.activatedRoute.queryParams.subscribe(
(params: Params) => {
- console.log('IndexPageComponent', params[QueryParams.ID]);
+ const seqParam = params[QueryParams.ID];
+ this.userSeq = !!seqParam ? seqParam : undefined;
}
);
}
diff --git a/src/app/pages/group/components/sidenav.page.component.html b/src/app/pages/group/components/sidenav.page.component.html
index 25f90c1..06f18d9 100644
--- a/src/app/pages/group/components/sidenav.page.component.html
+++ b/src/app/pages/group/components/sidenav.page.component.html
@@ -1,4 +1,63 @@
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/app/pages/group/components/sidenav.page.component.scss b/src/app/pages/group/components/sidenav.page.component.scss
index 76d36e6..5a24e7f 100644
--- a/src/app/pages/group/components/sidenav.page.component.scss
+++ b/src/app/pages/group/components/sidenav.page.component.scss
@@ -1,3 +1,32 @@
+@import '~@ucap/lg-scss/mixins';
+
.sidenav-container {
overflow: hidden;
+ display: flex;
+ flex-flow: column;
+ align-content: flex-start;
+ background-color: #f1f2f6;
+
+ .title-section {
+ display: flex;
+ flex-flow: column;
+ background-color: $white;
+ .title {
+ display: flex;
+ flex-flow: row nowrap;
+ justify-content: space-between;
+ padding: 0 0 0 17px;
+ background-color: $white;
+ align-items: center;
+ width: 100%;
+ h3 {
+ @include font-family-txt(18, left, $lipstick);
+ align-items: center;
+ font-weight: 600;
+ }
+ .menu-btn {
+ justify-self: end;
+ }
+ }
+ }
}
diff --git a/src/app/pages/group/components/sidenav.page.component.ts b/src/app/pages/group/components/sidenav.page.component.ts
index 1d43551..f72840f 100644
--- a/src/app/pages/group/components/sidenav.page.component.ts
+++ b/src/app/pages/group/components/sidenav.page.component.ts
@@ -1,11 +1,27 @@
-import { Subscription } from 'rxjs';
+import { Subscription, of } from 'rxjs';
+import { take, map, catchError } from 'rxjs/operators';
-import { Component, OnInit, OnDestroy } from '@angular/core';
+import {
+ Component,
+ OnInit,
+ OnDestroy,
+ ChangeDetectorRef,
+ ViewChild
+} from '@angular/core';
import { ActivatedRoute, Router, Params } from '@angular/router';
-import { LogService } from '@ucap/ng-logger';
+import { Store } from '@ngrx/store';
-import { QueryParams } from '../types/params.type';
+import { MatDialog } from '@angular/material/dialog';
+
+import { LogService } from '@ucap/ng-logger';
+import { GroupActions } from '@ucap/ng-store-group';
+
+import { SelectUserDialogType } from '@app/types';
+
+import { CreateDialogComponent } from '@app/sections/group/dialogs/create.dialog.component';
+import { I18nService } from '@ucap/ng-i18n';
+import { ListSectionComponent } from '@app/sections/group/components/list.section.component';
@Component({
selector: 'app-pages-group-sidenav',
@@ -13,13 +29,113 @@ import { QueryParams } from '../types/params.type';
styleUrls: ['./sidenav.page.component.scss']
})
export class SidenavPageComponent implements OnInit, OnDestroy {
+ @ViewChild('sectionGroupList', { static: false })
+ sectionGroupList: ListSectionComponent;
+
+ searchObj: any = {
+ isShowSearch: false,
+ companyCode: '',
+ searchWord: ''
+ };
+
+ showType: string;
+
constructor(
private activatedRoute: ActivatedRoute,
private router: Router,
- private logService: LogService
- ) {}
+ private logService: LogService,
+ private i18nService: I18nService,
+ private store: Store
,
+ private changeDetectorRef: ChangeDetectorRef,
+ public dialog: MatDialog
+ ) {
+ this.i18nService.setDefaultNamespace('group');
+ }
- ngOnInit(): void {}
+ ngOnInit(): void {
+ this.showType = 'ALL';
+ }
ngOnDestroy(): void {}
+
+ onClickFab(event: MouseEvent) {}
+ onKeyDownSearch(params: {
+ isShowSearch: boolean;
+ companyCode: string;
+ searchWord: string;
+ }) {
+ this.searchObj = {
+ isShowSearch: params.isShowSearch,
+ companyCode: params.companyCode,
+ searchWord: params.searchWord
+ };
+ this.changeDetectorRef.detectChanges();
+ }
+
+ onClickCancel() {
+ this.searchObj = {
+ isShowSearch: false,
+ companyCode: '',
+ searchWord: ''
+ };
+ this.changeDetectorRef.detectChanges();
+ }
+
+ onClickGroupMenu(menuType: string) {
+ switch (menuType) {
+ case 'GROUP_NEW':
+ {
+ const dialogRef = this.dialog.open(CreateDialogComponent, {
+ width: '100%',
+ height: '100%'
+ });
+
+ dialogRef
+ .afterClosed()
+ .pipe(
+ take(1),
+ map((result) => {}),
+ catchError((err) => {
+ return of(err);
+ })
+ )
+ .subscribe();
+ }
+ break;
+ case 'GROUP_EXPAND_MORE':
+ {
+ this.sectionGroupList.onExpandMore();
+ }
+ break;
+ case 'GROUP_EXPAND_LESS':
+ {
+ this.sectionGroupList.onExpandLess();
+ }
+ break;
+ case 'GROUP_CHANGE_ODER':
+ {
+ }
+ break;
+ }
+ }
+
+ onClickShowGroupMenu(menuType: string) {
+ switch (menuType) {
+ case 'ALL':
+ {
+ this.showType = 'ALL';
+ }
+ break;
+ case 'ONLINE_BUDDY':
+ {
+ this.showType = 'ONLINE_BUDDY';
+ }
+ break;
+ case 'ON_OFF':
+ {
+ this.showType = 'ON_OFF';
+ }
+ break;
+ }
+ }
}
diff --git a/src/app/pages/group/group.page.module.ts b/src/app/pages/group/group.page.module.ts
index 9335a60..0d6ae50 100644
--- a/src/app/pages/group/group.page.module.ts
+++ b/src/app/pages/group/group.page.module.ts
@@ -3,25 +3,45 @@ import { CommonModule } from '@angular/common';
import { FlexLayoutModule } from '@angular/flex-layout';
+import { MatButtonModule } from '@angular/material/button';
+import { MatIconModule } from '@angular/material/icon';
+import { MatMenuModule } from '@angular/material/menu';
+
+import { UiModule } from '@ucap/ng-ui';
+
+import { AppOrganizationModule } from '@app/ucap/organization/organization.module';
+
import { AppGroupSectionModule } from '@app/sections/group/group.section.module';
import { AppGroupRoutingPageModule } from './group-routing.page.module';
-import { IndexPageComponent } from './components/index.page.component';
-import { SidenavPageComponent } from './components/sidenav.page.component';
-
-export const COMPONENTS = [IndexPageComponent, SidenavPageComponent];
-
-export { IndexPageComponent, SidenavPageComponent };
+import { COMPONENTS } from './components';
+import { I18nModule, UCAP_I18N_NAMESPACE } from '@ucap/ng-i18n';
@NgModule({
imports: [
CommonModule,
FlexLayoutModule,
+
+ MatButtonModule,
+ MatIconModule,
+ MatMenuModule,
+
+ AppOrganizationModule,
+
AppGroupSectionModule,
- AppGroupRoutingPageModule
+ AppGroupRoutingPageModule,
+
+ UiModule,
+ I18nModule
],
declarations: [...COMPONENTS],
- entryComponents: []
+ entryComponents: [],
+ providers: [
+ {
+ provide: UCAP_I18N_NAMESPACE,
+ useValue: ['group', 'common']
+ }
+ ]
})
export class AppGroupPageModule {}
diff --git a/src/app/pages/organization/components/index.page.component.html b/src/app/pages/organization/components/index.page.component.html
index 632a969..fd96073 100644
--- a/src/app/pages/organization/components/index.page.component.html
+++ b/src/app/pages/organization/components/index.page.component.html
@@ -1 +1,5 @@
-Index page of organization is works!
+
diff --git a/src/app/pages/organization/components/index.page.component.ts b/src/app/pages/organization/components/index.page.component.ts
index 0b54496..63a1fbf 100644
--- a/src/app/pages/organization/components/index.page.component.ts
+++ b/src/app/pages/organization/components/index.page.component.ts
@@ -1,10 +1,58 @@
-import { Component } from '@angular/core';
+import { Subject } from 'rxjs';
+import { takeUntil } from 'rxjs/operators';
+
+import { Component, OnInit, OnDestroy, ChangeDetectorRef } from '@angular/core';
+import { ActivatedRoute } from '@angular/router';
+
+import { Store } from '@ngrx/store';
+
+import { QueryParams } from '../types/params.type';
@Component({
selector: 'app-pages-organization-index',
templateUrl: './index.page.component.html',
styleUrls: ['./index.page.component.scss']
})
-export class IndexPageComponent {
- constructor() {}
+export class IndexPageComponent implements OnInit, OnDestroy {
+ // tslint:disable-next-line: variable-name
+ _searchData: {
+ companyCode: string;
+ searchWord: string;
+ isSearch: boolean;
+ };
+
+ deptSeq: string;
+
+ private ngOnDestroySubject: Subject;
+
+ constructor(
+ private store: Store,
+ private activatedRoute: ActivatedRoute,
+ private changeDetectorRef: ChangeDetectorRef
+ ) {}
+
+ ngOnInit(): void {
+ this.ngOnDestroySubject = new Subject();
+
+ this.activatedRoute.queryParams
+ .pipe(takeUntil(this.ngOnDestroySubject))
+ .subscribe((params) => {
+ console.log('activatedRoute.queryParams');
+ if (!!params) {
+ const companyCode = params[QueryParams.DEPT_SEQ];
+ console.log('activatedRoute.queryParams', companyCode);
+ this._searchData = {
+ companyCode,
+ searchWord: '',
+ isSearch: false
+ };
+ }
+ });
+ }
+
+ ngOnDestroy(): void {
+ if (!!this.ngOnDestroySubject) {
+ this.ngOnDestroySubject.complete();
+ }
+ }
}
diff --git a/src/app/pages/organization/components/sidenav.page.component.html b/src/app/pages/organization/components/sidenav.page.component.html
index 00e135e..abde0bc 100644
--- a/src/app/pages/organization/components/sidenav.page.component.html
+++ b/src/app/pages/organization/components/sidenav.page.component.html
@@ -1,3 +1,33 @@
-
- sidenav page of ogranization is works!
+
+
+
+
+
+
diff --git a/src/app/pages/organization/components/sidenav.page.component.scss b/src/app/pages/organization/components/sidenav.page.component.scss
index e69de29..e88751d 100644
--- a/src/app/pages/organization/components/sidenav.page.component.scss
+++ b/src/app/pages/organization/components/sidenav.page.component.scss
@@ -0,0 +1,33 @@
+@import '~@ucap/lg-scss/mixins';
+
+.sidenav-container {
+ overflow: hidden;
+
+ .sub-header {
+ justify-content: space-between;
+ padding: 0 0 0 17px;
+ background-color: $white;
+ align-items: center;
+ width: 100%;
+ h3 {
+ @include font-family-txt(20, left, $lipstick);
+ align-items: center;
+ font-weight: 600;
+ }
+ .menu-btn {
+ justify-self: end;
+ }
+ }
+ .extra-box {
+ align-items: center;
+ font-weight: 600;
+ font-size: 16px;
+ color: $gray-re3;
+ padding: 0 17px;
+ line-height: 50px;
+ .ico-business {
+ vertical-align: middle;
+ margin-right: 10px;
+ }
+ }
+}
diff --git a/src/app/pages/organization/components/sidenav.page.component.ts b/src/app/pages/organization/components/sidenav.page.component.ts
index 0a5ba39..e487348 100644
--- a/src/app/pages/organization/components/sidenav.page.component.ts
+++ b/src/app/pages/organization/components/sidenav.page.component.ts
@@ -1,14 +1,68 @@
-import { Component } from '@angular/core';
+import { Subject } from 'rxjs';
+import { takeUntil } from 'rxjs/operators';
+
+import { Component, OnInit, OnDestroy, ChangeDetectorRef } from '@angular/core';
+import { Router, ActivatedRoute, Params } from '@angular/router';
import { LogService } from '@ucap/ng-logger';
+import { DeptInfo } from '@ucap/protocol-query';
+
+import { QueryParams } from '../types/params.type';
@Component({
selector: 'app-pages-ogranization-sidenav',
templateUrl: './sidenav.page.component.html',
styleUrls: ['./sidenav.page.component.scss']
})
-export class SidenavPageComponent {
- constructor(private logService: LogService) {
+export class SidenavPageComponent implements OnInit, OnDestroy {
+ initialExpanded: number;
+
+ private ngOnDestroySubject: Subject
;
+
+ constructor(
+ private router: Router,
+ private activatedRoute: ActivatedRoute,
+ private changeDetectorRef: ChangeDetectorRef,
+ private logService: LogService
+ ) {
this.logService.info('app-pages-ogranization-sidenav');
}
+
+ ngOnInit(): void {
+ this.ngOnDestroySubject = new Subject();
+
+ this.activatedRoute.queryParams
+ .pipe(takeUntil(this.ngOnDestroySubject))
+ .subscribe((params) => {
+ if (!!params) {
+ const deptSeq = params[QueryParams.DEPT_SEQ];
+ if (!!deptSeq) {
+ this.initialExpanded = Number(deptSeq);
+ }
+ }
+ });
+ }
+
+ ngOnDestroy(): void {
+ if (!!this.ngOnDestroySubject) {
+ this.ngOnDestroySubject.complete();
+ }
+ }
+
+ onClickedTree(node: DeptInfo) {
+ const queryParams: Params = {};
+ queryParams[QueryParams.DEPT_SEQ] = String(node.seq);
+
+ this.router.navigate(
+ [
+ 'organization',
+ {
+ outlets: { content: 'index' }
+ }
+ ],
+ {
+ queryParams
+ }
+ );
+ }
}
diff --git a/src/app/pages/organization/organization.page.module.ts b/src/app/pages/organization/organization.page.module.ts
index dfad3dd..fc0ac19 100644
--- a/src/app/pages/organization/organization.page.module.ts
+++ b/src/app/pages/organization/organization.page.module.ts
@@ -3,12 +3,29 @@ import { CommonModule } from '@angular/common';
import { FlexLayoutModule } from '@angular/flex-layout';
+import { MatButtonModule } from '@angular/material/button';
+import { MatIconModule } from '@angular/material/icon';
+import { MatMenuModule } from '@angular/material/menu';
+
+import { AppOrganizationModule } from '@app/ucap/organization/organization.module';
+import { AppOrganizationSectionModule } from '@app/sections/organization/organization.section.module';
import { AppOrganizationRoutingPageModule } from './organization-routing.page.module';
import { COMPONENTS } from './components';
@NgModule({
- imports: [CommonModule, FlexLayoutModule, AppOrganizationRoutingPageModule],
+ imports: [
+ CommonModule,
+ FlexLayoutModule,
+
+ MatButtonModule,
+ MatIconModule,
+ MatMenuModule,
+
+ AppOrganizationModule,
+ AppOrganizationSectionModule,
+ AppOrganizationRoutingPageModule
+ ],
declarations: [...COMPONENTS],
entryComponents: []
})
diff --git a/src/app/pages/organization/types/params.type.ts b/src/app/pages/organization/types/params.type.ts
new file mode 100644
index 0000000..dc41404
--- /dev/null
+++ b/src/app/pages/organization/types/params.type.ts
@@ -0,0 +1,3 @@
+export enum QueryParams {
+ DEPT_SEQ = 'dept_seq'
+}
diff --git a/src/app/sections/account/account.section.module.ts b/src/app/sections/account/account.section.module.ts
index 2c1a83f..e4339cc 100644
--- a/src/app/sections/account/account.section.module.ts
+++ b/src/app/sections/account/account.section.module.ts
@@ -1,20 +1,19 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
+import { ReactiveFormsModule } from '@angular/forms';
import { FlexLayoutModule } from '@angular/flex-layout';
-import { MatCheckboxModule } from '@angular/material/checkbox';
-
import { I18nModule, UCAP_I18N_NAMESPACE } from '@ucap/ng-i18n';
-
import { AuthenticationUiModule } from '@ucap/ng-ui-authentication';
-import { COMPONENTS } from './components';
import { MatButtonModule } from '@angular/material/button';
import { MatButtonToggleModule } from '@angular/material/button-toggle';
import { MatCardModule } from '@angular/material/card';
+import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatDatepickerModule } from '@angular/material/datepicker';
import { MatDialogModule } from '@angular/material/dialog';
+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';
@@ -26,8 +25,8 @@ import { MatSliderModule } from '@angular/material/slider';
import { MatTabsModule } from '@angular/material/tabs';
import { MatTooltipModule } from '@angular/material/tooltip';
import { MatToolbarModule } from '@angular/material/toolbar';
-import { MatFormFieldModule } from '@angular/material/form-field';
-import { ReactiveFormsModule } from '@angular/forms';
+
+import { COMPONENTS } from './components';
@NgModule({
imports: [
diff --git a/src/app/sections/account/components/component-ui/login.component.html b/src/app/sections/account/components/component-ui/login.component.html
index 5b4a42d..0a5d579 100644
--- a/src/app/sections/account/components/component-ui/login.component.html
+++ b/src/app/sections/account/components/component-ui/login.component.html
@@ -11,6 +11,7 @@
-
-
-
-
-
Welcome to Messenger
-
-
-
-
-
-
- {{ 'login.labels.rememberMe' | ucapI18n }}
-
+
+
+
+
+
+
Welcome to Messenger
+
-
-
- {{ 'login.labels.autoLogin' | ucapI18n }}
-
+
+
+
+
+ {{ 'login.labels.rememberMe' | ucapI18n }}
+
+
+
+
+
+ {{ 'login.labels.autoLogin' | ucapI18n }}
+
+
+
+
+
+
-
-
-
-
-
-
+
+
diff --git a/src/app/sections/account/components/login.section.component.scss b/src/app/sections/account/components/login.section.component.scss
index d58e261..a35480b 100644
--- a/src/app/sections/account/components/login.section.component.scss
+++ b/src/app/sections/account/components/login.section.component.scss
@@ -15,6 +15,12 @@ h1 {
}
}
+.login-section-container {
+ width: 100%;
+ height: 100%;
+ overflow: auto;
+}
+
.login-chk-area {
margin-top: 6px;
font-size: 13px;
diff --git a/src/app/sections/chat/chat.section.module.ts b/src/app/sections/chat/chat.section.module.ts
new file mode 100644
index 0000000..b3e1a0a
--- /dev/null
+++ b/src/app/sections/chat/chat.section.module.ts
@@ -0,0 +1,73 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { ReactiveFormsModule } from '@angular/forms';
+
+import { FlexLayoutModule } from '@angular/flex-layout';
+
+import { ScrollingModule } from '@angular/cdk/scrolling';
+
+import { MatRippleModule } from '@angular/material/core';
+import { MatAutocompleteModule } from '@angular/material/autocomplete';
+import { MatBadgeModule } from '@angular/material/badge';
+import { MatButtonModule } from '@angular/material/button';
+import { MatCardModule } from '@angular/material/card';
+import { MatCheckboxModule } from '@angular/material/checkbox';
+import { MatFormFieldModule } from '@angular/material/form-field';
+import { MatInputModule } from '@angular/material/input';
+import { MatIconModule } from '@angular/material/icon';
+import { MatMenuModule } from '@angular/material/menu';
+import { MatSelectModule } from '@angular/material/select';
+import { MatTreeModule } from '@angular/material/tree';
+import { MatToolbarModule } from '@angular/material/toolbar';
+import { MatTooltipModule } from '@angular/material/tooltip';
+
+import { PerfectScrollbarModule } from 'ngx-perfect-scrollbar';
+
+import { I18nModule, UCAP_I18N_NAMESPACE } from '@ucap/ng-i18n';
+import { UiModule } from '@ucap/ng-ui';
+import { ChatUiModule } from '@ucap/ng-ui-chat';
+import { AppChatModule } from '@app/ucap/chat/chat.module';
+
+import { COMPONENTS } from './components';
+
+@NgModule({
+ imports: [
+ CommonModule,
+ ReactiveFormsModule,
+ FlexLayoutModule,
+
+ MatInputModule,
+ MatFormFieldModule,
+ MatIconModule,
+ MatCardModule,
+ MatCheckboxModule,
+ MatAutocompleteModule,
+ MatSelectModule,
+ MatBadgeModule,
+ MatMenuModule,
+ MatToolbarModule,
+ MatButtonModule,
+ MatRippleModule,
+ MatTreeModule,
+ MatTooltipModule,
+
+ PerfectScrollbarModule,
+ ScrollingModule,
+
+ I18nModule,
+ UiModule,
+
+ ChatUiModule,
+ AppChatModule
+ ],
+ exports: [...COMPONENTS],
+ declarations: [...COMPONENTS],
+ entryComponents: [],
+ providers: [
+ {
+ provide: UCAP_I18N_NAMESPACE,
+ useValue: ['chat', 'common']
+ }
+ ]
+})
+export class AppChatSectionModule {}
diff --git a/src/app/sections/chat/components/chat-search.section.component.html b/src/app/sections/chat/components/chat-search.section.component.html
new file mode 100644
index 0000000..02f1d49
--- /dev/null
+++ b/src/app/sections/chat/components/chat-search.section.component.html
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
N건의 검색결과가 있습니다.
+
1/N
+
+
+
+
+
+
diff --git a/src/app/sections/chat/components/chat-search.section.component.scss b/src/app/sections/chat/components/chat-search.section.component.scss
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/sections/chat/components/chat-search.section.component.spec.ts b/src/app/sections/chat/components/chat-search.section.component.spec.ts
new file mode 100644
index 0000000..e45a1ca
--- /dev/null
+++ b/src/app/sections/chat/components/chat-search.section.component.spec.ts
@@ -0,0 +1,32 @@
+import { TestBed, async } from '@angular/core/testing';
+import { RouterTestingModule } from '@angular/router/testing';
+import { SearchSectionComponent } from './search.section.component';
+
+describe('app::sections::group::SearchSectionComponent', () => {
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ imports: [RouterTestingModule],
+ declarations: [SearchSectionComponent]
+ }).compileComponents();
+ }));
+
+ it('should create the app', () => {
+ const fixture = TestBed.createComponent(SearchSectionComponent);
+ const app = fixture.componentInstance;
+ expect(app).toBeTruthy();
+ });
+
+ it(`should have as title 'ucap-lg-web'`, () => {
+ const fixture = TestBed.createComponent(SearchSectionComponent);
+ const app = fixture.componentInstance;
+ });
+
+ it('should render title', () => {
+ const fixture = TestBed.createComponent(SearchSectionComponent);
+ fixture.detectChanges();
+ const compiled = fixture.nativeElement;
+ expect(compiled.querySelector('.content span').textContent).toContain(
+ 'ucap-lg-web app is running!'
+ );
+ });
+});
diff --git a/src/app/sections/chat/components/chat-search.section.component.ts b/src/app/sections/chat/components/chat-search.section.component.ts
new file mode 100644
index 0000000..cf2052e
--- /dev/null
+++ b/src/app/sections/chat/components/chat-search.section.component.ts
@@ -0,0 +1,51 @@
+import {
+ Component,
+ OnInit,
+ OnDestroy,
+ Output,
+ EventEmitter,
+ ChangeDetectorRef,
+ ChangeDetectionStrategy
+} from '@angular/core';
+
+@Component({
+ selector: 'app-sections-chat-chat-search',
+ templateUrl: './chat-search.section.component.html',
+ styleUrls: ['./chat-search.section.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class ChatSearchSectionComponent implements OnInit, OnDestroy {
+ searchObj: any = {
+ isSearch: false,
+ searchWord: ''
+ };
+
+ @Output()
+ chatSearch = new EventEmitter<{
+ isSearch: false;
+ searchWord: '';
+ }>();
+
+ @Output()
+ closeChatSearch = new EventEmitter
();
+
+ constructor(private changeDetectorRef: ChangeDetectorRef) {}
+
+ ngOnInit(): void {}
+
+ ngOnDestroy(): void {}
+
+ onChatSearch(searchWord: string) {
+ alert(searchWord);
+
+ this.searchObj = {
+ isSearch: true,
+ searchWord
+ };
+ this.chatSearch.emit(this.searchObj);
+ }
+
+ onCancel(): void {
+ this.closeChatSearch.emit();
+ }
+}
diff --git a/src/app/sections/chat/components/component-ui/chat-list-item.component.html b/src/app/sections/chat/components/component-ui/chat-list-item.component.html
new file mode 100644
index 0000000..12b95ab
--- /dev/null
+++ b/src/app/sections/chat/components/component-ui/chat-list-item.component.html
@@ -0,0 +1,64 @@
+
+
+
+
+
+ {{ roomName }}
+ ({{ roomInfo.joinUserCount }})
+
+
{{ roomInfo.finalEventMessage }}
+
{{ roomInfo.finalEventDate | ucapDate: 'LT' }}
+
+
0"
+ [matBadgeHidden]="roomInfo.noReadCnt === 0"
+ [matBadge]="roomInfo.noReadCnt"
+ matBadgeOverlap="true"
+ matBadgeColor="accent"
+ matBadgePosition="below after"
+ >
+
+
+
+
+
+
+
+ {{ roomName }}
+
+
+
+
diff --git a/src/app/sections/chat/components/component-ui/chat-list-item.component.scss b/src/app/sections/chat/components/component-ui/chat-list-item.component.scss
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/sections/chat/components/component-ui/chat-list-item.component.spec.ts b/src/app/sections/chat/components/component-ui/chat-list-item.component.spec.ts
new file mode 100644
index 0000000..4eeee62
--- /dev/null
+++ b/src/app/sections/chat/components/component-ui/chat-list-item.component.spec.ts
@@ -0,0 +1,25 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { ChatListItemComponent } from './chat-list-item.component';
+
+describe('ChatListItemComponent', () => {
+ let component: ChatListItemComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [ ChatListItemComponent ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(ChatListItemComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/sections/chat/components/component-ui/chat-list-item.component.ts b/src/app/sections/chat/components/component-ui/chat-list-item.component.ts
new file mode 100644
index 0000000..ce68555
--- /dev/null
+++ b/src/app/sections/chat/components/component-ui/chat-list-item.component.ts
@@ -0,0 +1,63 @@
+import {
+ Component,
+ OnInit,
+ ChangeDetectionStrategy,
+ Input,
+ OnDestroy,
+ EventEmitter,
+ Output,
+ ChangeDetectorRef
+} from '@angular/core';
+import { RoomInfo, RoomType } from '@ucap/protocol-room';
+
+@Component({
+ selector: 'app-chat-list-item',
+ templateUrl: './chat-list-item.component.html',
+ styleUrls: ['./chat-list-item.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class ChatListItemComponent implements OnInit, OnDestroy {
+ @Input()
+ roomInfo: RoomInfo;
+
+ @Input()
+ profileImageRoot: string;
+
+ @Input()
+ defaultProfileImage: string;
+
+ @Input()
+ profileImage: string;
+
+ @Input()
+ roomName: string;
+
+ @Input()
+ checkable = false;
+
+ @Input()
+ checked = false;
+
+ @Output()
+ toggleItem = new EventEmitter<{
+ checked: boolean;
+ roomInfo: RoomInfo;
+ }>();
+
+ RoomType = RoomType;
+
+ constructor(private changeDetectorRef: ChangeDetectorRef) {}
+
+ ngOnInit(): void {}
+
+ ngOnDestroy(): void {}
+
+ onToggleItem(value: boolean): void {
+ this.toggleItem.emit({
+ checked: value,
+ roomInfo: this.roomInfo
+ });
+
+ this.changeDetectorRef.detectChanges();
+ }
+}
diff --git a/src/app/sections/chat/components/component-ui/expansion.component.html b/src/app/sections/chat/components/component-ui/expansion.component.html
new file mode 100644
index 0000000..254d78c
--- /dev/null
+++ b/src/app/sections/chat/components/component-ui/expansion.component.html
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/app/sections/chat/components/component-ui/expansion.component.scss b/src/app/sections/chat/components/component-ui/expansion.component.scss
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/sections/chat/components/component-ui/expansion.component.spec.ts b/src/app/sections/chat/components/component-ui/expansion.component.spec.ts
new file mode 100644
index 0000000..31dc1f4
--- /dev/null
+++ b/src/app/sections/chat/components/component-ui/expansion.component.spec.ts
@@ -0,0 +1,26 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { By } from '@angular/platform-browser';
+import { DebugElement } from '@angular/core';
+
+import { ExpansionComponent } from './expansion.component';
+
+describe('ucap::ui-group::ExpansionComponent', () => {
+ let component: ExpansionComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [ExpansionComponent]
+ }).compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(ExpansionComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/sections/chat/components/component-ui/expansion.component.ts b/src/app/sections/chat/components/component-ui/expansion.component.ts
new file mode 100644
index 0000000..8db4212
--- /dev/null
+++ b/src/app/sections/chat/components/component-ui/expansion.component.ts
@@ -0,0 +1,177 @@
+import {
+ Component,
+ OnInit,
+ OnDestroy,
+ Input,
+ ViewChild,
+ ContentChild,
+ TemplateRef,
+ ChangeDetectionStrategy,
+ ChangeDetectorRef,
+ Directive
+} from '@angular/core';
+
+import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
+import { FlatTreeControl } from '@angular/cdk/tree';
+
+import { MatTreeFlattener, MatTree } from '@angular/material/tree';
+
+import { VirtualScrollTreeFlatDataSource } from '@ucap/ng-ui';
+import { Subject } from 'rxjs';
+import { takeUntil } from 'rxjs/operators';
+import { PerfectScrollbarDirective } from 'ngx-perfect-scrollbar';
+import { RoomInfo } from '@ucap/protocol-room';
+
+export interface ChatGroupNode {
+ nodeType: string;
+ roomInfo?: RoomInfo;
+ children?: ChatGroupNode[];
+}
+
+export interface FlatNode {
+ expandable: boolean;
+ level: number;
+ node: ChatGroupNode;
+}
+
+@Directive({
+ selector: '[ucapChatExpansionNode]'
+})
+export class ExpansionNodeDirective {}
+
+@Directive({
+ selector: '[ucapChatExpansionHeader]'
+})
+export class ExpansionHeaderDirective {}
+
+@Component({
+ selector: 'ucap-chat-expansion',
+ templateUrl: './expansion.component.html',
+ styleUrls: ['./expansion.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class ExpansionComponent implements OnInit, OnDestroy {
+ @Input()
+ set chatGroup(list: { division: string; roomList: RoomInfo[] }[]) {
+ if (!list || 0 === list.length) {
+ } else {
+ list.sort((a, b) =>
+ a.division < b.division ? 1 : a.division > b.division ? -1 : 0
+ );
+
+ for (const item of list) {
+ const nodeType = item.division;
+ const node: ChatGroupNode = {
+ nodeType,
+ children: []
+ };
+
+ item.roomList.sort((a, b) =>
+ a.finalEventDate < b.finalEventDate
+ ? 1
+ : a.finalEventDate > b.finalEventDate
+ ? -1
+ : 0
+ );
+
+ item.roomList.forEach((roomInfo) => {
+ node.children.push({
+ nodeType,
+ roomInfo
+ });
+ });
+
+ if (!!this.nodeMap.get(item.division)) {
+ this.nodeMap[item.division].push(node);
+ } else {
+ this.nodeMap.set(item.division, [node]);
+ }
+ }
+ }
+ this.refreshNodes();
+ }
+
+ @ViewChild('treeList', { static: false })
+ treeList: MatTree;
+
+ @ViewChild('cvsvList', { static: false })
+ cvsvList: CdkVirtualScrollViewport;
+
+ @ViewChild(PerfectScrollbarDirective, { static: false })
+ psDirectiveRef?: PerfectScrollbarDirective;
+
+ @ContentChild(ExpansionNodeDirective, {
+ read: TemplateRef,
+ static: false
+ })
+ nodeTemplate: TemplateRef;
+
+ @ContentChild(ExpansionHeaderDirective, {
+ read: TemplateRef,
+ static: false
+ })
+ headerTemplate: TemplateRef;
+
+ treeControl: FlatTreeControl;
+ treeFlattener: MatTreeFlattener;
+ dataSource: VirtualScrollTreeFlatDataSource;
+
+ private nodeMap: Map = new Map();
+ // tslint:disable-next-line: variable-name
+ private _ngOnDestroySubject: Subject;
+
+ constructor(private changeDetectorRef: ChangeDetectorRef) {
+ this.treeControl = new FlatTreeControl(
+ (node) => node.level,
+ (node) => node.expandable
+ );
+
+ this.treeFlattener = new MatTreeFlattener(
+ (node: ChatGroupNode, level: number) => {
+ return {
+ expandable: !!node.children && node.children.length > 0,
+ level,
+ nodeType: node.nodeType,
+ node
+ };
+ },
+ (node) => node.level,
+ (node) => node.expandable,
+ (node) => node.children
+ );
+
+ this.dataSource = new VirtualScrollTreeFlatDataSource<
+ ChatGroupNode,
+ FlatNode
+ >(this.treeControl, this.treeFlattener);
+ }
+
+ ngOnInit(): void {
+ this._ngOnDestroySubject = new Subject();
+
+ this.dataSource.cdkVirtualScrollViewport = this.cvsvList;
+ this.treeControl.expansionModel.changed
+ .pipe(takeUntil(this._ngOnDestroySubject))
+ .subscribe(() => {
+ this.cvsvList.checkViewportSize();
+ this.psDirectiveRef.update();
+ });
+ }
+
+ ngOnDestroy(): void {
+ if (!!this._ngOnDestroySubject) {
+ this._ngOnDestroySubject.next();
+ this._ngOnDestroySubject.complete();
+ }
+ }
+
+ isHeader = (_: number, node: FlatNode) => 0 === node.level;
+
+ private refreshNodes() {
+ const rootNode: ChatGroupNode[] = [];
+ this.nodeMap.forEach((node) => rootNode.push(...node));
+
+ this.dataSource.data = rootNode;
+ this.changeDetectorRef.detectChanges();
+ }
+}
diff --git a/src/app/sections/chat/components/component-ui/index.ts b/src/app/sections/chat/components/component-ui/index.ts
new file mode 100644
index 0000000..80b2d82
--- /dev/null
+++ b/src/app/sections/chat/components/component-ui/index.ts
@@ -0,0 +1,10 @@
+import { ChatListItemComponent } from './chat-list-item.component';
+import {
+ ExpansionComponent,
+ ExpansionNodeDirective,
+ ExpansionHeaderDirective
+} from './expansion.component';
+
+export const COMPONENTS = [ChatListItemComponent, ExpansionComponent];
+
+export const DIRECTIVES = [ExpansionNodeDirective, ExpansionHeaderDirective];
diff --git a/src/app/sections/chat/components/form.section.component.html b/src/app/sections/chat/components/form.section.component.html
new file mode 100644
index 0000000..3d6ec46
--- /dev/null
+++ b/src/app/sections/chat/components/form.section.component.html
@@ -0,0 +1,108 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/app/sections/chat/components/form.section.component.scss b/src/app/sections/chat/components/form.section.component.scss
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/sections/chat/components/form.section.component.spec.ts b/src/app/sections/chat/components/form.section.component.spec.ts
new file mode 100644
index 0000000..b9cd884
--- /dev/null
+++ b/src/app/sections/chat/components/form.section.component.spec.ts
@@ -0,0 +1,32 @@
+import { TestBed, async } from '@angular/core/testing';
+import { RouterTestingModule } from '@angular/router/testing';
+import { FormSectionComponent } from './form.section.component';
+
+describe('app::sections::group::FormSectionComponent', () => {
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ imports: [RouterTestingModule],
+ declarations: [FormSectionComponent]
+ }).compileComponents();
+ }));
+
+ it('should create the app', () => {
+ const fixture = TestBed.createComponent(FormSectionComponent);
+ const app = fixture.componentInstance;
+ expect(app).toBeTruthy();
+ });
+
+ it(`should have as title 'ucap-lg-web'`, () => {
+ const fixture = TestBed.createComponent(FormSectionComponent);
+ const app = fixture.componentInstance;
+ });
+
+ it('should render title', () => {
+ const fixture = TestBed.createComponent(FormSectionComponent);
+ fixture.detectChanges();
+ const compiled = fixture.nativeElement;
+ expect(compiled.querySelector('.content span').textContent).toContain(
+ 'ucap-lg-web app is running!'
+ );
+ });
+});
diff --git a/src/app/sections/chat/components/form.section.component.ts b/src/app/sections/chat/components/form.section.component.ts
new file mode 100644
index 0000000..da88483
--- /dev/null
+++ b/src/app/sections/chat/components/form.section.component.ts
@@ -0,0 +1,71 @@
+import { Subject } from 'rxjs';
+import { takeUntil } from 'rxjs/operators';
+
+import {
+ Component,
+ OnInit,
+ OnDestroy,
+ ChangeDetectionStrategy,
+ ChangeDetectorRef,
+ Input
+} from '@angular/core';
+import { Store, select } from '@ngrx/store';
+import { RoomInfo } from '@ucap/protocol-room';
+import { Dictionary } from '@ngrx/entity';
+
+@Component({
+ selector: 'app-sections-chat-form',
+ templateUrl: './form.section.component.html',
+ styleUrls: ['./form.section.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class FormSectionComponent implements OnInit, OnDestroy {
+ @Input()
+ set roomId(roomId: string) {
+ this._roomId = roomId;
+ }
+ get roomId(): string {
+ return this._roomId;
+ }
+ // tslint:disable-next-line: variable-name
+ _roomId: string;
+
+ currentRoomInfo: RoomInfo;
+
+ selectorType = '';
+
+ private ngOnDestroySubject: Subject;
+ constructor(
+ private store: Store,
+ private changeDetectorRef: ChangeDetectorRef
+ ) {}
+
+ ngOnInit(): void {
+ this.ngOnDestroySubject = new Subject();
+
+ this.store
+ .pipe(
+ takeUntil(this.ngOnDestroySubject),
+ select(
+ (state: any) => state.chat.room.rooms.entities as Dictionary
+ )
+ )
+ .subscribe((rooms) => {
+ if (!!this.roomId) {
+ this.currentRoomInfo = rooms[this.roomId];
+
+ this.changeDetectorRef.detectChanges();
+ }
+ });
+ }
+
+ ngOnDestroy(): void {
+ if (!!this.ngOnDestroySubject) {
+ this.ngOnDestroySubject.complete();
+ }
+ }
+
+ onOpenSelector(type: string): void {
+ this.selectorType = type;
+ }
+}
diff --git a/src/app/sections/chat/components/index.ts b/src/app/sections/chat/components/index.ts
new file mode 100644
index 0000000..2aaf9a8
--- /dev/null
+++ b/src/app/sections/chat/components/index.ts
@@ -0,0 +1,15 @@
+import { SearchSectionComponent } from './search.section.component';
+import { ListSectionComponent } from './list.section.component';
+import { InfoSectionComponent } from './info.section.component';
+import { ChatSearchSectionComponent } from './chat-search.section.component';
+import { FormSectionComponent } from './form.section.component';
+import { MessageSectionComponent } from './message.section.component';
+
+export const COMPONENTS = [
+ SearchSectionComponent,
+ ListSectionComponent,
+ InfoSectionComponent,
+ ChatSearchSectionComponent,
+ FormSectionComponent,
+ MessageSectionComponent
+];
diff --git a/src/app/sections/chat/components/info.section.component.html b/src/app/sections/chat/components/info.section.component.html
new file mode 100644
index 0000000..28ffc53
--- /dev/null
+++ b/src/app/sections/chat/components/info.section.component.html
@@ -0,0 +1,74 @@
+
+
+
+
+
+ {{ roomName }}
+
+ ({{ currentRoomInfo?.joinUserCount }})
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/app/sections/chat/components/info.section.component.scss b/src/app/sections/chat/components/info.section.component.scss
new file mode 100644
index 0000000..98fcdb2
--- /dev/null
+++ b/src/app/sections/chat/components/info.section.component.scss
@@ -0,0 +1,15 @@
+@import '~@ucap/lg-scss/mixins';
+
+.info-chat-toolbar {
+ height: 50px;
+ min-height: 50px;
+ background-color: $white;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-content: center;
+ .mat-toolbar-row {
+ height: 50px;
+ min-height: 50px;
+ }
+}
diff --git a/src/app/sections/chat/components/info.section.component.spec.ts b/src/app/sections/chat/components/info.section.component.spec.ts
new file mode 100644
index 0000000..9fe87da
--- /dev/null
+++ b/src/app/sections/chat/components/info.section.component.spec.ts
@@ -0,0 +1,32 @@
+import { TestBed, async } from '@angular/core/testing';
+import { RouterTestingModule } from '@angular/router/testing';
+import { InfoSectionComponent } from './info.section.component';
+
+describe('app::sections::group::InfoSectionComponent', () => {
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ imports: [RouterTestingModule],
+ declarations: [InfoSectionComponent]
+ }).compileComponents();
+ }));
+
+ it('should create the app', () => {
+ const fixture = TestBed.createComponent(InfoSectionComponent);
+ const app = fixture.componentInstance;
+ expect(app).toBeTruthy();
+ });
+
+ it(`should have as title 'ucap-lg-web'`, () => {
+ const fixture = TestBed.createComponent(InfoSectionComponent);
+ const app = fixture.componentInstance;
+ });
+
+ it('should render title', () => {
+ const fixture = TestBed.createComponent(InfoSectionComponent);
+ fixture.detectChanges();
+ const compiled = fixture.nativeElement;
+ expect(compiled.querySelector('.content span').textContent).toContain(
+ 'ucap-lg-web app is running!'
+ );
+ });
+});
diff --git a/src/app/sections/chat/components/info.section.component.ts b/src/app/sections/chat/components/info.section.component.ts
new file mode 100644
index 0000000..566b122
--- /dev/null
+++ b/src/app/sections/chat/components/info.section.component.ts
@@ -0,0 +1,224 @@
+import {
+ Component,
+ OnInit,
+ OnDestroy,
+ ChangeDetectionStrategy,
+ Input,
+ Output,
+ EventEmitter,
+ ChangeDetectorRef
+} from '@angular/core';
+import { Dictionary } from '@ngrx/entity';
+import { Store, select } from '@ngrx/store';
+import { combineLatest, Subject } from 'rxjs';
+import { takeUntil } from 'rxjs/operators';
+
+import { RoomSelector, RoomActions } from '@ucap/ng-store-chat';
+import {
+ LoginSelector,
+ ConfigurationSelector
+} from '@ucap/ng-store-authentication';
+import {
+ RoomUserMap,
+ RoomUserShortMap
+} from '@ucap/ng-store-chat/lib/store/room/state';
+import { LoginResponse } from '@ucap/protocol-authentication';
+import { RoomInfo, RoomType, UpdateRequest } from '@ucap/protocol-room';
+
+import {
+ TranslatePipe as OrganizationTranslate,
+ TranslateService
+} from '@ucap/ng-ui-organization';
+
+import { AppChatService } from '@app/services/app-chat.service';
+import { I18nService } from '@ucap/ng-i18n';
+import { VersionInfo2Response } from '@ucap/api-public';
+import { LocaleCode } from '@ucap/core';
+
+@Component({
+ selector: 'app-sections-chat-info',
+ templateUrl: './info.section.component.html',
+ styleUrls: ['./info.section.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class InfoSectionComponent implements OnInit, OnDestroy {
+ @Input()
+ set roomId(roomId: string) {
+ this._roomId = roomId;
+
+ // request selected room
+ if (!!this.roomId) {
+ this.store.dispatch(
+ RoomActions.selectedRoom({
+ roomId: this.roomId,
+ localeCode: LocaleCode.Korean
+ })
+ );
+ }
+
+ this.getRoomInfo();
+ }
+ get roomId(): string {
+ return this._roomId;
+ }
+ // tslint:disable-next-line: variable-name
+ _roomId: string;
+
+ @Output()
+ openChatSearch = new EventEmitter();
+
+ @Output()
+ rightDrawerToggle = new EventEmitter();
+
+ versionInfo2Res: VersionInfo2Response;
+ loginRes: LoginResponse;
+
+ defaultProfileImage: string;
+ defaultProfileImageMulti: string;
+
+ currentRoomInfo: RoomInfo;
+ roomList: RoomInfo[];
+ roomUsersDictionary: Dictionary;
+ roomUsersShortDictionary: Dictionary;
+
+ roomName: string;
+ roomImage: string;
+
+ RoomType = RoomType;
+ organizationTranslate: OrganizationTranslate;
+ private ngOnDestroySubject: Subject;
+
+ constructor(
+ private store: Store,
+ private appChatService: AppChatService,
+ private i18nService: I18nService,
+ private translateService: TranslateService,
+ private changeDetectorRef: ChangeDetectorRef
+ ) {
+ // language setting
+ this.translateService.setDefaultLang(this.i18nService.currentLng);
+ this.translateService.use(this.i18nService.currentLng);
+ this.organizationTranslate = new OrganizationTranslate(
+ this.translateService,
+ this.changeDetectorRef
+ );
+
+ // default image setting
+ this.defaultProfileImage = this.appChatService.defaultProfileImage;
+ this.defaultProfileImageMulti = this.appChatService.defaultProfileImage;
+ }
+
+ ngOnInit(): void {
+ this.ngOnDestroySubject = new Subject();
+
+ this.store
+ .pipe(
+ takeUntil(this.ngOnDestroySubject),
+ select(ConfigurationSelector.versionInfo2Response)
+ )
+ .subscribe((versionInfo2Res) => {
+ this.versionInfo2Res = versionInfo2Res;
+ });
+
+ combineLatest([
+ this.store.pipe(select(LoginSelector.loginRes)),
+ this.store.pipe(select(RoomSelector.rooms)),
+ this.store.pipe(
+ select(
+ (state: any) =>
+ state.chat.room.roomUsers.entities as Dictionary
+ )
+ ),
+ this.store.pipe(
+ select(
+ (state: any) =>
+ state.chat.room.roomUsersShort.entities as Dictionary<
+ RoomUserShortMap
+ >
+ )
+ )
+ ])
+ .pipe(takeUntil(this.ngOnDestroySubject))
+ .subscribe(([loginRes, rooms, roomUsers, roomUsersShort]) => {
+ this.loginRes = loginRes;
+ if (!!loginRes && !!this.roomId) {
+ this.roomList = rooms;
+ this.roomUsersDictionary = roomUsers;
+ this.roomUsersShortDictionary = roomUsersShort;
+
+ this.getRoomInfo();
+ }
+ });
+ }
+
+ ngOnDestroy(): void {
+ if (!!this.ngOnDestroySubject) {
+ this.ngOnDestroySubject.complete();
+ }
+ }
+
+ getRoomInfo(): void {
+ // room render.
+ if (!this.roomList || this.roomList.length === 0) {
+ return;
+ }
+
+ this.roomName = '...';
+ const idx = this.roomList.findIndex(
+ (roomInfo) => roomInfo.roomId === this.roomId
+ );
+ if (idx > -1 && !!this.roomList && this.roomList.length > 0) {
+ this.currentRoomInfo = this.roomList[idx];
+ }
+
+ if (!!this.currentRoomInfo) {
+ // Setting RoomName.
+ this.roomName = this.appChatService.getRoomName(
+ this.organizationTranslate,
+ this.loginRes,
+ this.currentRoomInfo,
+ this.roomUsersDictionary,
+ this.roomUsersShortDictionary
+ );
+
+ this.roomImage = this.appChatService.getRoomProfileImage(
+ this.currentRoomInfo,
+ this.loginRes,
+ this.roomUsersDictionary,
+ this.roomUsersShortDictionary
+ );
+ }
+ this.changeDetectorRef.detectChanges();
+ }
+
+ onToggleAlarm(roomInfo: RoomInfo): void {
+ if (!roomInfo) {
+ return;
+ }
+
+ this.store.dispatch(
+ RoomActions.update({
+ req: {
+ roomId: roomInfo.roomId,
+ roomName:
+ roomInfo.roomName.trim().length === 0
+ ? ' '
+ : roomInfo.roomName.trim(),
+ receiveAlarm: !roomInfo.receiveAlarm,
+ syncAll: false
+ } as UpdateRequest
+ })
+ );
+ this.changeDetectorRef.detectChanges();
+ }
+
+ onOpenRoomUserList(roomInfo: RoomInfo): void {}
+
+ onOpenChatSearch(): void {
+ this.openChatSearch.emit();
+ }
+
+ onRightDrawerToggle(): void {
+ this.rightDrawerToggle.emit();
+ }
+}
diff --git a/src/app/sections/chat/components/list.section.component.html b/src/app/sections/chat/components/list.section.component.html
new file mode 100644
index 0000000..8df303e
--- /dev/null
+++ b/src/app/sections/chat/components/list.section.component.html
@@ -0,0 +1,30 @@
+
+
+
+
diff --git a/src/app/sections/chat/components/list.section.component.scss b/src/app/sections/chat/components/list.section.component.scss
new file mode 100644
index 0000000..356b3ad
--- /dev/null
+++ b/src/app/sections/chat/components/list.section.component.scss
@@ -0,0 +1,2 @@
+.list-container {
+}
diff --git a/src/app/sections/chat/components/list.section.component.spec.ts b/src/app/sections/chat/components/list.section.component.spec.ts
new file mode 100644
index 0000000..c60a7bb
--- /dev/null
+++ b/src/app/sections/chat/components/list.section.component.spec.ts
@@ -0,0 +1,32 @@
+import { TestBed, async } from '@angular/core/testing';
+import { RouterTestingModule } from '@angular/router/testing';
+import { ListSectionComponent } from './list.section.component';
+
+describe('app::sections::group::ListSectionComponent', () => {
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ imports: [RouterTestingModule],
+ declarations: [ListSectionComponent]
+ }).compileComponents();
+ }));
+
+ it('should create the app', () => {
+ const fixture = TestBed.createComponent(ListSectionComponent);
+ const app = fixture.componentInstance;
+ expect(app).toBeTruthy();
+ });
+
+ it(`should have as title 'ucap-lg-web'`, () => {
+ const fixture = TestBed.createComponent(ListSectionComponent);
+ const app = fixture.componentInstance;
+ });
+
+ it('should render title', () => {
+ const fixture = TestBed.createComponent(ListSectionComponent);
+ fixture.detectChanges();
+ const compiled = fixture.nativeElement;
+ expect(compiled.querySelector('.content span').textContent).toContain(
+ 'ucap-lg-web app is running!'
+ );
+ });
+});
diff --git a/src/app/sections/chat/components/list.section.component.ts b/src/app/sections/chat/components/list.section.component.ts
new file mode 100644
index 0000000..1127468
--- /dev/null
+++ b/src/app/sections/chat/components/list.section.component.ts
@@ -0,0 +1,416 @@
+import { Subject, combineLatest } from 'rxjs';
+import { takeUntil, take } from 'rxjs/operators';
+
+import {
+ Component,
+ OnInit,
+ OnDestroy,
+ ChangeDetectionStrategy,
+ ChangeDetectorRef,
+ Input,
+ EventEmitter,
+ Output,
+ NgZone
+} from '@angular/core';
+
+import { Store, select } from '@ngrx/store';
+
+import {
+ FixedSizeVirtualScrollStrategy,
+ VIRTUAL_SCROLL_STRATEGY
+} from '@angular/cdk/scrolling';
+
+import { LoginResponse } from '@ucap/protocol-authentication';
+
+import { LogService } from '@ucap/ng-logger';
+import { SessionStorageService } from '@ucap/ng-web-storage';
+import {
+ LoginSelector,
+ ConfigurationSelector
+} from '@ucap/ng-store-authentication';
+import { RoomSelector, RoomActions } from '@ucap/ng-store-chat';
+import {
+ RoomInfo,
+ RoomType,
+ ExitAllRequest,
+ UpdateRequest,
+ ExitRequest
+} from '@ucap/protocol-room';
+import {
+ RoomUserMap,
+ RoomUserShortMap
+} from '@ucap/ng-store-chat/lib/store/room/state';
+import { Dictionary } from '@ngrx/entity';
+import {
+ TranslatePipe as OrganizationTranslate,
+ TranslateService
+} from '@ucap/ng-ui-organization';
+import { I18nService } from '@ucap/ng-i18n';
+import { AppChatService } from '@app/services/app-chat.service';
+import {
+ DateService,
+ ConfirmDialogComponent,
+ ConfirmDialogData,
+ ConfirmDialogResult
+} from '@ucap/ng-ui';
+import { VersionInfo2Response } from '@ucap/api-public';
+import { MatDialog } from '@angular/material/dialog';
+import { Router } from '@angular/router';
+
+export class ChatVirtualScrollStrategy extends FixedSizeVirtualScrollStrategy {
+ constructor() {
+ super(60, 150, 200); // (itemSize, minBufferPx, maxBufferPx)
+ }
+}
+
+@Component({
+ selector: 'app-sections-chat-list',
+ templateUrl: './list.section.component.html',
+ styleUrls: ['./list.section.component.scss'],
+ providers: [
+ {
+ provide: VIRTUAL_SCROLL_STRATEGY,
+ useClass: ChatVirtualScrollStrategy
+ }
+ ],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class ListSectionComponent implements OnInit, OnDestroy {
+ @Input()
+ set searchObj(obj: { isShowSearch: boolean; searchWord: string }) {
+ this._searchObj = obj;
+
+ if (obj.isShowSearch && obj.searchWord.localeCompare('') !== 0) {
+ this.onRoomSearch(obj);
+ } else {
+ this._searchObj.isShowSearch = false;
+ this.searchRoomList = [];
+ }
+ this.searchResultList.emit(this.searchRoomList);
+ }
+
+ get searchObj() {
+ return this._searchObj;
+ }
+ // tslint:disable-next-line: variable-name
+ _searchObj: any;
+
+ @Input()
+ checkable = false;
+
+ @Input()
+ selectedRoomList: RoomInfo[] = [];
+
+ @Output()
+ searchResultList = new EventEmitter();
+
+ @Output()
+ toggleItem = new EventEmitter<{
+ checked: boolean;
+ roomInfo: RoomInfo;
+ }>();
+
+ @Output()
+ delRoom = new EventEmitter();
+
+ versionInfo2Res: VersionInfo2Response;
+ loginRes: LoginResponse;
+
+ defaultProfileImage: string;
+ defaultProfileImageMulti: string;
+
+ roomList: RoomInfo[];
+ roomUsersDictionary: Dictionary;
+ roomUsersShortDictionary: Dictionary;
+
+ searchRoomList: RoomInfo[];
+
+ roomGroup: { division: string; roomList: RoomInfo[] }[];
+
+ organizationTranslate: OrganizationTranslate;
+
+ private ngOnDestroySubject: Subject;
+
+ constructor(
+ private router: Router,
+ private appChatService: AppChatService,
+ private dateService: DateService,
+ private sessionStorageService: SessionStorageService,
+ private i18nService: I18nService,
+ private translateService: TranslateService,
+ private store: Store,
+ private dialog: MatDialog,
+ private changeDetectorRef: ChangeDetectorRef,
+ private ngZone: NgZone,
+ private logService: LogService
+ ) {
+ // language setting
+ this.translateService.setDefaultLang(this.i18nService.currentLng);
+ this.translateService.use(this.i18nService.currentLng);
+ this.organizationTranslate = new OrganizationTranslate(
+ this.translateService,
+ this.changeDetectorRef
+ );
+ this.i18nService.setDefaultNamespace('chat');
+
+ // default image setting
+ this.defaultProfileImage = this.appChatService.defaultProfileImage;
+ this.defaultProfileImageMulti = this.appChatService.defaultProfileImage;
+ }
+
+ ngOnInit(): void {
+ this.ngOnDestroySubject = new Subject();
+
+ this.store
+ .pipe(
+ takeUntil(this.ngOnDestroySubject),
+ select(ConfigurationSelector.versionInfo2Response)
+ )
+ .subscribe((versionInfo2Res) => {
+ this.versionInfo2Res = versionInfo2Res;
+ });
+
+ this.store
+ .pipe(takeUntil(this.ngOnDestroySubject), select(LoginSelector.loginRes))
+ .subscribe((loginRes) => {
+ this.loginRes = loginRes;
+ });
+
+ this.store
+ .pipe(takeUntil(this.ngOnDestroySubject), select(RoomSelector.rooms))
+ .subscribe((rooms) => {
+ rooms = (rooms || []).filter((info) => info.isJoinRoom);
+ this.roomList = rooms;
+
+ // groupping.
+ this.initGroup();
+
+ this.changeDetectorRef.detectChanges();
+ });
+
+ combineLatest([
+ this.store.pipe(
+ select(
+ (state: any) =>
+ state.chat.room.roomUsers.entities as Dictionary
+ )
+ ),
+ this.store.pipe(
+ select(
+ (state: any) =>
+ state.chat.room.roomUsersShort.entities as Dictionary<
+ RoomUserShortMap
+ >
+ )
+ )
+ ])
+ .pipe(takeUntil(this.ngOnDestroySubject))
+ .subscribe(([roomUsers, roomUsersShort]) => {
+ this.roomUsersDictionary = roomUsers;
+ this.roomUsersShortDictionary = roomUsersShort;
+ this.changeDetectorRef.detectChanges();
+ });
+ }
+
+ ngOnDestroy(): void {
+ if (!!this.ngOnDestroySubject) {
+ this.ngOnDestroySubject.complete();
+ }
+ }
+
+ initGroup() {
+ this.roomGroup = [];
+
+ this.roomList.forEach((roomInfo) => {
+ const date = roomInfo.finalEventDate;
+ let division = '';
+ try {
+ const value = this.dateService.get(date, 'LL');
+
+ if (value === 'Invalid date') {
+ division = date;
+ } else {
+ division = value;
+ }
+ } catch (error) {
+ division = date;
+ }
+
+ const index = this.roomGroup.findIndex(
+ (info) => info.division === division
+ );
+ if (index > -1) {
+ this.roomGroup[index] = {
+ ...this.roomGroup[index],
+ roomList: [...this.roomGroup[index].roomList, roomInfo]
+ };
+ } else {
+ this.roomGroup.push({
+ division,
+ roomList: [roomInfo]
+ });
+ }
+ });
+ }
+
+ getRoomName(roomInfo: RoomInfo): string {
+ if (!roomInfo) {
+ return '';
+ }
+
+ const roomName = this.appChatService.getRoomName(
+ this.organizationTranslate,
+ this.loginRes,
+ roomInfo,
+ this.roomUsersDictionary,
+ this.roomUsersShortDictionary
+ );
+
+ return roomName;
+ }
+
+ getRoomProfileImage(roomInfo: RoomInfo): string {
+ let roomImage = '';
+ if (!!roomInfo) {
+ roomImage = this.appChatService.getRoomProfileImage(
+ roomInfo,
+ this.loginRes,
+ this.roomUsersDictionary,
+ this.roomUsersShortDictionary
+ );
+ }
+
+ return roomImage;
+ }
+
+ isToday(date: any) {
+ return this.dateService.isToday(date);
+ }
+
+ onRoomSearch(obj: { isShowSearch: boolean; searchWord: string }) {
+ const searchRoomList: RoomInfo[] = [];
+
+ this.roomList.forEach((roomInfo) => {
+ if (roomInfo.roomName.indexOf(obj.searchWord) > -1) {
+ searchRoomList.push(roomInfo);
+ } else {
+ const roomUsers = this.appChatService.getRoomUserList(
+ this.loginRes,
+ roomInfo.roomId,
+ this.roomUsersDictionary,
+ this.roomUsersShortDictionary
+ );
+
+ if (
+ roomUsers.existUsers &&
+ roomUsers.users.filter(
+ (userInfo) =>
+ userInfo.name.indexOf(obj.searchWord) > -1 ||
+ userInfo.nameEn.indexOf(obj.searchWord) > -1 ||
+ userInfo.nameCn.indexOf(obj.searchWord) > -1
+ ).length > 0
+ ) {
+ searchRoomList.push(roomInfo);
+ }
+ }
+ });
+
+ this.searchRoomList = searchRoomList;
+ }
+
+ getChecked(roomInfo: RoomInfo): boolean {
+ if (this.selectedRoomList.some((info) => info.roomId === roomInfo.roomId)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ onToggleItem(event: { checked: boolean; roomInfo: RoomInfo }): void {
+ this.toggleItem.emit(event);
+ }
+
+ onClickRoomItem(event: MouseEvent, roomInfo: RoomInfo): void {
+ event.preventDefault();
+ event.stopPropagation();
+
+ if (!!this.checkable) {
+ this.onToggleItem({
+ checked: !this.getChecked(roomInfo),
+ roomInfo
+ });
+ } else {
+ this.onOpenChatRoom(roomInfo);
+ }
+ }
+
+ onOpenChatRoom(roomInfo: RoomInfo): void {
+ this.ngZone.run(() => {
+ this.router.navigate(
+ [
+ 'chat',
+ {
+ outlets: { content: 'chatroom' }
+ }
+ ],
+ {
+ queryParams: { roomId: roomInfo.roomId }
+ }
+ );
+ });
+ }
+
+ onToggleAlarm(roomInfo: RoomInfo): void {
+ if (!roomInfo) {
+ return;
+ }
+
+ this.store.dispatch(
+ RoomActions.update({
+ req: {
+ roomId: roomInfo.roomId,
+ roomName:
+ roomInfo.roomName.trim().length === 0
+ ? ' '
+ : roomInfo.roomName.trim(),
+ receiveAlarm: !roomInfo.receiveAlarm,
+ syncAll: false
+ } as UpdateRequest
+ })
+ );
+ this.changeDetectorRef.detectChanges();
+ }
+
+ onDelRoom(roomInfo: RoomInfo): void {
+ if (!roomInfo) {
+ return;
+ }
+
+ const dialogRef = this.dialog.open<
+ ConfirmDialogComponent,
+ ConfirmDialogData,
+ ConfirmDialogResult
+ >(ConfirmDialogComponent, {
+ data: {
+ title: this.i18nService.t('room.dialog.titleExitFromRoom'),
+ html: this.i18nService.t('room.dialog.confirmExitFromRoom')
+ }
+ });
+
+ dialogRef
+ .afterClosed()
+ .pipe(take(1))
+ .subscribe((result) => {
+ if (!!result && !!result.choice) {
+ this.store.dispatch(
+ RoomActions.del({
+ req: {
+ roomId: roomInfo.roomId
+ } as ExitRequest
+ })
+ );
+ }
+ });
+ this.changeDetectorRef.detectChanges();
+ }
+}
diff --git a/src/app/sections/chat/components/list.section.strategy.ts b/src/app/sections/chat/components/list.section.strategy.ts
new file mode 100644
index 0000000..54b08bc
--- /dev/null
+++ b/src/app/sections/chat/components/list.section.strategy.ts
@@ -0,0 +1,31 @@
+import { Observable, Subject } from 'rxjs';
+
+import {
+ VirtualScrollStrategy,
+ CdkVirtualScrollViewport
+} from '@angular/cdk/scrolling';
+import { distinctUntilChanged } from 'rxjs/operators';
+
+export class ChatGroupVirtualScrollStrategy implements VirtualScrollStrategy {
+ scrolledIndexChange: Observable;
+
+ private indexSubject = new Subject();
+ private viewport: CdkVirtualScrollViewport | null = null;
+
+ constructor() {
+ this.scrolledIndexChange = this.indexSubject.pipe(distinctUntilChanged());
+ }
+
+ attach(viewport: CdkVirtualScrollViewport): void {
+ this.viewport = viewport;
+ }
+ detach(): void {
+ this.indexSubject.complete();
+ this.viewport = null;
+ }
+ onContentScrolled(): void {}
+ onDataLengthChanged(): void {}
+ onContentRendered(): void {}
+ onRenderedOffsetChanged(): void {}
+ scrollToIndex(index: number, behavior: ScrollBehavior): void {}
+}
diff --git a/src/app/sections/chat/components/message.section.component.html b/src/app/sections/chat/components/message.section.component.html
new file mode 100644
index 0000000..539a923
--- /dev/null
+++ b/src/app/sections/chat/components/message.section.component.html
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 스크롤 상단일때 도착한 최근 메시지 표시 영역
+
+
diff --git a/src/app/sections/chat/components/message.section.component.scss b/src/app/sections/chat/components/message.section.component.scss
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/sections/chat/components/message.section.component.spec.ts b/src/app/sections/chat/components/message.section.component.spec.ts
new file mode 100644
index 0000000..f563e0a
--- /dev/null
+++ b/src/app/sections/chat/components/message.section.component.spec.ts
@@ -0,0 +1,32 @@
+import { TestBed, async } from '@angular/core/testing';
+import { RouterTestingModule } from '@angular/router/testing';
+import { MessageSectionComponent } from './message.section.component';
+
+describe('app::sections::group::MessageSectionComponent', () => {
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ imports: [RouterTestingModule],
+ declarations: [MessageSectionComponent]
+ }).compileComponents();
+ }));
+
+ it('should create the app', () => {
+ const fixture = TestBed.createComponent(MessageSectionComponent);
+ const app = fixture.componentInstance;
+ expect(app).toBeTruthy();
+ });
+
+ it(`should have as title 'ucap-lg-web'`, () => {
+ const fixture = TestBed.createComponent(MessageSectionComponent);
+ const app = fixture.componentInstance;
+ });
+
+ it('should render title', () => {
+ const fixture = TestBed.createComponent(MessageSectionComponent);
+ fixture.detectChanges();
+ const compiled = fixture.nativeElement;
+ expect(compiled.querySelector('.content span').textContent).toContain(
+ 'ucap-lg-web app is running!'
+ );
+ });
+});
diff --git a/src/app/sections/chat/components/message.section.component.ts b/src/app/sections/chat/components/message.section.component.ts
new file mode 100644
index 0000000..396c8f2
--- /dev/null
+++ b/src/app/sections/chat/components/message.section.component.ts
@@ -0,0 +1,172 @@
+import {
+ Subject,
+ Observable,
+ BehaviorSubject,
+ Subscription,
+ merge
+} from 'rxjs';
+import { takeUntil } from 'rxjs/operators';
+
+import {
+ Component,
+ OnInit,
+ OnDestroy,
+ ChangeDetectionStrategy,
+ ChangeDetectorRef,
+ Input
+} from '@angular/core';
+import { Info, EventJson, EventType, FileType } from '@ucap/protocol-event';
+import { Store, select } from '@ngrx/store';
+import { Chatting } from '@ucap/ng-store-chat/lib/store/chatting/state';
+import { ChattingSelector, RoomSelector } from '@ucap/ng-store-chat';
+import {
+ UserInfo as RoomUserInfo,
+ UserInfoShort as RoomUserInfoShort
+} from '@ucap/protocol-room';
+import { LoginResponse } from '@ucap/protocol-authentication';
+import {
+ LoginSelector,
+ ConfigurationSelector
+} from '@ucap/ng-store-authentication';
+import { AppChatService } from '@app/services/app-chat.service';
+import { VersionInfo2Response } from '@ucap/api-public';
+import moment from 'moment';
+
+@Component({
+ selector: 'app-sections-chat-message',
+ templateUrl: './message.section.component.html',
+ styleUrls: ['./message.section.component.scss'],
+
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class MessageSectionComponent implements OnInit, OnDestroy {
+ @Input()
+ set roomId(roomId: string) {
+ this._roomId = roomId;
+
+ this.roomIdSubject.next(roomId);
+
+ this.initializeRoomData();
+ }
+ get roomId(): string {
+ return this._roomId;
+ }
+ // tslint:disable-next-line: variable-name
+ _roomId: string;
+
+ versionInfo2Res: VersionInfo2Response;
+ loginRes: LoginResponse;
+
+ roomIdSubject = new Subject();
+
+ chatting$: Observable;
+ roomUsers: RoomUserInfoShort[] = [];
+ // eventList$: Observable[]>;
+ eventList: Info[];
+
+ defaultProfileImage: string;
+ EventType = EventType;
+ FileType = FileType;
+
+ private ngOnDestroySubject = new Subject();
+ constructor(
+ private appChatService: AppChatService,
+ private store: Store,
+ private changeDetectorRef: ChangeDetectorRef
+ ) {
+ this.defaultProfileImage = this.appChatService.defaultProfileImage;
+ }
+
+ ngOnInit(): void {
+ this.store
+ .pipe(
+ takeUntil(this.ngOnDestroySubject),
+ select(ConfigurationSelector.versionInfo2Response)
+ )
+ .subscribe((versionInfo2Res) => {
+ this.versionInfo2Res = versionInfo2Res;
+ });
+ this.store
+ .pipe(takeUntil(this.ngOnDestroySubject), select(LoginSelector.loginRes))
+ .subscribe((loginRes) => {
+ this.loginRes = loginRes;
+ });
+ }
+
+ initializeRoomData() {
+ this.chatting$ = this.store.pipe(
+ takeUntil(merge(this.ngOnDestroySubject, this.roomIdSubject)),
+ select(ChattingSelector.chatting, this.roomId)
+ );
+
+ this.store
+ .pipe(
+ takeUntil(merge(this.ngOnDestroySubject, this.roomIdSubject)),
+ select(ChattingSelector.eventList, this.roomId)
+ )
+ .subscribe((eventList) => {
+ this.eventList = eventList;
+
+ this.changeDetectorRef.detectChanges();
+ });
+
+ this.store
+ .pipe(
+ takeUntil(merge(this.ngOnDestroySubject, this.roomIdSubject)),
+ select(RoomSelector.roomUserShort, this.roomId)
+ )
+ .subscribe((roomUserShort) => {
+ if (!!roomUserShort) {
+ this.roomUsers = roomUserShort.userInfos;
+ }
+ });
+ }
+
+ ngOnDestroy(): void {
+ if (!!this.ngOnDestroySubject) {
+ this.ngOnDestroySubject.complete();
+ }
+ }
+
+ /** Sender Info */
+ getSenderInfo(
+ senderSeq: number
+ ): RoomUserInfoShort | RoomUserInfo | undefined {
+ if (!!this.roomUsers && this.roomUsers.length) {
+ return this.roomUsers.find(
+ (userInfo) => userInfo.seq + '' === senderSeq + ''
+ );
+ }
+
+ return undefined;
+ }
+
+ /** Date Splitter show check */
+ getDateSplitter(event: Info): boolean {
+ const curIndex = this.eventList.findIndex((v) => v.seq === event.seq);
+
+ if (curIndex === 0) {
+ return true;
+ }
+ if (curIndex > 0) {
+ if (!this.eventList[curIndex]) {
+ return false;
+ }
+ return !moment(this.eventList[curIndex].sendDate).isSame(
+ moment(this.eventList[curIndex - 1].sendDate),
+ 'day'
+ );
+ }
+ return false;
+ }
+
+ /** Unread Count calculation */
+ getUnreadCount(message: Info): string | number {
+ const unreadCnt = this.roomUsers
+ .filter(
+ (user) => user.isJoinRoom && user.seq + '' !== message.senderSeq + ''
+ )
+ .filter((user) => user.lastReadEventSeq < message.seq).length;
+ return unreadCnt === 0 ? '' : unreadCnt;
+ }
+}
diff --git a/src/app/sections/chat/components/search.section.component.html b/src/app/sections/chat/components/search.section.component.html
new file mode 100644
index 0000000..b25f174
--- /dev/null
+++ b/src/app/sections/chat/components/search.section.component.html
@@ -0,0 +1,37 @@
+
diff --git a/src/app/sections/chat/components/search.section.component.scss b/src/app/sections/chat/components/search.section.component.scss
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/sections/chat/components/search.section.component.spec.ts b/src/app/sections/chat/components/search.section.component.spec.ts
new file mode 100644
index 0000000..e45a1ca
--- /dev/null
+++ b/src/app/sections/chat/components/search.section.component.spec.ts
@@ -0,0 +1,32 @@
+import { TestBed, async } from '@angular/core/testing';
+import { RouterTestingModule } from '@angular/router/testing';
+import { SearchSectionComponent } from './search.section.component';
+
+describe('app::sections::group::SearchSectionComponent', () => {
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ imports: [RouterTestingModule],
+ declarations: [SearchSectionComponent]
+ }).compileComponents();
+ }));
+
+ it('should create the app', () => {
+ const fixture = TestBed.createComponent(SearchSectionComponent);
+ const app = fixture.componentInstance;
+ expect(app).toBeTruthy();
+ });
+
+ it(`should have as title 'ucap-lg-web'`, () => {
+ const fixture = TestBed.createComponent(SearchSectionComponent);
+ const app = fixture.componentInstance;
+ });
+
+ it('should render title', () => {
+ const fixture = TestBed.createComponent(SearchSectionComponent);
+ fixture.detectChanges();
+ const compiled = fixture.nativeElement;
+ expect(compiled.querySelector('.content span').textContent).toContain(
+ 'ucap-lg-web app is running!'
+ );
+ });
+});
diff --git a/src/app/sections/chat/components/search.section.component.ts b/src/app/sections/chat/components/search.section.component.ts
new file mode 100644
index 0000000..4651d79
--- /dev/null
+++ b/src/app/sections/chat/components/search.section.component.ts
@@ -0,0 +1,147 @@
+import {
+ Component,
+ OnInit,
+ OnDestroy,
+ Output,
+ EventEmitter,
+ ChangeDetectorRef,
+ ChangeDetectionStrategy,
+ ViewChild
+} from '@angular/core';
+import { FormGroup, FormBuilder } from '@angular/forms';
+
+import { Store, select } from '@ngrx/store';
+
+import { LogService } from '@ucap/ng-logger';
+import { takeUntil, debounceTime } from 'rxjs/operators';
+import { Subject, combineLatest } from 'rxjs';
+
+import { RoomSelector } from '@ucap/ng-store-chat';
+import { LoginSelector } from '@ucap/ng-store-authentication';
+
+import { LoginResponse } from '@ucap/protocol-authentication';
+import { MatAutocompleteTrigger } from '@angular/material/autocomplete';
+
+@Component({
+ selector: 'app-sections-chat-search',
+ templateUrl: './search.section.component.html',
+ styleUrls: ['./search.section.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class SearchSectionComponent implements OnInit, OnDestroy {
+ @Output()
+ keyDownEnter = new EventEmitter<{
+ searchWord: string;
+ }>();
+
+ @Output()
+ searchCancel = new EventEmitter();
+
+ loginRes: LoginResponse;
+
+ fgSearch: FormGroup;
+ recommendedWordList: string[];
+ filteredRecommendedWordList: string[];
+
+ @ViewChild(MatAutocompleteTrigger) autocomplete: MatAutocompleteTrigger;
+
+ private ngOnDestroySubject: Subject;
+
+ constructor(
+ private store: Store,
+ private formBuilder: FormBuilder,
+ private changeDetectorRef: ChangeDetectorRef,
+ private logService: LogService
+ ) {}
+
+ ngOnInit(): void {
+ this.ngOnDestroySubject = new Subject();
+
+ this.fgSearch = this.formBuilder.group({
+ searchInput: null
+ });
+
+ this.fgSearch
+ .get('searchInput')
+ .valueChanges.pipe(debounceTime(100))
+ .subscribe((value) => {
+ if (value !== null && value.length > 0) {
+ this.filteredRecommendedWordList = this.recommendedWordList.filter(
+ (v) => {
+ return v.includes(value);
+ }
+ );
+ } else {
+ this.filteredRecommendedWordList = [];
+ }
+
+ this.changeDetectorRef.detectChanges();
+ });
+
+ this.store
+ .pipe(takeUntil(this.ngOnDestroySubject), select(LoginSelector.loginRes))
+ .subscribe((loginRes) => {
+ this.loginRes = loginRes;
+ });
+
+ combineLatest([
+ this.store.pipe(select(RoomSelector.rooms)),
+ this.store.pipe(select(RoomSelector.roomUsers)),
+ this.store.pipe(select(RoomSelector.roomUsersShort))
+ ])
+ .pipe(takeUntil(this.ngOnDestroySubject))
+ .subscribe(([rooms, roomUsers, roomUsersShort]) => {
+ rooms = rooms || [];
+ roomUsers = roomUsers || [];
+ roomUsersShort = roomUsersShort || [];
+
+ const recommendedWordList = [];
+ for (const r of rooms) {
+ if (!!r.roomName && '' !== r.roomName.trim()) {
+ recommendedWordList.push(r.roomName);
+ }
+ }
+ for (const ru of roomUsers) {
+ for (const u of ru.userInfos) {
+ if (!!this.loginRes && u.seq !== Number(this.loginRes.userSeq)) {
+ if (!!u.name && '' !== u.name.trim() && u.isJoinRoom) {
+ recommendedWordList.push(u.name);
+ }
+ }
+ }
+ }
+ for (const ru of roomUsersShort) {
+ for (const u of ru.userInfos) {
+ if (!!this.loginRes && u.seq !== Number(this.loginRes.userSeq)) {
+ if (!!u.name && '' !== u.name.trim() && u.isJoinRoom) {
+ recommendedWordList.push(u.name);
+ }
+ }
+ }
+ }
+ this.recommendedWordList = [
+ ...recommendedWordList.filter(
+ (item, index) => recommendedWordList.indexOf(item) === index
+ )
+ ];
+ });
+ }
+
+ ngOnDestroy(): void {
+ if (!!this.ngOnDestroySubject) {
+ this.ngOnDestroySubject.complete();
+ }
+ }
+
+ onKeyDownEnter(event: KeyboardEvent, searchWord: string) {
+ event.preventDefault();
+ event.stopPropagation();
+
+ this.autocomplete.closePanel();
+
+ this.keyDownEnter.emit({ searchWord });
+ }
+ onClickCancel() {
+ this.searchCancel.emit();
+ }
+}
diff --git a/src/app/sections/group/components/component-ui/dialogs/create-chat.dialog.component.html b/src/app/sections/group/components/component-ui/dialogs/create-chat.dialog.component.html
new file mode 100644
index 0000000..a026d34
--- /dev/null
+++ b/src/app/sections/group/components/component-ui/dialogs/create-chat.dialog.component.html
@@ -0,0 +1,131 @@
+
+
+ {{ data.title }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 새 그룹 추가
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ userInfo.name }}
+ clear
+
+
+
+
+
+ = 300 ? 'text-warn-color' : ''">
+ {{ selectedUserList.length }} / 300
+
+
+
+ = 300"
+ >
+
+
+
+
+
+ {{ selectedUserList.length }}
+
+
+
+
diff --git a/src/app/sections/group/components/component-ui/dialogs/create-chat.dialog.component.scss b/src/app/sections/group/components/component-ui/dialogs/create-chat.dialog.component.scss
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/sections/group/components/component-ui/dialogs/create-chat.dialog.component.spec.ts b/src/app/sections/group/components/component-ui/dialogs/create-chat.dialog.component.spec.ts
new file mode 100644
index 0000000..46e44a0
--- /dev/null
+++ b/src/app/sections/group/components/component-ui/dialogs/create-chat.dialog.component.spec.ts
@@ -0,0 +1,26 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { By } from '@angular/platform-browser';
+import { DebugElement } from '@angular/core';
+
+import { CreateChatDialogComponent } from './create-chat.dialog.component';
+
+describe('ucap::ui-organization::CreateChatDialogComponent', () => {
+ let component: CreateChatDialogComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [CreateChatDialogComponent]
+ }).compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(CreateChatDialogComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/sections/group/components/component-ui/dialogs/create-chat.dialog.component.ts b/src/app/sections/group/components/component-ui/dialogs/create-chat.dialog.component.ts
new file mode 100644
index 0000000..a65f04a
--- /dev/null
+++ b/src/app/sections/group/components/component-ui/dialogs/create-chat.dialog.component.ts
@@ -0,0 +1,134 @@
+import {
+ Component,
+ OnInit,
+ OnDestroy,
+ ChangeDetectionStrategy,
+ ChangeDetectorRef,
+ Inject,
+ ViewChild
+} from '@angular/core';
+import { UserInfo, GroupDetailData } from '@ucap/protocol-sync';
+import {
+ UserInfoSS,
+ UserInfoF,
+ UserInfoDN,
+ DeptInfo
+} from '@ucap/protocol-query';
+
+import { Store, select } from '@ngrx/store';
+import { takeUntil } from 'rxjs/operators';
+import {
+ CompanySelector,
+ DepartmentSelector
+} from '@ucap/ng-store-organization';
+import { Subject, combineLatest } from 'rxjs';
+import { AppAuthenticationService } from '@app/services/app-authentication.service';
+import { SelectUserDialogType } from '@app/types';
+import { RoomInfo, UserInfo as RoomUserInfo } from '@ucap/protocol-room';
+import { FormGroup, FormBuilder, Validators } from '@angular/forms';
+import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
+import { SelectUserSectionComponent } from '../../select-user.section.component';
+import { GroupActions } from '@ucap/ng-store-group';
+import { UserInfoTypes } from '../profile-list-item.component';
+
+export interface CreateChatDialogData {
+ type?: SelectUserDialogType;
+ title: string;
+ /** CASE :: EditMember */
+ group?: GroupDetailData;
+ /** CASE :: EventForward */
+ ignoreRoom?: RoomInfo[];
+ /** CASE :: EditChatMember */
+ curRoomUser?: (
+ | UserInfo
+ | UserInfoSS
+ | UserInfoF
+ | UserInfoDN
+ | RoomUserInfo
+ )[];
+}
+export interface CreateChatDialogResult {}
+
+@Component({
+ selector: 'ucap-local-organization-create-chat.dialog',
+ templateUrl: './create-chat.dialog.component.html',
+ styleUrls: ['./create-chat.dialog.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class CreateChatDialogComponent implements OnInit, OnDestroy {
+ private ngOnDestroySubject = new Subject();
+ isLinear = false;
+ firstFormGroup: FormGroup;
+ secondFormGroup: FormGroup;
+ selectedUserList: UserInfoTypes[] = [];
+ SelectUserDialogType = SelectUserDialogType;
+
+ constructor(
+ public dialogRef: MatDialogRef<
+ CreateChatDialogData,
+ CreateChatDialogResult
+ >,
+ @Inject(MAT_DIALOG_DATA) public data: CreateChatDialogData,
+ private changeDetectorRef: ChangeDetectorRef,
+ private store: Store,
+ private appAuthenticationService: AppAuthenticationService
+ ) {}
+
+ @ViewChild('selectBoxUserComponent', { static: false })
+ selectBoxUserComponent: SelectUserSectionComponent;
+
+ ngOnInit(): void {}
+
+ ngOnDestroy(): void {
+ if (!!this.ngOnDestroySubject) {
+ this.ngOnDestroySubject.complete();
+ }
+ }
+
+ onClickCancel() {}
+ onClickChoice(s: boolean) {}
+ getBtnValid() {}
+ getChipsRemoveYn(userInfo: UserInfo) {}
+ onClickDeleteUser(userInfo: UserInfo) {}
+
+ onChangeSelectedUserList(userList: UserInfoTypes[]) {
+ this.selectedUserList = userList;
+ this.changeDetectorRef.markForCheck();
+ }
+ onClickComplete(groupName: string) {
+ switch (this.data.type) {
+ case SelectUserDialogType.NewGroup:
+ {
+ const userSeqs: string[] = [];
+ this.selectedUserList.map((user) =>
+ userSeqs.push(user.seq.toString())
+ );
+ this.store.dispatch(
+ GroupActions.create({
+ groupName,
+ targetUserSeqs: userSeqs
+ })
+ );
+ }
+ break;
+ case SelectUserDialogType.NewChat:
+ {
+ }
+ break;
+ case SelectUserDialogType.EditChatMember:
+ {
+ }
+ break;
+ case SelectUserDialogType.EditMember:
+ {
+ }
+ break;
+ case SelectUserDialogType.MessageForward:
+ {
+ }
+ break;
+ }
+
+ this.dialogRef.close();
+ }
+}
diff --git a/src/app/sections/group/components/component-ui/dialogs/index.ts b/src/app/sections/group/components/component-ui/dialogs/index.ts
new file mode 100644
index 0000000..a2b25b0
--- /dev/null
+++ b/src/app/sections/group/components/component-ui/dialogs/index.ts
@@ -0,0 +1,3 @@
+import { CreateChatDialogComponent } from './create-chat.dialog.component';
+
+export const DIALOGS = [CreateChatDialogComponent];
diff --git a/src/app/sections/group/components/component-ui/index.ts b/src/app/sections/group/components/component-ui/index.ts
new file mode 100644
index 0000000..8f2e212
--- /dev/null
+++ b/src/app/sections/group/components/component-ui/index.ts
@@ -0,0 +1,9 @@
+import { ProfileListItemComponent } from './profile-list-item.component';
+import { ProfileComponent } from './profile.component';
+import { TenantSearchComponent } from './tenant-search.component';
+
+export const COMPONENTS = [
+ ProfileListItemComponent,
+ ProfileComponent,
+ TenantSearchComponent
+];
diff --git a/src/app/sections/group/components/component-ui/profile-list-item.component.html b/src/app/sections/group/components/component-ui/profile-list-item.component.html
new file mode 100644
index 0000000..d222f84
--- /dev/null
+++ b/src/app/sections/group/components/component-ui/profile-list-item.component.html
@@ -0,0 +1,42 @@
+
+
+
+
+
다른용무
+
+
+
+
+
+
+
+
+
+ {{ userInfo | ucapOrganizationTranslate: 'name' }}
+
+
+ {{ userInfo | ucapOrganizationTranslate: 'grade' }}
+
+
+
+ {{ userInfo | ucapOrganizationTranslate: 'deptName' }}
+
+
+
+
+ {{ userInfo.intro }}
+
+
+
+
+
diff --git a/src/app/sections/group/components/component-ui/profile-list-item.component.scss b/src/app/sections/group/components/component-ui/profile-list-item.component.scss
new file mode 100644
index 0000000..57f284c
--- /dev/null
+++ b/src/app/sections/group/components/component-ui/profile-list-item.component.scss
@@ -0,0 +1,101 @@
+@charset 'UTF-8';
+
+@import '../../../../../assets/scss/components.scss';
+
+.user-list {
+ display: flex;
+ flex-flow: row nowrap;
+ justify-content: space-between;
+ padding: 0 16px;
+ height: 70px;
+ align-items: center;
+ &.line-top {
+ border-top: 1px solid $gray-rec;
+ }
+ .user-profile-info {
+ display: inline-flex;
+ flex-direction: row;
+ flex-grow: 2.3;
+ .user-profile-thumb {
+ @include profile-avatar-default(
+ 0 5px 5px 0,
+ 8,
+ $green,
+ 18px
+ ); //오른 아래 공간, 모바일 온라인 아이콘 크기, 모바일 아이콘 색, 모바일 아이콘 bg크기
+ .presence {
+ //PC 상태
+ @include presence-state(8px); //원크기
+ }
+ .profile-image {
+ @include avatar-img(36px, 2px); //아바타 크기, 왼쪽공간
+ }
+ }
+ .user-info {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ justify-content: flex-start;
+ padding-left: 16px;
+ .user-n-g {
+ display: flex;
+ flex-flow: row-reverse nowrap;
+ align-items: flex-end;
+ height: 22px;
+ .user-name {
+ font: {
+ size: 16px;
+ weight: 600;
+ }
+ color: $gray-re21;
+ order: 1;
+ -ms-flex-order: 1;
+ }
+ .user-grade {
+ font: {
+ size: 13px;
+ }
+ color: $gray-re70;
+ margin-left: 4px;
+ order: 0;
+ -ms-flex-order: 0;
+ }
+ }
+ .dept-name {
+ font-size: 12px;
+ color: $gray-re6;
+ line-height: 16px;
+ }
+ }
+ }
+ .intro {
+ display: inline-flex;
+ flex-flow: row nowrap;
+ flex-basis: 35%;
+ flex-grow: 0;
+ align-items: baseline;
+ p {
+ font-size: 11px;
+ line-height: 1.4;
+ display: block;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ display: -webkit-box;
+ -webkit-line-clamp: 2;
+ -webkit-box-orient: vertical;
+ height: 30px;
+ }
+ &:before {
+ content: 'chat';
+ @include font-family-ico(12, center, $lipstick);
+ flex-direction: row;
+ align-items: flex-start;
+ width: 12px;
+ height: 12px;
+ line-height: 12px;
+ margin-right: 4.8px;
+ position: relative;
+ top: 2px;
+ }
+ }
+}
diff --git a/src/app/sections/group/components/component-ui/profile-list-item.component.spec.ts b/src/app/sections/group/components/component-ui/profile-list-item.component.spec.ts
new file mode 100644
index 0000000..80e7d67
--- /dev/null
+++ b/src/app/sections/group/components/component-ui/profile-list-item.component.spec.ts
@@ -0,0 +1,26 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { By } from '@angular/platform-browser';
+import { DebugElement } from '@angular/core';
+
+import { ProfileListItemComponent } from './profile-list-item.component';
+
+describe('ucap::ui-organization::ProfileListItemComponent', () => {
+ let component: ProfileListItemComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [ProfileListItemComponent]
+ }).compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(ProfileListItemComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/sections/group/components/component-ui/profile-list-item.component.ts b/src/app/sections/group/components/component-ui/profile-list-item.component.ts
new file mode 100644
index 0000000..f106b4e
--- /dev/null
+++ b/src/app/sections/group/components/component-ui/profile-list-item.component.ts
@@ -0,0 +1,64 @@
+import {
+ Component,
+ OnInit,
+ OnDestroy,
+ ChangeDetectionStrategy,
+ ChangeDetectorRef,
+ Input,
+ EventEmitter,
+ Output
+} from '@angular/core';
+import { UserInfo } from '@ucap/protocol-sync';
+import { UserInfoSS, UserInfoF, UserInfoDN } from '@ucap/protocol-query';
+
+export type UserInfoTypes = UserInfo | UserInfoSS | UserInfoF | UserInfoDN;
+
+@Component({
+ selector: 'ucap-local-organization-profile-list-item',
+ templateUrl: './profile-list-item.component.html',
+ styleUrls: ['./profile-list-item.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class ProfileListItemComponent implements OnInit, OnDestroy {
+ @Input()
+ set userInfo(user: UserInfoTypes) {
+ this._userInfo = user;
+ }
+ get userInfo(): UserInfoTypes {
+ return this._userInfo;
+ }
+ _userInfo: UserInfoTypes;
+
+ @Input()
+ defaultProfileImage: string;
+
+ @Input()
+ profileImageRoot: string;
+
+ @Input()
+ checkable = false;
+
+ @Output()
+ checkUser = new EventEmitter<{
+ isChecked: boolean;
+ userInfo: UserInfoTypes;
+ }>();
+
+ constructor(private changeDetectorRef: ChangeDetectorRef) {}
+
+ ngOnInit(): void {}
+
+ ngOnDestroy(): void {}
+
+ onClickProfileImage(event: Event, userInfo: UserInfoTypes): void {}
+
+ onChangeCheck(
+ value: boolean,
+ userInfo: UserInfo | UserInfoSS | UserInfoF | UserInfoDN
+ ) {
+ this.checkUser.emit({
+ isChecked: value,
+ userInfo
+ });
+ }
+}
diff --git a/src/app/sections/group/components/component-ui/profile.component.html b/src/app/sections/group/components/component-ui/profile.component.html
new file mode 100644
index 0000000..1c58bdf
--- /dev/null
+++ b/src/app/sections/group/components/component-ui/profile.component.html
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+ {{ userInfo.name }}
+ {{ userInfo.grade }}
+ ({{ userInfo.nameEn }})
+ O온라인
+
+
+
+
+
+
+
+ 이름 부서명, 전화번호, 이메일
+
+ search
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+ {{ userInfo.companyName }}
+
+ -
+ {{ userInfo.deptName }}
+
+ -
+ {{ userInfo.email }}
+
+ -
+ {{ userInfo.lineNumber }}
+
+ -
+ {{ userInfo.hpNumber }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/app/sections/group/components/component-ui/profile.component.scss b/src/app/sections/group/components/component-ui/profile.component.scss
new file mode 100644
index 0000000..873d432
--- /dev/null
+++ b/src/app/sections/group/components/component-ui/profile.component.scss
@@ -0,0 +1,77 @@
+@charset 'UTF-8';
+
+@import '../../../../../assets/scss/components.scss';
+
+.main-profile {
+ display: flex;
+ flex: 0 0 650px;
+ min-height: 100%;
+ min-width: 450px;
+ @include screen(mid) {
+ min-height: auto;
+ }
+ // background-image: url(../../../assets/images/bg/bg_profile1.svg),
+ // url(../../../assets/images/bg/bg_profile2.svg),
+ // url(../../../assets/images/bg/bg_profile3.svg),
+ // url(../../../assets/images/bg/bg_profile4.svg),
+ // url(../../../assets/images/bg/bg_profile5.svg), $bg-linear-gradient;
+ background-repeat: no-repeat;
+ background-position: -213px -223px, 433px 95px, 489px 72px, 433px 517px,
+ 335px 634px, 0 0;
+
+ .example-card {
+ display: flex;
+ flex-direction: column;
+ padding: 60px 8.7%;
+ width: 100%;
+ .my-input {
+ display: flex;
+ flex-flow: row nowrap;
+ justify-content: space-between;
+ margin: 0;
+ width: 100%;
+ border-bottom: 1px solid $white;
+ margin-top: 78px;
+ .my-in-input {
+ font-size: 16px;
+ color: $gray-re3;
+ flex-grow: 1;
+ height: 24px;
+ line-height: 24px;
+ margin-top: 8px;
+ }
+ button {
+ margin-bottom: 5px;
+ }
+ }
+ }
+
+ .user-profile-info-list {
+ margin-top: 60px;
+ ul {
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ height: 250px;
+ li {
+ font-size: 16px;
+ font-weight: 600;
+ color: $brown;
+ span {
+ width: 100px;
+ height: 34px;
+ border-radius: 18px;
+ border: solid 1px #f8f9fd;
+ background-color: #aaa0a5;
+ font-size: 14px;
+ font-weight: 600;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ color: $white;
+ margin-right: 40px;
+ }
+ }
+ }
+ }
+}
diff --git a/src/app/sections/group/components/component-ui/profile.component.spec.ts b/src/app/sections/group/components/component-ui/profile.component.spec.ts
new file mode 100644
index 0000000..0dc6211
--- /dev/null
+++ b/src/app/sections/group/components/component-ui/profile.component.spec.ts
@@ -0,0 +1,32 @@
+import { TestBed, async } from '@angular/core/testing';
+import { RouterTestingModule } from '@angular/router/testing';
+import { ProfileComponent } from './profile.component';
+
+describe('app::sections::group::ProfileComponent', () => {
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ imports: [RouterTestingModule],
+ declarations: [ProfileComponent]
+ }).compileComponents();
+ }));
+
+ it('should create the app', () => {
+ const fixture = TestBed.createComponent(ProfileComponent);
+ const app = fixture.componentInstance;
+ expect(app).toBeTruthy();
+ });
+
+ it(`should have as title 'ucap-lg-web'`, () => {
+ const fixture = TestBed.createComponent(ProfileComponent);
+ const app = fixture.componentInstance;
+ });
+
+ it('should render title', () => {
+ const fixture = TestBed.createComponent(ProfileComponent);
+ fixture.detectChanges();
+ const compiled = fixture.nativeElement;
+ expect(compiled.querySelector('.content span').textContent).toContain(
+ 'ucap-lg-web app is running!'
+ );
+ });
+});
diff --git a/src/app/sections/group/components/component-ui/profile.component.ts b/src/app/sections/group/components/component-ui/profile.component.ts
new file mode 100644
index 0000000..2a86ac7
--- /dev/null
+++ b/src/app/sections/group/components/component-ui/profile.component.ts
@@ -0,0 +1,280 @@
+import {
+ Component,
+ OnInit,
+ OnDestroy,
+ ChangeDetectionStrategy,
+ ChangeDetectorRef,
+ Input,
+ Output,
+ EventEmitter,
+ ViewChild,
+ ElementRef
+} from '@angular/core';
+
+import { AppKey } from '@app/types/app-key.type';
+import { LoginSession } from '@app/models/login-session';
+import { Subject } from 'rxjs';
+import { UserInfoSS, AuthResponse } from '@ucap/protocol-query';
+import { OpenProfileOptions } from '@ucap/protocol-buddy';
+import { FileUploadItem } from '@ucap/api';
+import { FormControl } from '@angular/forms';
+import { WorkStatusType } from '@ucap/protocol';
+import { I18nService } from '@ucap/ng-i18n';
+
+@Component({
+ selector: 'app-group-profile',
+ templateUrl: './profile.component.html',
+ styleUrls: ['./profile.component.scss'],
+
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class ProfileComponent implements OnInit, OnDestroy {
+ private ngOnDestroySubject = new Subject();
+
+ @Input()
+ profileImageRoot: string;
+
+ @Input()
+ isMe: boolean;
+ @Input()
+ isBuddy: boolean;
+ @Input()
+ isFavorit: boolean;
+
+ @Input()
+ set userInfo(u: UserInfoSS) {
+ this._userInfo = u;
+ }
+ get userInfo(): UserInfoSS {
+ return this._userInfo;
+ }
+ _userInfo: UserInfoSS;
+ @Input()
+ myMadn?: string;
+ @Input()
+ openProfileOptions?: OpenProfileOptions;
+ @Input()
+ useBuddyToggleButton: boolean;
+ @Input()
+ authInfo: AuthResponse;
+
+ @Output()
+ profileImageView = new EventEmitter();
+ @Output()
+ openChat = new EventEmitter();
+ @Output()
+ sendMessage = new EventEmitter();
+ @Output()
+ sendCall = new EventEmitter();
+ @Output()
+ sendSms = new EventEmitter();
+ @Output()
+ createConference = new EventEmitter();
+ @Output()
+ toggleFavorit = new EventEmitter<{
+ userInfo: UserInfoSS;
+ isFavorit: boolean;
+ }>();
+ @Output()
+ toggleBuddy = new EventEmitter<{
+ userInfo: UserInfoSS;
+ isBuddy: boolean;
+ }>();
+ @Output()
+ uploadProfileImage = new EventEmitter();
+ @Output()
+ updateIntro = new EventEmitter();
+
+ @ViewChild('profileImageFileInput', { static: false })
+ profileImageFileInput: ElementRef;
+
+ userIntroFormControl = new FormControl('');
+ profileImageFileUploadItem: FileUploadItem;
+
+ constructor(private i18nService: I18nService) {}
+
+ ngOnInit(): void {}
+
+ ngOnDestroy(): void {
+ if (!!this.ngOnDestroySubject) {
+ this.ngOnDestroySubject.complete();
+ }
+ }
+
+ onClickProfileImageView() {
+ this.profileImageView.emit();
+ }
+
+ onClickOpenChat() {
+ this.openChat.emit(this.userInfo);
+ }
+
+ onClickCall(type: string) {
+ let calleeNumber = '';
+
+ if (type === 'LINE') {
+ calleeNumber = this.userInfo.lineNumber;
+ } else {
+ calleeNumber = this.userInfo.hpNumber;
+ }
+ this.sendCall.emit(calleeNumber);
+ }
+
+ onClickSMS() {
+ this.sendSms.emit(this.userInfo.employeeNum);
+ }
+
+ onClickVideoConference() {
+ this.createConference.emit(Number(this.userInfo.seq));
+ }
+
+ onClickMessage() {
+ this.sendMessage.emit(this.userInfo);
+ }
+
+ onToggleFavorit() {
+ this.isFavorit = !this.isFavorit;
+
+ this.toggleFavorit.emit({
+ userInfo: this.userInfo,
+ isFavorit: this.isFavorit
+ });
+ }
+
+ onClickAddBuddy() {
+ this.toggleBuddy.emit({
+ userInfo: this.userInfo,
+ isBuddy: true
+ });
+ }
+
+ onClickDelBuddy() {
+ this.toggleBuddy.emit({
+ userInfo: this.userInfo,
+ isBuddy: false
+ });
+ }
+ onApplyIntroMessage(intro: string) {
+ if (intro.trim().length < 1) {
+ this.updateIntro.emit(' ');
+ } else {
+ this.updateIntro.emit(intro);
+ }
+ }
+
+ onChangeFileInput() {
+ this.profileImageFileUploadItem = FileUploadItem.fromFiles(
+ this.profileImageFileInput.nativeElement.files
+ )[0];
+
+ this.uploadProfileImage.emit(this.profileImageFileUploadItem);
+
+ this.profileImageFileInput.nativeElement.value = '';
+ }
+
+ getWorkstatus(userInfo: UserInfoSS): string {
+ let workstatus = '';
+ if (!!userInfo && !!userInfo.workstatus) {
+ switch (userInfo.workstatus) {
+ case WorkStatusType.VacationAM:
+ workstatus = '오전';
+ break;
+ case WorkStatusType.VacationPM:
+ workstatus = '오후';
+ break;
+ case WorkStatusType.VacationAll:
+ workstatus = '휴가';
+ break;
+ case WorkStatusType.LeaveOfAbsence:
+ workstatus = '휴직';
+ break;
+ case WorkStatusType.LongtermRefresh:
+ workstatus = '장기';
+ break;
+ }
+ }
+
+ return workstatus;
+ }
+ getWorkstatusStyle(userInfo: UserInfoSS): string {
+ // morning-off: 오전 afternoon-off: 오후 day-off: 휴가 long-time: 장기 leave-of-absence: 휴직
+ let style = '';
+ if (!!userInfo && !!userInfo.workstatus) {
+ switch (userInfo.workstatus) {
+ case WorkStatusType.VacationAM:
+ style = 'morning-off';
+ break;
+ case WorkStatusType.VacationPM:
+ style = 'afternoon-off';
+ break;
+ case WorkStatusType.VacationAll:
+ style = 'day-off';
+ break;
+ case WorkStatusType.LeaveOfAbsence:
+ style = 'leave-of-absence';
+ break;
+ case WorkStatusType.LongtermRefresh:
+ style = 'long-time';
+ break;
+ }
+ }
+
+ return style;
+ }
+
+ getDisabledBtn(type: string): boolean {
+ if (!this.myMadn || this.myMadn.trim().length === 0) {
+ if (type === 'LINE' || type === 'MOBILE') {
+ return true;
+ }
+ }
+
+ if (type === 'LINE') {
+ if (
+ !!this.userInfo &&
+ !!this.userInfo.lineNumber &&
+ this.userInfo.lineNumber.trim().length > 0
+ ) {
+ return false;
+ } else {
+ return true;
+ }
+ } else if (type === 'MOBILE') {
+ if (
+ !!this.userInfo &&
+ !!this.userInfo.hpNumber &&
+ this.userInfo.hpNumber.trim().length > 0
+ ) {
+ return false;
+ } else {
+ return true;
+ }
+ } else if (type === 'SMS') {
+ // const smsUtils = new SmsUtils(
+ // this.sessionStorageService,
+ // this.nativeService
+ // );
+ // return !smsUtils.getAuthSms();
+ }
+
+ return true;
+ }
+
+ getShowBuddyToggleBtn(type: 'DEL' | 'ADD'): boolean {
+ let rtn = false;
+ if (!this.useBuddyToggleButton) {
+ return false;
+ }
+
+ if (type === 'ADD') {
+ if (!this.isBuddy) {
+ rtn = true;
+ }
+ } else if (type === 'DEL') {
+ if (!!this.isBuddy) {
+ rtn = true;
+ }
+ }
+ return rtn;
+ }
+}
diff --git a/src/app/sections/group/components/component-ui/tenant-search.component.html b/src/app/sections/group/components/component-ui/tenant-search.component.html
new file mode 100644
index 0000000..15b5712
--- /dev/null
+++ b/src/app/sections/group/components/component-ui/tenant-search.component.html
@@ -0,0 +1,38 @@
+
+
+
+ {{ company.companyName }}
+
+
+
+
+
+ 이름 부서명, 전화번호, 이메일
+
+
+
+
+
+
diff --git a/src/app/sections/group/components/component-ui/tenant-search.component.scss b/src/app/sections/group/components/component-ui/tenant-search.component.scss
new file mode 100644
index 0000000..bfc8e47
--- /dev/null
+++ b/src/app/sections/group/components/component-ui/tenant-search.component.scss
@@ -0,0 +1,28 @@
+@charset 'UTF-8';
+
+@import '../../../../../assets//scss/components.scss';
+
+.search-container {
+ display: flex;
+ flex-flow: row nowrap;
+ margin: 0 15px 0 17px;
+ width: calc(100% - 32px);
+ border: 1px solid $lipstick;
+ background-color: $white;
+ .search-selec-box {
+ @include selecCtrl(40px, 12px);
+ flex-basis: 150px;
+ }
+ .search-in-box {
+ @include matinputCtrl1(
+ 0,
+ 0,
+ auto,
+ auto,
+ 40px
+ ); //$bdw: 1px, $bdr: 0, $maxw, $minw, $inputH
+ }
+ .btn-ico-search {
+ @include matbtnCtrl(40px, 40px, 0, 20px);
+ }
+}
diff --git a/src/app/sections/group/components/component-ui/tenant-search.component.spec.ts b/src/app/sections/group/components/component-ui/tenant-search.component.spec.ts
new file mode 100644
index 0000000..f4b9b1c
--- /dev/null
+++ b/src/app/sections/group/components/component-ui/tenant-search.component.spec.ts
@@ -0,0 +1,26 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { By } from '@angular/platform-browser';
+import { DebugElement } from '@angular/core';
+
+import { TenantSearchComponent } from './tenant-search.component';
+
+describe('ucap::ui-organization::TenantSearchComponent', () => {
+ let component: TenantSearchComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [TenantSearchComponent]
+ }).compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(TenantSearchComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/sections/group/components/component-ui/tenant-search.component.ts b/src/app/sections/group/components/component-ui/tenant-search.component.ts
new file mode 100644
index 0000000..8535aa4
--- /dev/null
+++ b/src/app/sections/group/components/component-ui/tenant-search.component.ts
@@ -0,0 +1,73 @@
+import {
+ Component,
+ OnInit,
+ OnDestroy,
+ ChangeDetectionStrategy,
+ ChangeDetectorRef,
+ Input,
+ EventEmitter,
+ Output
+} from '@angular/core';
+import { UserInfo } from '@ucap/protocol-sync';
+import { UserInfoSS, UserInfoF, UserInfoDN } from '@ucap/protocol-query';
+
+import { Store, select } from '@ngrx/store';
+import { takeUntil } from 'rxjs/operators';
+import { CompanySelector } from '@ucap/ng-store-organization';
+import { Subject } from 'rxjs';
+import { Company } from '@ucap/api-external';
+import { AppAuthenticationService } from '@app/services/app-authentication.service';
+
+@Component({
+ selector: 'ucap-local-organization-tenant-search',
+ templateUrl: './tenant-search.component.html',
+ styleUrls: ['./tenant-search.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class TenantSearchComponent implements OnInit, OnDestroy {
+ companyList: Company[];
+ companyCode: string;
+
+ @Output()
+ keyDownEnter = new EventEmitter<{
+ companyCode: string;
+ searchWord: string;
+ }>();
+
+ @Output()
+ searchCancel = new EventEmitter();
+
+ private ngOnDestroySubject = new Subject();
+ constructor(
+ private changeDetectorRef: ChangeDetectorRef,
+ private store: Store,
+ private appAuthenticationService: AppAuthenticationService
+ ) {}
+
+ ngOnInit(): void {
+ const userStore = this.appAuthenticationService.getUserStore();
+ this.companyCode = userStore.companyCode;
+
+ this.store
+ .pipe(
+ takeUntil(this.ngOnDestroySubject),
+ select(CompanySelector.companyList)
+ )
+ .subscribe((companyList) => {
+ this.companyList = companyList;
+ });
+ }
+
+ ngOnDestroy(): void {
+ if (!!this.ngOnDestroySubject) {
+ this.ngOnDestroySubject.complete();
+ }
+ }
+
+ onKeyDownEnter(searchWord: string) {
+ this.keyDownEnter.emit({ companyCode: this.companyCode, searchWord });
+ }
+ onClickCancel() {
+ this.searchCancel.emit();
+ }
+}
diff --git a/src/app/sections/group/components/index.ts b/src/app/sections/group/components/index.ts
index 4cbf983..84a2bed 100644
--- a/src/app/sections/group/components/index.ts
+++ b/src/app/sections/group/components/index.ts
@@ -1,4 +1,13 @@
import { ListSectionComponent } from './list.section.component';
import { SearchSectionComponent } from './search.section.component';
+import { ProfileSectionComponent } from './profile.section.component';
+import { InfoSectionComponent } from './info.section.component';
+import { SelectUserSectionComponent } from './select-user.section.component';
-export const COMPONENTS = [ListSectionComponent, SearchSectionComponent];
+export const COMPONENTS = [
+ ListSectionComponent,
+ SearchSectionComponent,
+ ProfileSectionComponent,
+ InfoSectionComponent,
+ SelectUserSectionComponent
+];
diff --git a/src/app/sections/group/components/info.section.component.html b/src/app/sections/group/components/info.section.component.html
new file mode 100644
index 0000000..f4f7355
--- /dev/null
+++ b/src/app/sections/group/components/info.section.component.html
@@ -0,0 +1,55 @@
+
+
+
+
알림봇
+
+
+
+
+
+
+ 화상회의
+ 2020.04.05
+
+
+ 화상회의 개설
+ 화상회의가 개설되었습니다.
+
+
+
+
+
+
+
Empty List
+
+
+
배너
+
diff --git a/src/app/sections/group/components/info.section.component.scss b/src/app/sections/group/components/info.section.component.scss
new file mode 100644
index 0000000..e089dd5
--- /dev/null
+++ b/src/app/sections/group/components/info.section.component.scss
@@ -0,0 +1,203 @@
+@import '~@ucap/lg-scss/mixins';
+
+.info {
+ display: flex;
+ flex-direction: column;
+ flex-grow: 1.354;
+ margin-left: 30px;
+ overflow: hidden;
+ justify-content: space-between;
+ min-width: 450px;
+ @include screen(mid) {
+ overflow: visible;
+ margin-left: 0;
+ padding: 30px 0;
+ }
+ .bookmark {
+ display: flex;
+ flex-direction: column;
+ .chatlist {
+ display: flex;
+ flex-direction: column;
+ margin-top: 15px;
+ .chatlist-con {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+ margin-top: 10px;
+ position: relative;
+ min-height: 70px;
+ &:after {
+ content: '';
+ width: calc(100% - 66px);
+ height: 1px;
+ background-color: $gray-rec;
+ position: absolute;
+ bottom: 0;
+ right: 0;
+ }
+ // Profile thumb//////////////////
+ .user-profile-thumb {
+ @include profile-avatar-default(
+ 0,
+ 18.6,
+ $green,
+ 30px
+ ); //오른 아래 공간, 모바일 온라인 아이콘 크기, 모바일 아이콘 색, 모바일 아이콘 bg크기
+ flex-basis: 46px;
+ .profileImage {
+ @include avatar-img(46px, 0); //아바타 크기, 왼쪽공간
+ }
+ }
+ //////////////////Profile thumb //
+ .chat-info {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ min-height: 70px;
+ flex-grow: 1;
+ padding-left: 20px;
+ .roomName {
+ font: {
+ size: 16px;
+ weight: 600;
+ }
+ color: #333;
+ line-height: 21px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ display: -webkit-box;
+ -webkit-line-clamp: 1;
+ -webkit-box-orient: vertical;
+ height: 21px;
+ }
+ .lastMessage {
+ font: {
+ size: 12px;
+ }
+ color: $gray-re70;
+ line-height: 16px;
+ margin-top: 8px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ display: -webkit-box;
+ -webkit-line-clamp: 1;
+ -webkit-box-orient: vertical;
+ height: 16px;
+ }
+ }
+ .subInfo {
+ color: $gray-re9;
+ line-height: 15px;
+ align-self: flex-start;
+ padding: 12px 0 0 12px;
+ .mat-badge-content {
+ right: 0 !important;
+ border: 1px solid #ffbf2a;
+ width: 20px;
+ height: 20px;
+ line-height: 20px;
+ box-sizing: content-box;
+ top: 23px !important;
+ //background-color: #ffbf2a;
+ }
+ }
+ }
+ }
+ }
+ .allim {
+ overflow-x: auto;
+ @include no-scrollbar();
+ padding-bottom: 10px;
+ margin-bottom: 20px;
+ position: relative;
+ .btn-scroll-ctrl {
+ position: absolute;
+ z-index: 10;
+ top: calc(50% - 23px);
+ left: 22px;
+ width: 46px;
+ height: 46px;
+ background-color: rgba(32, 32, 32, 0.56);
+ border: 1px solid $gray-re70;
+ border-radius: 50%;
+ justify-content: center;
+ display: flex;
+ align-items: center;
+ &.btn-scroll-right {
+ left: auto;
+ right: 22px;
+ }
+ }
+ .allimList {
+ margin-top: 24px;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ .allim-card {
+ flex: 1 0 200px;
+ padding: 27px 17px 0;
+ min-width: 200px;
+ height: 240px;
+ border: solid 1px #d4d4d4;
+ border-bottom-width: 2px;
+ background-color: #ffffff;
+ box-shadow: none;
+ border-radius: 0;
+ margin-left: 16px;
+ &:first-of-type {
+ margin-left: 0;
+ }
+ .mat-card-header {
+ .allim-header-image {
+ background-size: cover;
+ }
+ }
+ .allim-con-title {
+ display: block;
+ height: 36px;
+ line-height: 36px;
+ font-size: 14px;
+ font-weight: 600;
+ color: $gray-re3;
+ border-bottom: 1px solid #d4d4d4;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ align-self: stretch;
+ }
+ .allim-contents {
+ display: block;
+ font-size: 12px;
+ color: $gray-re70;
+ line-height: 1.67;
+ margin: 12px 0 22px;
+ max-height: 43px;
+ height: 43px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ display: -webkit-box;
+ -webkit-line-clamp: 2;
+ -webkit-box-orient: vertical;
+ align-self: stretch;
+ }
+ .btn-more {
+ width: 100%;
+ height: 40px;
+ border-top: 1px solid #d4d4d4;
+ font-size: 12px;
+ font-weight: 600;
+ color: $gray-re9;
+ border-radius: 0;
+ }
+ }
+ }
+ }
+ .banner-box {
+ width: 100%;
+ height: 74px;
+ @extend %guideline;
+ align-self: flex-end;
+ }
+}
diff --git a/src/app/sections/group/components/info.section.component.spec.ts b/src/app/sections/group/components/info.section.component.spec.ts
new file mode 100644
index 0000000..9fe87da
--- /dev/null
+++ b/src/app/sections/group/components/info.section.component.spec.ts
@@ -0,0 +1,32 @@
+import { TestBed, async } from '@angular/core/testing';
+import { RouterTestingModule } from '@angular/router/testing';
+import { InfoSectionComponent } from './info.section.component';
+
+describe('app::sections::group::InfoSectionComponent', () => {
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ imports: [RouterTestingModule],
+ declarations: [InfoSectionComponent]
+ }).compileComponents();
+ }));
+
+ it('should create the app', () => {
+ const fixture = TestBed.createComponent(InfoSectionComponent);
+ const app = fixture.componentInstance;
+ expect(app).toBeTruthy();
+ });
+
+ it(`should have as title 'ucap-lg-web'`, () => {
+ const fixture = TestBed.createComponent(InfoSectionComponent);
+ const app = fixture.componentInstance;
+ });
+
+ it('should render title', () => {
+ const fixture = TestBed.createComponent(InfoSectionComponent);
+ fixture.detectChanges();
+ const compiled = fixture.nativeElement;
+ expect(compiled.querySelector('.content span').textContent).toContain(
+ 'ucap-lg-web app is running!'
+ );
+ });
+});
diff --git a/src/app/sections/group/components/info.section.component.ts b/src/app/sections/group/components/info.section.component.ts
new file mode 100644
index 0000000..61c0fd6
--- /dev/null
+++ b/src/app/sections/group/components/info.section.component.ts
@@ -0,0 +1,251 @@
+import {
+ Component,
+ OnInit,
+ OnDestroy,
+ ChangeDetectionStrategy,
+ ChangeDetectorRef,
+ Input
+} from '@angular/core';
+
+import { AppKey } from '@app/types/app-key.type';
+import { LoginSession } from '@app/models/login-session';
+import { Subject, combineLatest } from 'rxjs';
+import { Store, select } from '@ngrx/store';
+import { takeUntil } from 'rxjs/operators';
+import {
+ LoginSelector,
+ ConfigurationSelector
+} from '@ucap/ng-store-authentication';
+import { LoginResponse } from '@ucap/protocol-authentication';
+import { RoomInfo, RoomType } from '@ucap/protocol-room';
+import { Dictionary } from '@ngrx/entity';
+import {
+ RoomUserMap,
+ RoomUserShortMap
+} from '@ucap/ng-store-chat/lib/store/room/state';
+import { RoomSelector } from '@ucap/ng-store-chat';
+import { VersionInfo2Response } from '@ucap/api-public';
+import { AppChatService } from '@app/services/app-chat.service';
+import { I18nService } from '@ucap/ng-i18n';
+import {
+ TranslatePipe as OrganizationTranslate,
+ TranslateService
+} from '@ucap/ng-ui-organization';
+
+@Component({
+ selector: 'app-sections-group-info',
+ templateUrl: './info.section.component.html',
+ styleUrls: ['./info.section.component.scss'],
+
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class InfoSectionComponent implements OnInit, OnDestroy {
+ @Input()
+ set userSeq(seq: string) {
+ this._userSeq = seq;
+ this.getInfoByUser(seq);
+ }
+ get userSeq(): string {
+ return this._userSeq;
+ }
+ // tslint:disable-next-line: variable-name
+ _userSeq: string;
+
+ versionInfo2Res: VersionInfo2Response;
+ loginRes: LoginResponse;
+ isMe = false;
+
+ defaultProfileImage: string;
+ defaultProfileImageMulti: string;
+
+ roomList: RoomInfo[];
+ roomUsersDictionary: Dictionary;
+ roomUsersShortDictionary: Dictionary;
+ filteredRoomList: RoomInfo[] = [];
+ allimRoomList: RoomInfo[] = [];
+
+ organizationTranslate: OrganizationTranslate;
+
+ private ngOnDestroySubject = new Subject();
+
+ constructor(
+ private appChatService: AppChatService,
+ private i18nService: I18nService,
+ private translateService: TranslateService,
+ private store: Store,
+ private changeDetectorRef: ChangeDetectorRef
+ ) {
+ // language setting
+ this.translateService.setDefaultLang(this.i18nService.currentLng);
+ this.translateService.use(this.i18nService.currentLng);
+ this.organizationTranslate = new OrganizationTranslate(
+ this.translateService,
+ this.changeDetectorRef
+ );
+
+ // default image setting
+ this.defaultProfileImage = this.appChatService.defaultProfileImage;
+ this.defaultProfileImageMulti = this.appChatService.defaultProfileImage;
+ }
+
+ ngOnInit(): void {
+ this.store
+ .pipe(
+ takeUntil(this.ngOnDestroySubject),
+ select(ConfigurationSelector.versionInfo2Response)
+ )
+ .subscribe((versionInfo2Res) => {
+ this.versionInfo2Res = versionInfo2Res;
+ });
+
+ this.store
+ .pipe(takeUntil(this.ngOnDestroySubject), select(LoginSelector.loginRes))
+ .subscribe((loginRes) => {
+ this.loginRes = loginRes;
+
+ if (
+ (!!this.userSeq && this.userSeq === loginRes.userSeq) ||
+ !this.userSeq
+ ) {
+ this.isMe = true;
+ } else {
+ this.isMe = false;
+ }
+ });
+
+ combineLatest([
+ this.store.pipe(select(RoomSelector.rooms)),
+ this.store.pipe(
+ select(
+ (state: any) =>
+ state.chat.room.roomUsers.entities as Dictionary
+ )
+ ),
+ this.store.pipe(
+ select(
+ (state: any) =>
+ state.chat.room.roomUsersShort.entities as Dictionary<
+ RoomUserShortMap
+ >
+ )
+ )
+ ])
+ .pipe(takeUntil(this.ngOnDestroySubject))
+ .subscribe(([rooms, roomUsers, roomUsersShort]) => {
+ rooms = (rooms || []).filter((info) => info.isJoinRoom);
+ this.roomList = rooms.sort((a, b) =>
+ a.finalEventDate < b.finalEventDate
+ ? 1
+ : a.finalEventDate > b.finalEventDate
+ ? -1
+ : 0
+ );
+
+ this.roomUsersDictionary = roomUsers;
+ this.roomUsersShortDictionary = roomUsersShort;
+
+ if (!!rooms && rooms.length > 0) {
+ this.getInfoByUser(this.userSeq);
+ }
+
+ this.changeDetectorRef.detectChanges();
+ });
+ }
+
+ ngOnDestroy(): void {
+ if (!!this.ngOnDestroySubject) {
+ this.ngOnDestroySubject.complete();
+ }
+ }
+
+ getInfoByUser(userSeq: string | number): void {
+ // is Me check
+ if (!!userSeq && !!this.loginRes && !!this.loginRes.userInfo) {
+ if (this.loginRes.userSeq + '' === userSeq + '') {
+ this.isMe = true;
+
+ // Allim room list setting
+ if (!!this.roomList && this.roomList.length > 0) {
+ this.allimRoomList = this.roomList.filter((roomInfo) => {
+ return (
+ roomInfo.roomType === RoomType.Bot ||
+ roomInfo.roomType === RoomType.Allim
+ );
+ });
+ }
+ } else {
+ this.isMe = false;
+ }
+ }
+
+ // list setting.
+ if (!!this.roomList && this.roomList.length > 0) {
+ this.filteredRoomList = this.roomList.filter((roomInfo) => {
+ if (!!this.isMe) {
+ // mine
+ return roomInfo.noReadCnt > 0;
+ } else {
+ // others
+ const roomUsers = !!this.roomUsersDictionary
+ ? this.roomUsersDictionary[roomInfo.roomId]
+ : undefined;
+ const roomUsersShort = !!this.roomUsersShortDictionary
+ ? this.roomUsersShortDictionary[roomInfo.roomId]
+ : undefined;
+
+ let users = [];
+ if (!!roomUsers && roomUsers.userInfos.length > 0) {
+ users = roomUsers.userInfos;
+ } else if (!!roomUsersShort && roomUsersShort.userInfos.length > 0) {
+ users = roomUsersShort.userInfos;
+ }
+
+ return (
+ users.filter(
+ (userInfo) => Number(userInfo.seq + '') === Number(userSeq + '')
+ ).length > 0
+ );
+ }
+ });
+ }
+
+ this.changeDetectorRef.detectChanges();
+ }
+
+ getRoomName(roomInfo: RoomInfo): string {
+ if (!roomInfo) {
+ return '';
+ }
+
+ const roomName = this.appChatService.getRoomName(
+ this.organizationTranslate,
+ this.loginRes,
+ roomInfo,
+ this.roomUsersDictionary,
+ this.roomUsersShortDictionary
+ );
+
+ return roomName;
+ }
+
+ getRoomProfileImage(roomInfo: RoomInfo): string {
+ let roomImage = '';
+ if (!!roomInfo) {
+ roomImage = this.appChatService.getRoomProfileImage(
+ roomInfo,
+ this.loginRes,
+ this.roomUsersDictionary,
+ this.roomUsersShortDictionary
+ );
+ }
+
+ return roomImage;
+ }
+
+ onClickRoomItem(event: MouseEvent, roomInfo: RoomInfo): void {
+ event.preventDefault();
+ event.stopPropagation();
+
+ console.log('OPEN CHAT ROOM / Group / Main');
+ }
+}
diff --git a/src/app/sections/group/components/list.section.component.html b/src/app/sections/group/components/list.section.component.html
index 53b98af..5821dba 100644
--- a/src/app/sections/group/components/list.section.component.html
+++ b/src/app/sections/group/components/list.section.component.html
@@ -1,37 +1,27 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
diff --git a/src/app/sections/group/components/list.section.component.scss b/src/app/sections/group/components/list.section.component.scss
index 356b3ad..63a93c1 100644
--- a/src/app/sections/group/components/list.section.component.scss
+++ b/src/app/sections/group/components/list.section.component.scss
@@ -1,2 +1,7 @@
.list-container {
}
+.search-wrpper {
+ overflow: auto;
+ position: relative;
+ height: 350px;
+}
diff --git a/src/app/sections/group/components/list.section.component.ts b/src/app/sections/group/components/list.section.component.ts
index 458b7f3..9692e36 100644
--- a/src/app/sections/group/components/list.section.component.ts
+++ b/src/app/sections/group/components/list.section.component.ts
@@ -1,12 +1,16 @@
-import { Observable, Subject, combineLatest } from 'rxjs';
-import { filter, takeUntil } from 'rxjs/operators';
+import { Subject, of } from 'rxjs';
+import { takeUntil, take, map, catchError } from 'rxjs/operators';
import {
Component,
OnInit,
OnDestroy,
ChangeDetectionStrategy,
- ChangeDetectorRef
+ ChangeDetectorRef,
+ Input,
+ ViewChild,
+ EventEmitter,
+ Output
} from '@angular/core';
import { Store, select } from '@ngrx/store';
@@ -18,21 +22,44 @@ import {
CdkVirtualScrollViewport
} from '@angular/cdk/scrolling';
-import { VersionInfo2Response } from '@ucap/api-public';
-import { Company } from '@ucap/api-external';
import { LoginResponse } from '@ucap/protocol-authentication';
import { UserInfo, GroupDetailData } from '@ucap/protocol-sync';
import { LogService } from '@ucap/ng-logger';
-import { NodeType } from '@ucap/ng-ui-group';
+import { ExpansionComponent as AppExpansionComponent } from '@app/ucap/group/components/expansion.component';
import { SessionStorageService } from '@ucap/ng-web-storage';
import { LoginSelector } from '@ucap/ng-store-authentication';
-import { CompanySelector } from '@ucap/ng-store-organization';
-import { BuddySelector, GroupSelector } from '@ucap/ng-store-group';
+import { GroupActions } from '@ucap/ng-store-group';
+import { I18nService } from '@ucap/ng-i18n';
import { AppAuthenticationService } from '@app/services/app-authentication.service';
-import { AppKey } from '@app/types/app-key.type';
-import { LoginSession } from '@app/models/login-session';
+import { QueryProtocolService } from '@ucap/ng-protocol-query';
+import {
+ UserInfoSS,
+ UserInfoF,
+ UserInfoDN,
+ DeptSearchType
+} from '@ucap/protocol-query';
+import { UserInfo as RoomUserInfo } from '@ucap/protocol-room';
+
+import { Router } from '@angular/router';
+import { MatDialog } from '@angular/material/dialog';
+import {
+ ConfirmDialogComponent,
+ ConfirmDialogData,
+ ConfirmDialogResult,
+ AlertDialogComponent,
+ AlertDialogData,
+ AlertDialogResult
+} from '@ucap/ng-ui';
+import { PresenceActions, PresenceSelector } from '@ucap/ng-store-organization';
+
+export type UserInfoTypes =
+ | UserInfo
+ | UserInfoSS
+ | UserInfoF
+ | UserInfoDN
+ | RoomUserInfo;
export class GroupVirtualScrollStrategy extends FixedSizeVirtualScrollStrategy {
constructor() {
@@ -53,116 +80,69 @@ export class GroupVirtualScrollStrategy extends FixedSizeVirtualScrollStrategy {
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ListSectionComponent implements OnInit, OnDestroy {
- loginSession: LoginSession;
- versionInfo2Res: VersionInfo2Response;
+ @Input()
+ set searchObj(obj: {
+ isShowSearch: boolean;
+ companyCode: string;
+ searchWord: string;
+ }) {
+ this._searchObj = obj;
+ if (obj.isShowSearch && obj.searchWord.localeCompare('') !== 0) {
+ this.onOrganizationTenantSearch(obj);
+ } else {
+ this._searchObj.isShowSearch = false;
+ this.searchUserInfos = [];
+ }
+ }
+
+ get searchObj() {
+ return this._searchObj;
+ }
+ _searchObj: any;
+
+ @Input()
+ checkable = false;
+
+ @Input()
+ showType: string;
+
+ @Output()
+ checkUser = new EventEmitter<{
+ isChecked: boolean;
+ userInfo: any;
+ }>();
+
+ @ViewChild('appGroupExpansion', { static: false })
+ appGroupExpansion: AppExpansionComponent;
+
loginRes: LoginResponse;
- companyList: Company[];
- displayOrder: NodeType[] = [
- NodeType.Profile,
- NodeType.Favorite,
- NodeType.Buddy,
- NodeType.Default
- ];
-
- profile: UserInfo;
- favorites: UserInfo[];
- groupBuddies: { group: GroupDetailData; buddyList: UserInfo[] }[];
+ searchUserInfos: UserInfoSS[] = [];
private ngOnDestroySubject = new Subject
();
constructor(
+ private router: Router,
private appAuthenticationService: AppAuthenticationService,
private sessionStorageService: SessionStorageService,
private store: Store,
private changeDetectorRef: ChangeDetectorRef,
- private logService: LogService
- ) {}
+ private i18nService: I18nService,
+ private logService: LogService,
+ private queryProtocolService: QueryProtocolService,
+ public dialog: MatDialog
+ ) {
+ this.i18nService.setDefaultNamespace('group');
+ }
ngOnInit(): void {
this.ngOnDestroySubject = new Subject();
- this.sessionStorageService
- .get$(AppKey.VerInfoResponse)
- .pipe(takeUntil(this.ngOnDestroySubject))
- .subscribe(
- (versionInfo2Response) => (this.versionInfo2Res = versionInfo2Response)
- );
-
- this.appAuthenticationService
- .getLoginSession$()
- .pipe(takeUntil(this.ngOnDestroySubject))
- .subscribe((loginSession) => (this.loginSession = loginSession));
-
this.store
.pipe(takeUntil(this.ngOnDestroySubject), select(LoginSelector.loginRes))
.subscribe((loginRes) => {
this.loginRes = loginRes;
});
-
- this.store
- .pipe(
- takeUntil(this.ngOnDestroySubject),
- select(CompanySelector.companyList)
- )
- .subscribe((companyList) => {
- this.companyList = companyList;
- });
-
- combineLatest([
- this.store.pipe(select(BuddySelector.buddies)),
- this.store.pipe(select(GroupSelector.groups))
- ])
- .pipe(takeUntil(this.ngOnDestroySubject))
- .subscribe(([buddies, groups]) => {
- buddies = buddies || [];
- groups = groups || [];
-
- const favorites = buddies
- .filter((buddy) => buddy.isFavorit)
- .sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
-
- if (!!favorites && 0 < favorites.length) {
- this.favorites = favorites;
- this.changeDetectorRef.markForCheck();
- }
-
- const tempOrder: GroupDetailData[] = [];
- let defaultGroup: GroupDetailData;
- const buddyGroup: GroupDetailData[] = [];
- groups.forEach((group) => {
- if (0 === group.seq) {
- defaultGroup = group;
- } else {
- buddyGroup.push(group);
- }
- });
-
- tempOrder.push(
- ...buddyGroup.sort((a, b) =>
- a.name < b.name ? -1 : a.name > b.name ? 1 : 0
- )
- );
-
- if (!!defaultGroup) {
- tempOrder.push(defaultGroup);
- }
-
- groups = tempOrder;
-
- if (!!groups && 0 < groups.length) {
- this.groupBuddies = [];
- for (const group of groups) {
- this.groupBuddies.push({
- group,
- buddyList: buddies.filter((buddy) => {
- return -1 < group.userSeqs.indexOf(String(buddy.seq));
- })
- });
- }
- this.changeDetectorRef.detectChanges();
- }
- });
}
ngOnDestroy(): void {
@@ -170,4 +150,196 @@ export class ListSectionComponent implements OnInit, OnDestroy {
this.ngOnDestroySubject.complete();
}
}
+
+ onOrganizationTenantSearch(obj: {
+ isShowSearch: boolean;
+ companyCode: string;
+ searchWord: string;
+ }) {
+ const searchUserInfos: UserInfoSS[] = [];
+
+ this.queryProtocolService
+ .deptUser({
+ divCd: 'GRP',
+ companyCode: this._searchObj.companyCode,
+ searchRange: DeptSearchType.All,
+ search: this._searchObj.searchWord,
+ senderCompanyCode: this.loginRes.userInfo.companyCode,
+ senderEmployeeType: this.loginRes.userInfo.employeeType
+ })
+ .pipe(
+ map((resObj) => {
+ const userInfos = resObj.userInfos;
+
+ searchUserInfos.push(...userInfos);
+ // 검색 결과 처리.
+ this.searchUserInfos = searchUserInfos.sort((a, b) =>
+ a.name < b.name ? -1 : a.name > b.name ? 1 : 0
+ );
+ // this.searchProcessing = false;
+
+ // 검색 결과에 따른 프레즌스 조회.
+ const userSeqList: string[] = [];
+ this.searchUserInfos.map((user) => userSeqList.push(user.seq));
+ this.changeDetectorRef.markForCheck();
+
+ if (userSeqList.length > 0) {
+ this.store.dispatch(
+ PresenceActions.bulkInfo({
+ divCd: 'groupSrch',
+ userSeqs: userSeqList
+ })
+ );
+ }
+ }),
+ catchError((error) => {
+ // this.searchProcessing = false;
+ return of(error);
+ })
+ )
+ .subscribe();
+ }
+
+ onCheckUser(params: { isChecked: boolean; userInfo: any }) {
+ this.checkUser.emit(params);
+ }
+
+ onClickUser(params: { event: MouseEvent; userInfo: UserInfo }) {
+ params.event.preventDefault();
+ params.event.stopPropagation();
+
+ this.router.navigate(
+ [
+ 'group',
+ {
+ outlets: { content: 'index' }
+ }
+ ],
+ {
+ queryParams: { id: params.userInfo.seq }
+ }
+ );
+ }
+
+ onClickSearchUser(event: MouseEvent, userInfo: UserInfo) {
+ this.onClickUser({ event, userInfo });
+ }
+ onExpandMore() {
+ this.appGroupExpansion.onExpandMore();
+ }
+ onExpandLess() {
+ this.appGroupExpansion.onExpandLess();
+ }
+
+ onProfileMenu(event) {
+ console.log(event);
+ }
+ onSelectGroupMenu(params: { menuType: string; group: GroupDetailData }) {
+ switch (params.menuType) {
+ case 'CHAT':
+ {
+ this.dialog.open<
+ AlertDialogComponent,
+ AlertDialogData,
+ AlertDialogResult
+ >(AlertDialogComponent, {
+ data: {
+ title: '그룹대화',
+ html: '그룹대화'
+ }
+ });
+ }
+ break;
+ case 'SEND_MESSAGE':
+ {
+ this.dialog.open<
+ AlertDialogComponent,
+ AlertDialogData,
+ AlertDialogResult
+ >(AlertDialogComponent, {
+ data: {
+ title: '그룹쪽지',
+ html: '그룹쪽지'
+ }
+ });
+ }
+ break;
+ case 'RENAME':
+ {
+ }
+ break;
+ case 'MANAGE_MEMBER':
+ {
+ }
+ break;
+ case 'DELETE':
+ {
+ const dialogRef = this.dialog.open<
+ ConfirmDialogComponent,
+ ConfirmDialogData,
+ ConfirmDialogResult
+ >(ConfirmDialogComponent, {
+ data: {
+ title: this.i18nService.t('moreMenu.group.removeGroup'),
+ html: this.i18nService.t('moreMenu.confirm.removeGroup')
+ }
+ });
+ dialogRef
+ .afterClosed()
+ .pipe(take(1))
+ .subscribe((result) => {
+ if (!!result && !!result.choice) {
+ this.store.dispatch(GroupActions.del({ group: params.group }));
+ }
+ });
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ // onEditGroupName(params: { editName: string; group: GroupDetailData }) {
+ // if (params.editName.localeCompare('') === 0) {
+ // const dialogRef = this.dialog.open<
+ // AlertDialogComponent,
+ // AlertDialogData,
+ // AlertDialogResult
+ // >(AlertDialogComponent, {
+ // data: {
+ // title: this.i18nService.t('moreMenu.error.label'),
+ // html: this.i18nService.t('moreMenu.error.requireName')
+ // }
+ // });
+ // dialogRef
+ // .afterClosed()
+ // .pipe(
+ // take(1),
+ // map((result) => {}),
+ // catchError((err) => {
+ // return of(err);
+ // })
+ // )
+ // .subscribe();
+ // return;
+ // }
+ // this.store.dispatch(
+ // GroupActions.update({
+ // req: {
+ // groupSeq: params.group.seq,
+ // groupName: params.editName,
+ // userSeqs: params.group.userSeqs
+ // }
+ // })
+ // );
+ // console.log(params.editName);
+ // }
+
+ getStatusBulkInfo(buddy: UserInfoTypes) {
+ return this.store.pipe(
+ select(PresenceSelector.selectEntitiesStatusBulkInfo),
+ map((statusBulkInfo) =>
+ !!statusBulkInfo ? statusBulkInfo[buddy.seq] : undefined
+ )
+ );
+ }
}
diff --git a/src/app/sections/group/components/profile.section.component.html b/src/app/sections/group/components/profile.section.component.html
new file mode 100644
index 0000000..6adfd3c
--- /dev/null
+++ b/src/app/sections/group/components/profile.section.component.html
@@ -0,0 +1 @@
+
diff --git a/src/app/sections/group/components/profile.section.component.scss b/src/app/sections/group/components/profile.section.component.scss
new file mode 100644
index 0000000..8a387b9
--- /dev/null
+++ b/src/app/sections/group/components/profile.section.component.scss
@@ -0,0 +1,2 @@
+.profile-container {
+}
diff --git a/src/app/sections/group/components/profile.section.component.spec.ts b/src/app/sections/group/components/profile.section.component.spec.ts
new file mode 100644
index 0000000..ed3fe11
--- /dev/null
+++ b/src/app/sections/group/components/profile.section.component.spec.ts
@@ -0,0 +1,32 @@
+import { TestBed, async } from '@angular/core/testing';
+import { RouterTestingModule } from '@angular/router/testing';
+import { ProfileSectionComponent } from './profile.section.component';
+
+describe('app::sections::group::ProfileSectionComponent', () => {
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ imports: [RouterTestingModule],
+ declarations: [ProfileSectionComponent]
+ }).compileComponents();
+ }));
+
+ it('should create the app', () => {
+ const fixture = TestBed.createComponent(ProfileSectionComponent);
+ const app = fixture.componentInstance;
+ expect(app).toBeTruthy();
+ });
+
+ it(`should have as title 'ucap-lg-web'`, () => {
+ const fixture = TestBed.createComponent(ProfileSectionComponent);
+ const app = fixture.componentInstance;
+ });
+
+ it('should render title', () => {
+ const fixture = TestBed.createComponent(ProfileSectionComponent);
+ fixture.detectChanges();
+ const compiled = fixture.nativeElement;
+ expect(compiled.querySelector('.content span').textContent).toContain(
+ 'ucap-lg-web app is running!'
+ );
+ });
+});
diff --git a/src/app/sections/group/components/profile.section.component.ts b/src/app/sections/group/components/profile.section.component.ts
new file mode 100644
index 0000000..ec3bd62
--- /dev/null
+++ b/src/app/sections/group/components/profile.section.component.ts
@@ -0,0 +1,142 @@
+import {
+ Component,
+ OnInit,
+ OnDestroy,
+ ChangeDetectionStrategy,
+ ChangeDetectorRef,
+ Input
+} from '@angular/core';
+
+import { AppKey } from '@app/types/app-key.type';
+import { LoginSession } from '@app/models/login-session';
+import { Subject } from 'rxjs';
+import { Store, select } from '@ngrx/store';
+import { takeUntil, take, map } from 'rxjs/operators';
+import {
+ LoginSelector,
+ AuthorizationSelector
+} from '@ucap/ng-store-authentication';
+import { LoginResponse } from '@ucap/protocol-authentication';
+import { QueryProtocolService } from '@ucap/ng-protocol-query';
+import { UserInfoSS, AuthResponse } from '@ucap/protocol-query';
+
+@Component({
+ selector: 'app-sections-group-profile',
+ templateUrl: './profile.section.component.html',
+ styleUrls: ['./profile.section.component.scss'],
+
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class ProfileSectionComponent implements OnInit, OnDestroy {
+ @Input()
+ set userSeq(seq: string) {
+ this._userSeq = seq;
+ this.getUserInfo(seq);
+ }
+ get userSeq(): string {
+ return this._userSeq;
+ }
+ // tslint:disable-next-line: variable-name
+ _userSeq: string;
+
+ loginRes: LoginResponse;
+ authRes: AuthResponse;
+ isMe = false;
+
+ profileUserInfo: UserInfoSS;
+
+ private ngOnDestroySubject = new Subject();
+
+ constructor(
+ private store: Store,
+ private queryProtocolService: QueryProtocolService,
+ private changeDetectorRef: ChangeDetectorRef
+ ) {}
+
+ ngOnInit(): void {
+ this.store
+ .pipe(takeUntil(this.ngOnDestroySubject), select(LoginSelector.loginRes))
+ .subscribe((loginRes) => {
+ this.loginRes = loginRes;
+
+ if (!!loginRes) {
+ const seq = !!this.userSeq ? this.userSeq : loginRes.userSeq;
+ this.getUserInfo(seq);
+ }
+
+ if (!!this.userSeq && this.userSeq === loginRes.userSeq) {
+ this.isMe = true;
+ } else {
+ this.isMe = false;
+ }
+ });
+
+ this.store
+ .pipe(
+ takeUntil(this.ngOnDestroySubject),
+ select(AuthorizationSelector.authResponse)
+ )
+ .subscribe((authRes) => {
+ this.authRes = authRes;
+ });
+ }
+
+ ngOnDestroy(): void {
+ if (!!this.ngOnDestroySubject) {
+ this.ngOnDestroySubject.complete();
+ }
+ }
+
+ getUserInfo(userSeq: string | number): void {
+ if (!!userSeq && !!this.loginRes && !!this.loginRes.userInfo) {
+ this.queryProtocolService
+ .dataUser({
+ divCd: 'OPENPROF',
+ seq: Number(userSeq + ''),
+ senderCompanyCode: this.loginRes.userInfo.companyCode,
+ senderEmployeeType: this.loginRes.userInfo.employeeType
+ })
+ .pipe(
+ take(1),
+ map((res) => {
+ if (this.loginRes.userSeq + '' === userSeq + '') {
+ this.isMe = true;
+ } else {
+ this.isMe = false;
+ }
+
+ this.profileUserInfo = res.userInfo;
+ this.changeDetectorRef.detectChanges();
+ })
+ )
+ .subscribe();
+ }
+ }
+
+ onToggleBuddy(param: { userInfo: UserInfoSS; isBuddy: boolean }) {
+ console.log('onToggleBuddy : ', param);
+ }
+ onToggleFavorite(param: { userInfo: UserInfoSS; isFavorite: boolean }) {
+ console.log('onToggleFavorite : ', param);
+ }
+
+ onOpenChat(userInfo: UserInfoSS) {
+ console.log('onOpenChat : ', userInfo);
+ }
+
+ onSendCall(calleeNum: string) {
+ console.log('onSendCall : ', calleeNum);
+ }
+
+ onSendSms(calleenum: string) {
+ console.log('onSendSms : ', calleenum);
+ }
+
+ onCreateConference(userSeq: number) {
+ console.log('onCreateConference : ', userSeq);
+ }
+
+ onSendMessage(userInfo: UserInfoSS) {
+ console.log('onSendMessage : ', userInfo);
+ }
+}
diff --git a/src/app/sections/group/components/search.section.component.html b/src/app/sections/group/components/search.section.component.html
index 0cafbea..bf0c3f5 100644
--- a/src/app/sections/group/components/search.section.component.html
+++ b/src/app/sections/group/components/search.section.component.html
@@ -1,3 +1,5 @@
-
- search section of group
-
+
diff --git a/src/app/sections/group/components/search.section.component.ts b/src/app/sections/group/components/search.section.component.ts
index 6485ad8..fe5dca7 100644
--- a/src/app/sections/group/components/search.section.component.ts
+++ b/src/app/sections/group/components/search.section.component.ts
@@ -1,16 +1,44 @@
-import { Component, OnInit, OnDestroy } from '@angular/core';
+import {
+ Component,
+ OnInit,
+ OnDestroy,
+ Output,
+ EventEmitter,
+ ChangeDetectionStrategy
+} from '@angular/core';
import { LogService } from '@ucap/ng-logger';
+import { I18nService } from '@ucap/ng-i18n';
@Component({
selector: 'app-sections-group-search',
templateUrl: './search.section.component.html',
- styleUrls: ['./search.section.component.scss']
+ styleUrls: ['./search.section.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush
})
export class SearchSectionComponent implements OnInit, OnDestroy {
- constructor(private logService: LogService) {}
+ @Output()
+ keyDownEnter = new EventEmitter<{
+ companyCode: string;
+ searchWord: string;
+ }>();
+
+ @Output()
+ searchCancel = new EventEmitter();
+
+ constructor(
+ private logService: LogService,
+ private i18nService: I18nService
+ ) {}
ngOnInit(): void {}
ngOnDestroy(): void {}
+
+ onKeyDownEnter(params: { companyCode: string; searchWord: string }) {
+ this.keyDownEnter.emit(params);
+ }
+ onClickCancel() {
+ this.searchCancel.emit();
+ }
}
diff --git a/src/app/sections/group/components/select-user.section.component.html b/src/app/sections/group/components/select-user.section.component.html
new file mode 100644
index 0000000..a93b680
--- /dev/null
+++ b/src/app/sections/group/components/select-user.section.component.html
@@ -0,0 +1,131 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ 그룹
+
+
+
+
+
+
+
+ 조직도
+
+
+
+
+
+
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ userInfo.name }}
+ clear
+
+
+
+
+
+ = 300 ? 'text-warn-color' : ''">
+ {{ selectedUserList.length }} / 300
+
+
+
+ = 300"
+ >
+
+
+
+
+
+ {{ selectedUserList.length }}
+
+
+
+
diff --git a/src/app/sections/group/components/select-user.section.component.scss b/src/app/sections/group/components/select-user.section.component.scss
new file mode 100644
index 0000000..3d4537a
--- /dev/null
+++ b/src/app/sections/group/components/select-user.section.component.scss
@@ -0,0 +1,12 @@
+.container {
+ overflow: hidden;
+ .tap-container {
+ height: 300px;
+ }
+
+ .organization-tree {
+ width: 100%;
+ height: calc(100% - 30px);
+ padding-bottom: 10px;
+ }
+}
diff --git a/src/app/sections/group/components/select-user.section.component.spec.ts b/src/app/sections/group/components/select-user.section.component.spec.ts
new file mode 100644
index 0000000..19c9d4c
--- /dev/null
+++ b/src/app/sections/group/components/select-user.section.component.spec.ts
@@ -0,0 +1,26 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { By } from '@angular/platform-browser';
+import { DebugElement } from '@angular/core';
+
+import { SelectUserSectionComponent } from './select-user.section.component';
+
+describe('ucap::ui-organization::SelectUserSectionComponent', () => {
+ let component: SelectUserSectionComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [SelectUserSectionComponent]
+ }).compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(SelectUserSectionComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/sections/group/components/select-user.section.component.ts b/src/app/sections/group/components/select-user.section.component.ts
new file mode 100644
index 0000000..e853572
--- /dev/null
+++ b/src/app/sections/group/components/select-user.section.component.ts
@@ -0,0 +1,237 @@
+import {
+ Component,
+ OnInit,
+ OnDestroy,
+ ChangeDetectionStrategy,
+ ChangeDetectorRef,
+ Output,
+ EventEmitter,
+ Input
+} from '@angular/core';
+
+import { Store, select } from '@ngrx/store';
+import { takeUntil, map, catchError } from 'rxjs/operators';
+
+import { Subject, combineLatest, of } from 'rxjs';
+import { AppAuthenticationService } from '@app/services/app-authentication.service';
+import { SelectUserDialogType } from '@app/types';
+import { QueryProtocolService } from '@ucap/ng-protocol-query';
+
+import { UserInfo, GroupDetailData } from '@ucap/protocol-sync';
+import {
+ UserInfoSS,
+ UserInfoF,
+ UserInfoDN,
+ DeptSearchType
+} from '@ucap/protocol-query';
+import { UserInfo as RoomUserInfo } from '@ucap/protocol-room';
+import { LoginResponse } from '@ucap/protocol-authentication';
+import { LoginSelector } from '@ucap/ng-store-authentication';
+
+export type UserInfoTypes =
+ | UserInfo
+ | UserInfoSS
+ | UserInfoF
+ | UserInfoDN
+ | RoomUserInfo;
+
+@Component({
+ selector: 'app-group-select-user',
+ templateUrl: './select-user.section.component.html',
+ styleUrls: ['./select-user.section.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class SelectUserSectionComponent implements OnInit, OnDestroy {
+ breadcrumbs: any = [
+ {
+ label: 'LGCNS'
+ },
+ {
+ label: 'IT Helpdesk'
+ },
+ {
+ label: 'Issue Log'
+ }
+ ];
+
+ @Input()
+ isDialog = false;
+
+ @Input()
+ existUsers: UserInfoTypes[];
+
+ @Input()
+ set checkable(check: boolean) {
+ this._checkable = check;
+ }
+ get checkable(): boolean {
+ return this._checkable;
+ }
+ _checkable = false;
+
+ @Output()
+ changeUserList = new EventEmitter();
+
+ @Output()
+ cancel = new EventEmitter();
+
+ @Output()
+ confirm = new EventEmitter();
+
+ isSearch = false;
+ searchWord: string;
+
+ private ngOnDestroySubject = new Subject();
+
+ selectedUserList: UserInfoTypes[] = [];
+ searchUserInfos: UserInfoSS[] = [];
+
+ loginRes: LoginResponse;
+
+ SelectUserDialogType = SelectUserDialogType;
+
+ constructor(
+ private changeDetectorRef: ChangeDetectorRef,
+ private store: Store,
+ private appAuthenticationService: AppAuthenticationService,
+ private queryProtocolService: QueryProtocolService
+ ) {}
+
+ ngOnInit(): void {
+ this.ngOnDestroySubject = new Subject();
+
+ this.store
+ .pipe(takeUntil(this.ngOnDestroySubject), select(LoginSelector.loginRes))
+ .subscribe((loginRes) => {
+ this.loginRes = loginRes;
+ });
+ }
+
+ ngOnDestroy(): void {
+ if (!!this.ngOnDestroySubject) {
+ this.ngOnDestroySubject.complete();
+ }
+ }
+
+ onCheckUser(params: { isChecked: boolean; userInfo: UserInfoTypes }) {
+ console.log(params);
+ if (
+ this.selectedUserList.filter((user) => user.seq === params.userInfo.seq)
+ .length === 0
+ ) {
+ this.selectedUserList = [...this.selectedUserList, params.userInfo];
+ } else {
+ this.selectedUserList = this.selectedUserList.filter(
+ (item) => item.seq !== params.userInfo.seq
+ );
+ }
+
+ this.changeUserList.emit(this.selectedUserList);
+ }
+
+ getCheckedUser(userInfo: UserInfoTypes) {
+ if (!!this.selectedUserList && this.selectedUserList.length > 0) {
+ return (
+ this.selectedUserList.filter((item) => item.seq === userInfo.seq)
+ .length > 0
+ );
+ }
+ return false;
+ }
+
+ onCheckGroup(params: {
+ isChecked: boolean;
+ groupBuddyList: { group: GroupDetailData; buddyList: UserInfo[] };
+ }) {
+ if (params.isChecked) {
+ params.groupBuddyList.buddyList.forEach((item) => {
+ if (
+ this.selectedUserList.filter((user) => user.seq === item.seq)
+ .length === 0
+ ) {
+ this.selectedUserList = [...this.selectedUserList, item];
+ }
+ });
+ } else {
+ this.selectedUserList = this.selectedUserList.filter(
+ (item) =>
+ params.groupBuddyList.buddyList.filter((del) => del.seq === item.seq)
+ .length === 0
+ );
+ }
+ this.changeDetectorRef.markForCheck();
+ }
+ getSelectedUserList(): any[] {
+ return this.selectedUserList;
+ }
+
+ getChipsRemoveYn(userInfo: UserInfoTypes) {
+ if (!!this.existUsers && this.existUsers.length > 0) {
+ return !(
+ this.existUsers.filter((user) => user.seq === userInfo.seq).length > 0
+ );
+ } else {
+ return true;
+ }
+ }
+
+ onClickDeleteUser(userInfo: UserInfoTypes) {
+ this.selectedUserList = this.selectedUserList.filter(
+ (item) => item.seq !== userInfo.seq
+ );
+ this.changeDetectorRef.markForCheck();
+ }
+
+ onChanged(data: { companyCode: string; searchWord: string }) {
+ this.isSearch = true;
+ this.searchWord = data.searchWord;
+
+ const searchUserInfos: UserInfoSS[] = [];
+
+ this.queryProtocolService
+ .deptUser({
+ divCd: 'GRP',
+ companyCode: data.companyCode,
+ searchRange: DeptSearchType.All,
+ search: data.searchWord,
+ senderCompanyCode: this.loginRes.userInfo.companyCode,
+ senderEmployeeType: this.loginRes.userInfo.employeeType
+ })
+ .pipe(
+ map((resObj) => {
+ const userInfos = resObj.userInfos;
+
+ searchUserInfos.push(...userInfos);
+ // 검색 결과 처리.
+ this.searchUserInfos = searchUserInfos.sort((a, b) =>
+ a.name < b.name ? -1 : a.name > b.name ? 1 : 0
+ );
+ // this.searchProcessing = false;
+
+ // 검색 결과에 따른 프레즌스 조회.
+ const userSeqList: number[] = [];
+ this.searchUserInfos.map((user) =>
+ userSeqList.push(Number(user.seq))
+ );
+ this.changeDetectorRef.markForCheck();
+ if (userSeqList.length > 0) {
+ // this.store.dispatch(
+ // StatusStore.bulkInfo({
+ // divCd: 'groupSrch',
+ // userSeqs: userSeqList
+ // })
+ // );
+ }
+ }),
+ catchError((error) => {
+ // this.searchProcessing = false;
+ return of(error);
+ })
+ )
+ .subscribe();
+ }
+
+ onCanceled() {
+ this.isSearch = false;
+ }
+}
diff --git a/src/app/sections/group/dialogs/create.dialog.component.html b/src/app/sections/group/dialogs/create.dialog.component.html
new file mode 100644
index 0000000..1f1ec97
--- /dev/null
+++ b/src/app/sections/group/dialogs/create.dialog.component.html
@@ -0,0 +1,67 @@
+
+
+
+ 그룹 생성
+
+
+
+
+
+
+
+
+
diff --git a/src/app/sections/group/dialogs/create.dialog.component.scss b/src/app/sections/group/dialogs/create.dialog.component.scss
new file mode 100644
index 0000000..cccbc78
--- /dev/null
+++ b/src/app/sections/group/dialogs/create.dialog.component.scss
@@ -0,0 +1,9 @@
+.dialog-container {
+ width: 100%;
+ height: 100%;
+
+ .dialog-body {
+ width: 100%;
+ height: 100%;
+ }
+}
diff --git a/src/app/sections/group/dialogs/create.dialog.component.spec.ts b/src/app/sections/group/dialogs/create.dialog.component.spec.ts
new file mode 100644
index 0000000..6c2d293
--- /dev/null
+++ b/src/app/sections/group/dialogs/create.dialog.component.spec.ts
@@ -0,0 +1,26 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { By } from '@angular/platform-browser';
+import { DebugElement } from '@angular/core';
+
+import { CreateDialogComponent } from './create.dialog.component';
+
+describe('ucap::ui-organization::CreateChatDialogComponent', () => {
+ let component: CreateDialogComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [CreateDialogComponent]
+ }).compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(CreateDialogComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/sections/group/dialogs/create.dialog.component.ts b/src/app/sections/group/dialogs/create.dialog.component.ts
new file mode 100644
index 0000000..072ce45
--- /dev/null
+++ b/src/app/sections/group/dialogs/create.dialog.component.ts
@@ -0,0 +1,153 @@
+import { Subject } from 'rxjs';
+
+import {
+ Component,
+ OnInit,
+ OnDestroy,
+ ChangeDetectionStrategy,
+ ChangeDetectorRef,
+ Inject,
+ Input
+} from '@angular/core';
+
+import { Store } from '@ngrx/store';
+
+import {
+ MatDialogRef,
+ MAT_DIALOG_DATA,
+ MatDialog
+} from '@angular/material/dialog';
+
+import { UserInfo, GroupDetailData } from '@ucap/protocol-sync';
+import { UserInfoSS, UserInfoF, UserInfoDN } from '@ucap/protocol-query';
+import { UserInfo as RoomUserInfo } from '@ucap/protocol-room';
+import { MatStepper } from '@angular/material/stepper';
+import { FormBuilder, FormGroup, Validators } from '@angular/forms';
+import { I18nService } from '@ucap/ng-i18n';
+import { GroupActions } from '@ucap/ng-store-group';
+import {
+ AlertDialogComponent,
+ AlertDialogData,
+ AlertDialogResult
+} from '@ucap/ng-ui';
+
+export type UserInfoTypes =
+ | UserInfo
+ | UserInfoSS
+ | UserInfoF
+ | UserInfoDN
+ | RoomUserInfo;
+
+export interface CreateDialogData {
+ title: string;
+}
+export interface CreateDialogResult {}
+
+@Component({
+ selector: 'app-dialog-group-create',
+ templateUrl: './create.dialog.component.html',
+ styleUrls: ['./create.dialog.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class CreateDialogComponent implements OnInit, OnDestroy {
+ @Input()
+ set groupName(g: string) {
+ this._groupName = g;
+ }
+ get groupName(): string {
+ return this._groupName;
+ }
+ _groupName: string;
+
+ constructor(
+ public dialogRef: MatDialogRef,
+ @Inject(MAT_DIALOG_DATA) public data: CreateDialogData,
+ private changeDetectorRef: ChangeDetectorRef,
+ private store: Store,
+ private formBuilder: FormBuilder,
+ private i18nService: I18nService,
+ public dialog: MatDialog
+ ) {}
+
+ private ngOnDestroySubject: Subject;
+ currentStep = 0;
+ inputForm: FormGroup;
+
+ selectedUserList: UserInfoTypes[] = [];
+
+ ngOnInit(): void {
+ this.ngOnDestroySubject = new Subject();
+
+ this.inputForm = this.formBuilder.group({
+ groupName: [
+ this.groupName,
+ [
+ Validators.required
+ // StringUtil.includes(, CharactorType.Special),
+ // this.checkBanWords(),
+ // this.checkSameName()
+ ]
+ ]
+ });
+ }
+
+ ngOnDestroy(): void {
+ if (!!this.ngOnDestroySubject) {
+ this.ngOnDestroySubject.complete();
+ }
+ }
+
+ onClosed(event: MouseEvent): void {
+ this.dialogRef.close();
+ }
+
+ onCancel(stepper: MatStepper) {
+ if (stepper.selectedIndex > 0) {
+ this.currentStep--;
+ stepper.previous();
+ return;
+ }
+ this.dialogRef.close();
+ }
+ onConfirm(stepper: MatStepper) {
+ const userSeqs: string[] = [];
+
+ this.selectedUserList.map((user) => userSeqs.push(user.seq.toString()));
+ const groupName = this.inputForm.get('groupName').value as string;
+
+ if (!groupName || groupName.localeCompare('') === 0) {
+ this.dialog.open<
+ AlertDialogComponent,
+ AlertDialogData,
+ AlertDialogResult
+ >(AlertDialogComponent, {
+ data: {
+ title: this.i18nService.t('moreMenu.error.label'),
+ html: this.i18nService.t('moreMenu.error.requireName')
+ }
+ });
+ return;
+ }
+
+ this.store.dispatch(
+ GroupActions.create({
+ groupName,
+ targetUserSeqs: userSeqs
+ })
+ );
+
+ this.dialogRef.close();
+ }
+ onCompleteConfirm(stepper: MatStepper) {
+ this.currentStep++;
+ stepper.next();
+ }
+
+ onKeyupGroupName() {
+ this.inputForm.get('groupName').markAsTouched();
+ }
+
+ onChangeUserList(selectedUserList: UserInfoTypes[]) {
+ this.selectedUserList = selectedUserList;
+ }
+}
diff --git a/src/app/sections/group/dialogs/edit-inline-input.dialog.component.html b/src/app/sections/group/dialogs/edit-inline-input.dialog.component.html
new file mode 100644
index 0000000..e657d53
--- /dev/null
+++ b/src/app/sections/group/dialogs/edit-inline-input.dialog.component.html
@@ -0,0 +1,20 @@
+
+
+ {{ data.curValue }}
+
+
+
diff --git a/src/app/sections/group/dialogs/edit-inline-input.dialog.component.scss b/src/app/sections/group/dialogs/edit-inline-input.dialog.component.scss
new file mode 100644
index 0000000..42e0cbf
--- /dev/null
+++ b/src/app/sections/group/dialogs/edit-inline-input.dialog.component.scss
@@ -0,0 +1,14 @@
+.dialog-container {
+ width: 100%;
+ height: 100%;
+ overflow: hidden;
+ .ng-star-inserted {
+ float: left;
+ }
+ mat-dialog-container {
+ padding: 0px !important;
+ background: transparent !important;
+ }
+ .ucap-edit-group-name-dialog {
+ }
+}
diff --git a/src/app/sections/group/dialogs/edit-inline-input.dialog.component.spec.ts b/src/app/sections/group/dialogs/edit-inline-input.dialog.component.spec.ts
new file mode 100644
index 0000000..b6bee23
--- /dev/null
+++ b/src/app/sections/group/dialogs/edit-inline-input.dialog.component.spec.ts
@@ -0,0 +1,24 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { EditInlineInputDialogComponent } from './edit-inline-input.dialog.component';
+
+describe('ucap::ui-organization::EditInlineInputDialogComponent', () => {
+ let component: EditInlineInputDialogComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [EditInlineInputDialogComponent]
+ }).compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(EditInlineInputDialogComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/sections/group/dialogs/edit-inline-input.dialog.component.ts b/src/app/sections/group/dialogs/edit-inline-input.dialog.component.ts
new file mode 100644
index 0000000..0581b16
--- /dev/null
+++ b/src/app/sections/group/dialogs/edit-inline-input.dialog.component.ts
@@ -0,0 +1,120 @@
+import { Subject, of } from 'rxjs';
+
+import {
+ Component,
+ OnInit,
+ OnDestroy,
+ ChangeDetectionStrategy,
+ Inject
+} from '@angular/core';
+
+import { Store } from '@ngrx/store';
+
+import {
+ MatDialogRef,
+ MAT_DIALOG_DATA,
+ MatDialog,
+ MatDialogConfig
+} from '@angular/material/dialog';
+
+import { I18nService } from '@ucap/ng-i18n';
+
+import {
+ AlertDialogComponent,
+ AlertDialogData,
+ AlertDialogResult
+} from '@ucap/ng-ui';
+import { take, map, catchError } from 'rxjs/operators';
+
+export interface EditInlineInputDialogData {
+ title: string;
+ curValue?: string;
+ placeholder: string;
+ left?: number;
+ top?: number;
+}
+export interface EditInlineInputDialogResult {
+ choice: boolean;
+ curValue?: string;
+}
+
+@Component({
+ selector: 'app-dialog-group-edit-inline-input',
+ templateUrl: './edit-inline-input.dialog.component.html',
+ styleUrls: ['./edit-inline-input.dialog.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class EditInlineInputDialogComponent implements OnInit, OnDestroy {
+ constructor(
+ public dialogRef: MatDialogRef<
+ EditInlineInputDialogData,
+ EditInlineInputDialogResult
+ >,
+ @Inject(MAT_DIALOG_DATA) public data: EditInlineInputDialogData,
+
+ private store: Store,
+ private i18nService: I18nService,
+ public dialog: MatDialog
+ ) {}
+
+ private ngOnDestroySubject: Subject;
+
+ ngOnInit(): void {
+ const matDialogConfig: MatDialogConfig = new MatDialogConfig();
+
+ matDialogConfig.position = {
+ left: `${this.data.left}px`,
+ top: `${this.data.top}px`
+ };
+
+ this.dialogRef.updatePosition(matDialogConfig.position);
+
+ this.ngOnDestroySubject = new Subject();
+ }
+
+ ngOnDestroy(): void {
+ if (!!this.ngOnDestroySubject) {
+ this.ngOnDestroySubject.complete();
+ }
+ }
+
+ onClosed(event: MouseEvent): void {
+ this.doAction(false);
+ }
+
+ onApply(inputValue: string) {
+ this.doAction(true, inputValue);
+ }
+
+ doAction(choice: boolean, curValue?: string): void {
+ if (!choice) {
+ this.dialogRef.close({ choice: false });
+ }
+
+ if (curValue.localeCompare('') === 0) {
+ const dialogRef = this.dialog.open<
+ AlertDialogComponent,
+ AlertDialogData,
+ AlertDialogResult
+ >(AlertDialogComponent, {
+ data: {
+ title: this.i18nService.t('moreMenu.error.label'),
+ html: this.i18nService.t('moreMenu.error.requireName')
+ }
+ });
+ dialogRef
+ .afterClosed()
+ .pipe(
+ take(1),
+ map((result) => {}),
+ catchError((err) => {
+ return of(err);
+ })
+ )
+ .subscribe();
+ return;
+ }
+
+ this.dialogRef.close({ choice, curValue });
+ }
+}
diff --git a/src/app/sections/group/dialogs/index.ts b/src/app/sections/group/dialogs/index.ts
new file mode 100644
index 0000000..3b6f510
--- /dev/null
+++ b/src/app/sections/group/dialogs/index.ts
@@ -0,0 +1,4 @@
+import { CreateDialogComponent } from './create.dialog.component';
+import { EditInlineInputDialogComponent } from './edit-inline-input.dialog.component';
+
+export const DIALOGS = [CreateDialogComponent, EditInlineInputDialogComponent];
diff --git a/src/app/sections/group/dialogs/manage.dialog.component.html b/src/app/sections/group/dialogs/manage.dialog.component.html
new file mode 100644
index 0000000..78a0d92
--- /dev/null
+++ b/src/app/sections/group/dialogs/manage.dialog.component.html
@@ -0,0 +1,39 @@
+
+
+
+ {{ data.title }}
+
+
+
+
+
+
+
+
+
diff --git a/src/app/sections/group/dialogs/manage.dialog.component.scss b/src/app/sections/group/dialogs/manage.dialog.component.scss
new file mode 100644
index 0000000..cccbc78
--- /dev/null
+++ b/src/app/sections/group/dialogs/manage.dialog.component.scss
@@ -0,0 +1,9 @@
+.dialog-container {
+ width: 100%;
+ height: 100%;
+
+ .dialog-body {
+ width: 100%;
+ height: 100%;
+ }
+}
diff --git a/src/app/sections/group/dialogs/manage.dialog.component.spec.ts b/src/app/sections/group/dialogs/manage.dialog.component.spec.ts
new file mode 100644
index 0000000..d20bf16
--- /dev/null
+++ b/src/app/sections/group/dialogs/manage.dialog.component.spec.ts
@@ -0,0 +1,26 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { By } from '@angular/platform-browser';
+import { DebugElement } from '@angular/core';
+
+import { CreateDialogComponent } from './create.dialog.component';
+
+describe('ucap::ui-organization::ManageDialogComponent', () => {
+ let component: ManageDialogComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [ManageDialogComponent]
+ }).compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(ManageDialogComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/sections/group/dialogs/manage.dialog.component.ts b/src/app/sections/group/dialogs/manage.dialog.component.ts
new file mode 100644
index 0000000..f20486d
--- /dev/null
+++ b/src/app/sections/group/dialogs/manage.dialog.component.ts
@@ -0,0 +1,98 @@
+import { Subject } from 'rxjs';
+
+import {
+ Component,
+ OnInit,
+ OnDestroy,
+ ChangeDetectionStrategy,
+ ChangeDetectorRef,
+ Inject,
+ Input
+} from '@angular/core';
+
+import { Store } from '@ngrx/store';
+
+import {
+ MatDialogRef,
+ MAT_DIALOG_DATA,
+ MatDialog
+} from '@angular/material/dialog';
+
+import { UserInfo, GroupDetailData } from '@ucap/protocol-sync';
+import { UserInfoSS, UserInfoF, UserInfoDN } from '@ucap/protocol-query';
+import { UserInfo as RoomUserInfo } from '@ucap/protocol-room';
+import { MatStepper } from '@angular/material/stepper';
+import { FormBuilder, FormGroup, Validators } from '@angular/forms';
+import { I18nService } from '@ucap/ng-i18n';
+import { GroupActions } from '@ucap/ng-store-group';
+import {
+ AlertDialogComponent,
+ AlertDialogData,
+ AlertDialogResult
+} from '@ucap/ng-ui';
+
+export type UserInfoTypes =
+ | UserInfo
+ | UserInfoSS
+ | UserInfoF
+ | UserInfoDN
+ | RoomUserInfo;
+
+export interface ManageDialogData {
+ title: string;
+}
+export interface ManageDialogResult {}
+
+@Component({
+ selector: 'app-dialog-group-manage',
+ templateUrl: './manage.dialog.component.html',
+ styleUrls: ['./manage.dialog.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class ManageDialogComponent implements OnInit, OnDestroy {
+ constructor(
+ public dialogRef: MatDialogRef,
+ @Inject(MAT_DIALOG_DATA) public data: ManageDialogData,
+ private changeDetectorRef: ChangeDetectorRef,
+ private store: Store,
+ private formBuilder: FormBuilder,
+ private i18nService: I18nService,
+ public dialog: MatDialog
+ ) {}
+
+ private ngOnDestroySubject: Subject;
+ currentStep = 0;
+
+ selectedUserList: UserInfoTypes[] = [];
+
+ ngOnInit(): void {
+ this.ngOnDestroySubject = new Subject();
+ }
+
+ ngOnDestroy(): void {
+ if (!!this.ngOnDestroySubject) {
+ this.ngOnDestroySubject.complete();
+ }
+ }
+
+ onClosed(event: MouseEvent): void {
+ this.dialogRef.close();
+ }
+
+ onDelete(stepper: MatStepper) {
+ this.dialogRef.close();
+ }
+ onCopy(stepper: MatStepper) {
+ this.dialogRef.close();
+ }
+ onMove(stepper: MatStepper) {}
+
+ onAdd(stepper: MatStepper) {
+ this.currentStep++;
+ stepper.next();
+ }
+
+ onChangeUserList(selectedUserList: UserInfoTypes[]) {
+ this.selectedUserList = selectedUserList;
+ }
+}
diff --git a/src/app/sections/group/group.section.module.ts b/src/app/sections/group/group.section.module.ts
index 0dbed0f..bd2aa1b 100644
--- a/src/app/sections/group/group.section.module.ts
+++ b/src/app/sections/group/group.section.module.ts
@@ -1,33 +1,73 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
+import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { FlexLayoutModule } from '@angular/flex-layout';
+import { MatFormFieldModule } from '@angular/material/form-field';
+import { MatInputModule } from '@angular/material/input';
import { MatCheckboxModule } from '@angular/material/checkbox';
+import { MatTabsModule } from '@angular/material/tabs';
+import { MatChipsModule } from '@angular/material/chips';
+import { MatButtonModule } from '@angular/material/button';
+import { MatStepperModule } from '@angular/material/stepper';
+import { MatCardModule } from '@angular/material/card';
+import { MatMenuModule } from '@angular/material/menu';
+import { MatIconModule } from '@angular/material/icon';
+import { MatSelectModule } from '@angular/material/select';
+
+import { PerfectScrollbarModule } from 'ngx-perfect-scrollbar';
import { I18nModule, UCAP_I18N_NAMESPACE } from '@ucap/ng-i18n';
+import { UiModule } from '@ucap/ng-ui';
+
import { OrganizationUiModule } from '@ucap/ng-ui-organization';
-import { GroupUiModule } from '@ucap/ng-ui-group';
+import { ChatUiModule } from '@ucap/ng-ui-chat';
+
+import { AppLayoutsModule } from '@app/layouts/layouts.module';
+import { AppOrganizationModule } from '@app/ucap/organization/organization.module';
+import { AppGroupModule } from '@app/ucap/group/group.module';
import { COMPONENTS } from './components';
+import { DIALOGS } from './dialogs';
@NgModule({
imports: [
CommonModule,
+ FormsModule,
+ ReactiveFormsModule,
FlexLayoutModule,
+ MatFormFieldModule,
+ MatInputModule,
+ MatIconModule,
+ MatCardModule,
MatCheckboxModule,
+ MatFormFieldModule,
+ MatInputModule,
+ MatSelectModule,
+ MatTabsModule,
+ MatChipsModule,
+ MatButtonModule,
+ MatStepperModule,
+ MatMenuModule,
+ PerfectScrollbarModule,
I18nModule,
+ UiModule,
OrganizationUiModule,
- GroupUiModule
+ ChatUiModule,
+
+ AppLayoutsModule,
+ AppOrganizationModule,
+ AppGroupModule
],
- exports: [...COMPONENTS],
- declarations: [...COMPONENTS],
- entryComponents: [],
+ exports: [...COMPONENTS, ...DIALOGS],
+ declarations: [...COMPONENTS, ...DIALOGS],
+ entryComponents: [...DIALOGS],
providers: [
{
provide: UCAP_I18N_NAMESPACE,
- useValue: ['group']
+ useValue: ['group', 'common']
}
]
})
diff --git a/src/app/sections/organization/components/component-ui/index.ts b/src/app/sections/organization/components/component-ui/index.ts
new file mode 100644
index 0000000..98bcc9b
--- /dev/null
+++ b/src/app/sections/organization/components/component-ui/index.ts
@@ -0,0 +1 @@
+export const COMPONENTS = [];
diff --git a/src/app/sections/organization/components/detail-table.component.html b/src/app/sections/organization/components/detail-table.component.html
new file mode 100644
index 0000000..048f5ec
--- /dev/null
+++ b/src/app/sections/organization/components/detail-table.component.html
@@ -0,0 +1,148 @@
+
+
+
+
+
+
+
+ {{ 'label.selectedUsers' | ucapI18n }}
+ {{ selectedUserList.length }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ userInfo.name }}
+ clear
+
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+
+
diff --git a/src/app/sections/organization/components/detail-table.component.scss b/src/app/sections/organization/components/detail-table.component.scss
new file mode 100644
index 0000000..38e013d
--- /dev/null
+++ b/src/app/sections/organization/components/detail-table.component.scss
@@ -0,0 +1,235 @@
+// @charset 'utf-8';
+// @mixin ellipsis($row) {
+// overflow: hidden;
+// text-overflow: ellipsis;
+// @if $row == 1 {
+// display: block;
+// white-space: nowrap;
+// word-wrap: normal;
+// } @else if $row >= 2 {
+// display: -webkit-box;
+// -webkit-line-clamp: $row;
+// -webkit-box-orient: vertical;
+// word-wrap: break-word;
+// }
+// }
+// @mixin disable-selection {
+// -webkit-touch-callout: none; /* iOS Safari */
+// -webkit-user-select: none; /* Safari */
+// -khtml-user-select: none; /* Konqueror HTML */
+// -moz-user-select: none; /* Firefox */
+// -ms-user-select: none; /* Internet Explorer/Edge */
+// user-select: none; /* Non-prefixed version, currently supported by Chrome and Opera */
+// }
+
+// /*.scrollbar {
+// height: 550px;
+// }*/
+
+// .wrapper {
+// height: 300px;
+// }
+
+// mat-form-field {
+// font-size: 14px;
+// width: 100%;
+// }
+// .list-chip {
+// height: 100px;
+// width: 100%;
+// padding: 10px;
+// border: 1px solid #dddddd;
+// overflow: auto;
+// background-color: #f9f9f9;
+// margin-top: 10px;
+// }
+
+// table {
+// width: 100%;
+// min-width: 600px;
+// table-layout: fixed;
+// }
+
+// th.mat-header-cell {
+// @include disable-selection;
+// position: relative;
+// padding: 0 10px;
+
+// span.ui-column-divider {
+// display: block;
+// position: absolute;
+// top: 10px;
+// right: 0;
+// margin: 0;
+// width: 2px;
+// height: 40px;
+// padding: 0;
+// cursor: initial;
+// border: none;
+// background-color: #d4d4d4;
+
+// &.resizable {
+// cursor: col-resize;
+// }
+// }
+// span {
+// &[mat-sort-header='name'],
+// &[mat-sort-header='grade'] {
+// display: inline-flex;
+// padding-right: 10px;
+// }
+// }
+// &.profileImage {
+// width: 90px;
+// }
+// &.mat-column-checkable {
+// width: 50px;
+// }
+// }
+
+// td.mat-cell {
+// padding: 6px;
+// position: relative;
+
+// div {
+// @include ellipsis(1);
+// }
+// div:nth-chlid(2) {
+// padding-top: 4px;
+// }
+
+// &.profileImage {
+// width: 90px;
+// text-overflow: unset;
+// flex: 0 0 90px;
+
+// .table-item {
+// display: flex;
+// width: 70px;
+// min-width: 70px;
+// font-size: 1em;
+
+// .presence {
+// transform: translateY(6px);
+// }
+// .thumbnail {
+// cursor: pointer;
+// &-mask {
+// display: inline-block;
+// width: 40px;
+// height: 40px;
+// border-radius: 50%;
+// overflow: hidden;
+// margin-right: 0;
+// position: relative;
+// img {
+// width: 40px;
+// height: auto;
+// background-color: #efefef;
+// }
+// }
+// }
+// .marker-mobile-state {
+// position: absolute;
+// background-color: #ffffff;
+// width: 20px;
+// height: 20px;
+// border-radius: 50%;
+// bottom: 0;
+// left: 64px;
+// display: flex;
+// align-items: center;
+// align-content: center;
+// justify-content: center;
+// .mat-icon {
+// font-size: 0.9em;
+// width: 18px;
+// height: 18px;
+// line-height: 18px;
+// min-width: 18px;
+// min-height: 18px;
+// }
+// }
+// }
+// }
+
+// &.mat-column-checkable {
+// padding-left: 10px;
+// flex: 0 0 50px;
+// }
+
+// &.profileInfo {
+// cursor: pointer;
+// flex: 0 0 200px;
+// .baseInfo {
+// display: flex;
+// font-size: 1em;
+// @include ellipsis(1);
+// .name {
+// font-size: 1em;
+// font-weight: 600;
+// }
+// .grade {
+// font-size: 0.86em;
+// color: #666666;
+// }
+// }
+// .deptName {
+// font-size: 0.9em;
+// color: #666666;
+// }
+// }
+
+// .companyName,
+// .workplace,
+// .responsibilities {
+// font-size: 0.86em;
+// font-weight: 600;
+// }
+
+// .hpNumber {
+// cursor: pointer;
+// }
+// .lineNumber {
+// cursor: pointer;
+// }
+// }
+
+// .work-status {
+// display: inline-block;
+// justify-content: center;
+// justify-items: center;
+// color: #ffffff;
+// height: 100%;
+// min-width: 32px;
+// margin-right: 4px;
+// border-radius: 24px;
+// flex: 0 0 auto;
+// font-size: 0.8em;
+// text-align: center;
+// }
+
+// .no-search-result {
+// display: flex;
+// width: 100%;
+// margin-top: 40px;
+// justify-content: center;
+// justify-items: center;
+// font-size: 1.1em;
+// }
+
+// ::ng-deep .integrate-search-org {
+// td.mat-cell {
+// &.profileInfo {
+// cursor: initial !important;
+// }
+// }
+// }
+
+// ::ng-deep .ps__rail-y {
+// z-index: 101;
+// }
+
+// .example-chip-list {
+// width: 100%;
+// }
diff --git a/src/app/sections/organization/components/detail-table.component.spec.ts b/src/app/sections/organization/components/detail-table.component.spec.ts
new file mode 100644
index 0000000..17ac9d1
--- /dev/null
+++ b/src/app/sections/organization/components/detail-table.component.spec.ts
@@ -0,0 +1,24 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { DetailTableComponent } from './detail-table.component';
+
+describe('Organization::DetailTableComponent', () => {
+ let component: DetailTableComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [DetailTableComponent]
+ }).compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(DetailTableComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/sections/organization/components/detail-table.component.ts b/src/app/sections/organization/components/detail-table.component.ts
new file mode 100644
index 0000000..a930a19
--- /dev/null
+++ b/src/app/sections/organization/components/detail-table.component.ts
@@ -0,0 +1,302 @@
+import {
+ Component,
+ ViewChild,
+ OnInit,
+ ChangeDetectorRef,
+ Input,
+ OnDestroy,
+ ChangeDetectionStrategy
+} from '@angular/core';
+import { MatTableDataSource } from '@angular/material/table';
+import { SelectionModel } from '@angular/cdk/collections';
+import { MatSort } from '@angular/material/sort';
+import { Subject, of } from 'rxjs';
+import { map, takeUntil, take, catchError } from 'rxjs/operators';
+import {
+ DeptInfo,
+ DeptSearchType,
+ DeptUserRequest,
+ UserInfoSS,
+ AuthResponse
+} from '@ucap/protocol-query';
+import {
+ DepartmentSelector,
+ PresenceActions
+} from '@ucap/ng-store-organization';
+import { select, Store } from '@ngrx/store';
+import { QueryProtocolService } from '@ucap/ng-protocol-query';
+import {
+ LoginSelector,
+ AuthorizationSelector,
+ ConfigurationSelector
+} from '@ucap/ng-store-authentication';
+import { LoginResponse } from '@ucap/protocol-authentication';
+import {
+ FixedSizeVirtualScrollStrategy,
+ VIRTUAL_SCROLL_STRATEGY,
+ CdkVirtualScrollViewport
+} from '@angular/cdk/scrolling';
+import { VersionInfo2Response } from '@ucap/api-public';
+import { PerfectScrollbarDirective } from 'ngx-perfect-scrollbar';
+
+export class DepartmentUserVirtualScrollStrategy extends FixedSizeVirtualScrollStrategy {
+ constructor() {
+ super(184, 150, 200);
+ }
+}
+
+@Component({
+ selector: 'app-organization-detail-table',
+ templateUrl: './detail-table.component.html',
+ styleUrls: ['./detail-table.component.scss'],
+ providers: [
+ {
+ provide: VIRTUAL_SCROLL_STRATEGY,
+ useClass: DepartmentUserVirtualScrollStrategy
+ }
+ ],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class DetailTableComponent implements OnInit, OnDestroy {
+ @Input()
+ searchData: any;
+
+ @Input()
+ set deptSeq(deptSeq: string) {
+ this._deptSeq = deptSeq;
+ this.getSelectedDeptInfo();
+ }
+ get deptSeq(): string {
+ return this._deptSeq;
+ }
+ // tslint:disable-next-line: variable-name
+ _deptSeq: string;
+
+ versionInfo2Res: VersionInfo2Response;
+ loginRes: LoginResponse;
+ authRes: AuthResponse;
+
+ defaultProfileImage = 'assets/images/ico/img_nophoto.svg';
+
+ departmentInfoList: DeptInfo[];
+ selectedDeptInfo: DeptInfo;
+
+ departmentUserListProcessing = false;
+ departmentUserList: UserInfoSS[] = [];
+ searchUserList: UserInfoSS[] = [];
+
+ selectedUserList: UserInfoSS[] = [];
+
+ @ViewChild('cvsvList', { static: false })
+ cvsvList: CdkVirtualScrollViewport;
+
+ @ViewChild(PerfectScrollbarDirective, { static: false })
+ psDirectiveRef?: PerfectScrollbarDirective;
+
+ private ngOnDestroySubject: Subject;
+
+ constructor(
+ private store: Store,
+ private queryProtocolService: QueryProtocolService,
+ private changeDetectorRef: ChangeDetectorRef
+ ) {}
+
+ ngOnInit() {
+ this.ngOnDestroySubject = new Subject();
+
+ this.store
+ .pipe(
+ takeUntil(this.ngOnDestroySubject),
+ select(ConfigurationSelector.versionInfo2Response)
+ )
+ .subscribe((versionInfo2Res) => {
+ this.versionInfo2Res = versionInfo2Res;
+ });
+
+ this.store
+ .pipe(takeUntil(this.ngOnDestroySubject), select(LoginSelector.loginRes))
+ .subscribe((loginRes) => {
+ this.loginRes = loginRes;
+ });
+
+ this.store
+ .pipe(
+ takeUntil(this.ngOnDestroySubject),
+ select(AuthorizationSelector.authResponse)
+ )
+ .subscribe((authRes) => {
+ this.authRes = authRes;
+ });
+
+ this.store
+ .pipe(
+ takeUntil(this.ngOnDestroySubject),
+ select(DepartmentSelector.departmentInfoList)
+ )
+ .subscribe((departmentInfoList) => {
+ this.departmentInfoList = departmentInfoList;
+ this.getSelectedDeptInfo();
+ });
+ }
+
+ ngOnDestroy(): void {
+ if (!!this.ngOnDestroySubject) {
+ this.ngOnDestroySubject.complete();
+ }
+ }
+
+ getSelectedDeptInfo() {
+ if (
+ !!this.deptSeq &&
+ !!this.departmentInfoList &&
+ this.departmentInfoList.length > 0
+ ) {
+ const existDept = this.departmentInfoList.some((deptInfo) => {
+ if (Number(deptInfo.seq + '') === Number(this.deptSeq + '')) {
+ this.selectedDeptInfo = deptInfo;
+ return true;
+ }
+ });
+
+ if (!!existDept && !!this.loginRes) {
+ this.departmentUserListProcessing = true;
+ const req: DeptUserRequest = {
+ divCd: 'ORG',
+ companyCode: this.loginRes.companyCode,
+ seq: Number(this.deptSeq),
+ search: '',
+ searchRange: DeptSearchType.All,
+ senderCompanyCode: this.loginRes.companyCode,
+ senderEmployeeType: this.loginRes.userInfo.employeeType
+ };
+ this.queryProtocolService
+ .deptUser(req)
+ .pipe(
+ take(1),
+ map((res) => {
+ this.departmentUserList = res.userInfos;
+ this.departmentUserListProcessing = false;
+
+ if (!!this.cvsvList) {
+ this.cvsvList.checkViewportSize();
+ }
+ if (!!this.psDirectiveRef) {
+ this.psDirectiveRef.update();
+ }
+
+ this.changeDetectorRef.detectChanges();
+ }),
+ catchError((error) => {
+ this.departmentUserListProcessing = false;
+ return of(error);
+ })
+ )
+ .subscribe();
+ }
+
+ this.changeDetectorRef.detectChanges();
+ }
+ }
+
+ /** 전체선택 체크여부 */
+ getCheckedAllUser() {
+ if (!this.loginRes) {
+ return false;
+ }
+
+ const compareList: UserInfoSS[] = !!this.searchData.isSearch
+ ? this.searchUserList
+ : this.departmentUserList;
+ if (
+ !compareList ||
+ compareList.length === 0 ||
+ compareList.filter((item) => item.seq !== this.loginRes.userSeq)
+ .length === 0 ||
+ compareList
+ .filter((item) => item.seq !== this.loginRes.userSeq)
+ .filter(
+ (item) =>
+ !(
+ this.selectedUserList.filter((user) => user.seq === item.seq)
+ .length > 0
+ )
+ ).length > 0
+ ) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+ /** 전체선택 이벤트 */
+ onCheckAllUser(value: boolean) {
+ if (!this.loginRes) {
+ return false;
+ }
+
+ const trgtUserList = !!this.searchData.isSearch
+ ? this.searchUserList
+ : this.departmentUserList;
+
+ if (!!value) {
+ this.selectedUserList = trgtUserList.slice();
+ } else {
+ this.selectedUserList = [];
+ }
+
+ this.changeDetectorRef.detectChanges();
+ }
+ /** 개별 체크여부 */
+ getCheckedUser(userInfo: UserInfoSS) {
+ if (!!this.selectedUserList && this.selectedUserList.length > 0) {
+ return (
+ this.selectedUserList.filter((item) => item.seq === userInfo.seq)
+ .length > 0
+ );
+ }
+ return false;
+ }
+ /** 개별선택(토글) 이벤트 */
+ onToggleUser(param: { isChecked: boolean; userInfo: UserInfoSS }) {
+ if (!this.loginRes || param.userInfo.seq === this.loginRes.userSeq) {
+ return;
+ }
+
+ if (
+ !this.selectedUserList.some((user) => user.seq === param.userInfo.seq)
+ ) {
+ this.selectedUserList = [...this.selectedUserList, param.userInfo];
+ } else {
+ this.selectedUserList = this.selectedUserList.filter(
+ (item) => item.seq !== param.userInfo.seq
+ );
+ }
+ this.changeDetectorRef.detectChanges();
+ }
+
+ onClickDeleteUserChips(userInfo: UserInfoSS) {
+ this.selectedUserList = this.selectedUserList.filter(
+ (item) => item.seq !== userInfo.seq
+ );
+ this.changeDetectorRef.detectChanges();
+ }
+
+ onOpenProfile(userInfo: UserInfoSS): void {
+ alert('Open Profile');
+ }
+
+ onClickAddGroup(): void {
+ alert('onClickAddGroup');
+ }
+ onClickChatOpen(): void {
+ alert('onClickChatOpen');
+ }
+ onClickMessage(): void {
+ alert('onClickMessage');
+ }
+ onClickCall(): void {
+ alert('onClickCall');
+ }
+ onClickConference(): void {
+ alert('onClickConference');
+ }
+}
diff --git a/src/app/sections/organization/components/index.ts b/src/app/sections/organization/components/index.ts
new file mode 100644
index 0000000..6855536
--- /dev/null
+++ b/src/app/sections/organization/components/index.ts
@@ -0,0 +1,4 @@
+import { DetailTableComponent } from './detail-table.component';
+import { MemberListComponent } from './member-list.component';
+
+export const COMPONENTS = [DetailTableComponent, MemberListComponent];
diff --git a/src/app/sections/organization/components/member-list.component.html b/src/app/sections/organization/components/member-list.component.html
new file mode 100644
index 0000000..b90e9af
--- /dev/null
+++ b/src/app/sections/organization/components/member-list.component.html
@@ -0,0 +1,34 @@
+
diff --git a/src/app/sections/organization/components/member-list.component.scss b/src/app/sections/organization/components/member-list.component.scss
new file mode 100644
index 0000000..21bee5a
--- /dev/null
+++ b/src/app/sections/organization/components/member-list.component.scss
@@ -0,0 +1,34 @@
+.member-list-container {
+ width: 100%;
+ height: 100%;
+
+ .member-list-body {
+ align-content: space-between;
+ padding: 0 30px;
+ background-color: white;
+
+ .list-header {
+ justify-content: space-between;
+ align-items: center;
+ border-bottom: 2px solid #999999;
+ padding: 12px 0 13px;
+
+ .list-header-title {
+ h5 {
+ font-size: 13px;
+ align-items: center;
+ font-weight: 600;
+ color: #333333;
+ flex-grow: 1;
+ strong {
+ color: #e42f66;
+ }
+ }
+ }
+ .list-header-toolbox {
+ position: absolute;
+ right: 0px;
+ }
+ }
+ }
+}
diff --git a/src/app/sections/organization/components/member-list.component.spec.ts b/src/app/sections/organization/components/member-list.component.spec.ts
new file mode 100644
index 0000000..1e6aed4
--- /dev/null
+++ b/src/app/sections/organization/components/member-list.component.spec.ts
@@ -0,0 +1,24 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { MemberListComponent } from './member-list.component';
+
+describe('app::section::organization::MemberListComponent', () => {
+ let component: MemberListComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [MemberListComponent]
+ }).compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(MemberListComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/sections/organization/components/member-list.component.ts b/src/app/sections/organization/components/member-list.component.ts
new file mode 100644
index 0000000..d7b381b
--- /dev/null
+++ b/src/app/sections/organization/components/member-list.component.ts
@@ -0,0 +1,102 @@
+import {
+ Component,
+ ViewChild,
+ OnInit,
+ ChangeDetectorRef,
+ Input,
+ OnDestroy,
+ ChangeDetectionStrategy
+} from '@angular/core';
+import { MatTableDataSource } from '@angular/material/table';
+import { SelectionModel } from '@angular/cdk/collections';
+import { MatSort } from '@angular/material/sort';
+import { Subject, of } from 'rxjs';
+import { map, takeUntil, take, catchError } from 'rxjs/operators';
+import {
+ DeptInfo,
+ DeptSearchType,
+ DeptUserRequest,
+ UserInfoSS,
+ AuthResponse
+} from '@ucap/protocol-query';
+import {
+ DepartmentSelector,
+ PresenceActions
+} from '@ucap/ng-store-organization';
+import { select, Store } from '@ngrx/store';
+import { QueryProtocolService } from '@ucap/ng-protocol-query';
+import {
+ LoginSelector,
+ AuthorizationSelector,
+ ConfigurationSelector
+} from '@ucap/ng-store-authentication';
+import { LoginResponse } from '@ucap/protocol-authentication';
+import {
+ FixedSizeVirtualScrollStrategy,
+ VIRTUAL_SCROLL_STRATEGY,
+ CdkVirtualScrollViewport
+} from '@angular/cdk/scrolling';
+import { VersionInfo2Response } from '@ucap/api-public';
+import { PerfectScrollbarDirective } from 'ngx-perfect-scrollbar';
+
+export class MemberVirtualScrollStrategy extends FixedSizeVirtualScrollStrategy {
+ constructor() {
+ super(184, 150, 200);
+ }
+}
+
+@Component({
+ selector: 'app-sections-organization-member-list',
+ templateUrl: './member-list.component.html',
+ styleUrls: ['./member-list.component.scss'],
+ providers: [
+ {
+ provide: VIRTUAL_SCROLL_STRATEGY,
+ useClass: MemberVirtualScrollStrategy
+ }
+ ],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class MemberListComponent implements OnInit, OnDestroy {
+ @Input()
+ set searchData(data: {
+ companyCode: string;
+ searchWord: string;
+ isSearch: boolean;
+ }) {
+ this._searchData = data;
+ }
+
+ // tslint:disable-next-line: variable-name
+ _searchData: {
+ companyCode: string;
+ searchWord: string;
+ isSearch: boolean;
+ };
+
+ private ngOnDestroySubject: Subject;
+
+ constructor(
+ private store: Store,
+ private queryProtocolService: QueryProtocolService,
+ private changeDetectorRef: ChangeDetectorRef
+ ) {}
+
+ ngOnInit() {
+ this.ngOnDestroySubject = new Subject();
+ }
+
+ ngOnDestroy(): void {
+ if (!!this.ngOnDestroySubject) {
+ this.ngOnDestroySubject.complete();
+ }
+ }
+
+ onChangedSearch(data: {
+ isSearch: boolean;
+ companyCode: string;
+ searchWord: string;
+ }) {
+ this._searchData = data;
+ }
+}
diff --git a/src/app/sections/organization/components/tree.section.component.html b/src/app/sections/organization/components/tree.section.component.html
new file mode 100644
index 0000000..baa861c
--- /dev/null
+++ b/src/app/sections/organization/components/tree.section.component.html
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/src/app/sections/organization/components/tree.section.component.scss b/src/app/sections/organization/components/tree.section.component.scss
new file mode 100644
index 0000000..8e9eeba
--- /dev/null
+++ b/src/app/sections/organization/components/tree.section.component.scss
@@ -0,0 +1,2 @@
+.tree-container {
+}
diff --git a/src/app/sections/organization/components/tree.section.component.spec.ts b/src/app/sections/organization/components/tree.section.component.spec.ts
new file mode 100644
index 0000000..fc648f7
--- /dev/null
+++ b/src/app/sections/organization/components/tree.section.component.spec.ts
@@ -0,0 +1,32 @@
+import { TestBed, async } from '@angular/core/testing';
+import { RouterTestingModule } from '@angular/router/testing';
+import { TreeSectionComponent } from './tree.section.component';
+
+describe('app::sections::organization::TreeSectionComponent', () => {
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ imports: [RouterTestingModule],
+ declarations: [TreeSectionComponent]
+ }).compileComponents();
+ }));
+
+ it('should create the app', () => {
+ const fixture = TestBed.createComponent(TreeSectionComponent);
+ const app = fixture.componentInstance;
+ expect(app).toBeTruthy();
+ });
+
+ it(`should have as title 'ucap-lg-web'`, () => {
+ const fixture = TestBed.createComponent(TreeSectionComponent);
+ const app = fixture.componentInstance;
+ });
+
+ it('should render title', () => {
+ const fixture = TestBed.createComponent(TreeSectionComponent);
+ fixture.detectChanges();
+ const compiled = fixture.nativeElement;
+ expect(compiled.querySelector('.content span').textContent).toContain(
+ 'ucap-lg-web app is running!'
+ );
+ });
+});
diff --git a/src/app/sections/organization/components/tree.section.component.ts b/src/app/sections/organization/components/tree.section.component.ts
new file mode 100644
index 0000000..5950bc7
--- /dev/null
+++ b/src/app/sections/organization/components/tree.section.component.ts
@@ -0,0 +1,101 @@
+import { Observable, Subject, combineLatest } from 'rxjs';
+import { filter, takeUntil } from 'rxjs/operators';
+
+import {
+ Component,
+ OnInit,
+ OnDestroy,
+ ChangeDetectionStrategy,
+ ChangeDetectorRef
+} from '@angular/core';
+
+import { Store, select } from '@ngrx/store';
+
+import {
+ VirtualScrollStrategy,
+ FixedSizeVirtualScrollStrategy,
+ VIRTUAL_SCROLL_STRATEGY,
+ CdkVirtualScrollViewport
+} from '@angular/cdk/scrolling';
+
+import { VersionInfo2Response } from '@ucap/api-public';
+import { Company } from '@ucap/api-external';
+import { LoginResponse } from '@ucap/protocol-authentication';
+import { UserInfo, GroupDetailData } from '@ucap/protocol-sync';
+import { DeptInfo } from '@ucap/protocol-query';
+
+import { LogService } from '@ucap/ng-logger';
+import { NodeType } from '@ucap/ng-ui-group';
+import { SessionStorageService } from '@ucap/ng-web-storage';
+import {
+ LoginSelector,
+ ConfigurationSelector
+} from '@ucap/ng-store-authentication';
+import { DepartmentSelector } from '@ucap/ng-store-organization';
+import { BuddySelector, GroupSelector } from '@ucap/ng-store-group';
+
+import { AppAuthenticationService } from '@app/services/app-authentication.service';
+import { AppKey } from '@app/types/app-key.type';
+import { LoginSession } from '@app/models/login-session';
+import { environment } from '@environments';
+
+export class TreeVirtualScrollStrategy extends FixedSizeVirtualScrollStrategy {
+ constructor() {
+ super(48, 250, 500); // (itemSize, minBufferPx, maxBufferPx)
+ }
+}
+
+@Component({
+ selector: 'app-sections-organization-tree',
+ templateUrl: './tree.section.component.html',
+ styleUrls: ['./tree.section.component.scss'],
+ providers: [
+ {
+ provide: VIRTUAL_SCROLL_STRATEGY,
+ useClass: TreeVirtualScrollStrategy
+ }
+ ],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class TreeSectionComponent implements OnInit, OnDestroy {
+ treeData: {
+ deptInfoList: DeptInfo[];
+ displayRoot?: boolean;
+ };
+
+ loginRes: LoginResponse;
+
+ private ngOnDestroySubject = new Subject();
+
+ constructor(
+ private appAuthenticationService: AppAuthenticationService,
+ private sessionStorageService: SessionStorageService,
+ private store: Store,
+ private changeDetectorRef: ChangeDetectorRef,
+ private logService: LogService
+ ) {}
+
+ ngOnInit(): void {
+ this.ngOnDestroySubject = new Subject();
+
+ combineLatest([
+ this.store.pipe(select(LoginSelector.loginRes)),
+ this.store.pipe(select(DepartmentSelector.departmentInfoList))
+ ])
+ .pipe(takeUntil(this.ngOnDestroySubject))
+ .subscribe(([loginRes, deptInfoList]) => {
+ this.loginRes = loginRes;
+ this.treeData = {
+ deptInfoList,
+ displayRoot: environment.productConfig.organization.displayRoot
+ };
+ this.changeDetectorRef.markForCheck();
+ });
+ }
+
+ ngOnDestroy(): void {
+ if (!!this.ngOnDestroySubject) {
+ this.ngOnDestroySubject.complete();
+ }
+ }
+}
diff --git a/src/app/sections/organization/components/tree.section.strategy.ts b/src/app/sections/organization/components/tree.section.strategy.ts
new file mode 100644
index 0000000..564488d
--- /dev/null
+++ b/src/app/sections/organization/components/tree.section.strategy.ts
@@ -0,0 +1,32 @@
+import { Observable, Subject } from 'rxjs';
+
+import {
+ VirtualScrollStrategy,
+ CdkVirtualScrollViewport
+} from '@angular/cdk/scrolling';
+import { distinctUntilChanged } from 'rxjs/operators';
+
+export class OrganizationTreeVirtualScrollStrategy
+ implements VirtualScrollStrategy {
+ scrolledIndexChange: Observable;
+
+ private indexSubject = new Subject();
+ private viewport: CdkVirtualScrollViewport | null = null;
+
+ constructor() {
+ this.scrolledIndexChange = this.indexSubject.pipe(distinctUntilChanged());
+ }
+
+ attach(viewport: CdkVirtualScrollViewport): void {
+ this.viewport = viewport;
+ }
+ detach(): void {
+ this.indexSubject.complete();
+ this.viewport = null;
+ }
+ onContentScrolled(): void {}
+ onDataLengthChanged(): void {}
+ onContentRendered(): void {}
+ onRenderedOffsetChanged(): void {}
+ scrollToIndex(index: number, behavior: ScrollBehavior): void {}
+}
diff --git a/src/app/sections/organization/organization.section.module.ts b/src/app/sections/organization/organization.section.module.ts
new file mode 100644
index 0000000..5d0664c
--- /dev/null
+++ b/src/app/sections/organization/organization.section.module.ts
@@ -0,0 +1,64 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+
+import { FlexLayoutModule } from '@angular/flex-layout';
+import { ReactiveFormsModule } from '@angular/forms';
+
+import { ScrollingModule } from '@angular/cdk/scrolling';
+
+import { MatCheckboxModule } from '@angular/material/checkbox';
+import { MatFormFieldModule } from '@angular/material/form-field';
+import { MatIconModule } from '@angular/material/icon';
+import { MatInputModule } from '@angular/material/input';
+import { MatSelectModule } from '@angular/material/select';
+import { MatChipsModule } from '@angular/material/chips';
+import { MatTableModule } from '@angular/material/table';
+import { MatButtonModule } from '@angular/material/button';
+import { MatExpansionModule } from '@angular/material/expansion';
+
+import { PerfectScrollbarModule } from 'ngx-perfect-scrollbar';
+
+import { I18nModule, UCAP_I18N_NAMESPACE } from '@ucap/ng-i18n';
+import { UiModule } from '@ucap/ng-ui';
+import { OrganizationUiModule } from '@ucap/ng-ui-organization';
+import { AppOrganizationModule } from '@app/ucap/organization/organization.module';
+
+import { COMPONENTS } from './components';
+
+@NgModule({
+ imports: [
+ CommonModule,
+ FlexLayoutModule,
+ ReactiveFormsModule,
+
+ ScrollingModule,
+
+ MatCheckboxModule,
+ MatFormFieldModule,
+ MatIconModule,
+ MatInputModule,
+ MatSelectModule,
+ MatChipsModule,
+ MatTableModule,
+ MatButtonModule,
+ MatExpansionModule,
+
+ PerfectScrollbarModule,
+
+ I18nModule,
+ UiModule,
+ OrganizationUiModule,
+
+ AppOrganizationModule
+ ],
+ exports: [...COMPONENTS],
+ declarations: [...COMPONENTS],
+ entryComponents: [],
+ providers: [
+ {
+ provide: UCAP_I18N_NAMESPACE,
+ useValue: ['organization', 'common']
+ }
+ ]
+})
+export class AppOrganizationSectionModule {}
diff --git a/src/app/services/app-authentication.service.ts b/src/app/services/app-authentication.service.ts
index d743e31..29b42d8 100644
--- a/src/app/services/app-authentication.service.ts
+++ b/src/app/services/app-authentication.service.ts
@@ -26,6 +26,7 @@ import { UserStore } from '@app/models/user-store';
import { AppKey } from '@app/types/app-key.type';
import { environment } from '@environments';
+import { PresenceActions } from '@ucap/ng-store-organization';
@Injectable({
providedIn: 'root'
@@ -238,6 +239,12 @@ export class AppAuthenticationService {
loginSession
})
);
+ this.store.dispatch(
+ PresenceActions.bulkInfo({
+ divCd: 'myBulk',
+ userSeqs: [loginRes.userSeq]
+ })
+ );
resolve({
loginSession,
loginRes
@@ -251,11 +258,7 @@ export class AppAuthenticationService {
}
logout() {
- this.sessionStorageService.remove(AppKey.LoginResponse);
- this.sessionStorageService.remove(AppKey.VerInfoResponse);
this.sessionStorageService.remove(AppKey.LoginSession);
- this.sessionStorageService.remove(AppKey.UrlInfoResponse);
- this.sessionStorageService.remove(AppKey.AuthResponse);
let userStore = this.getUserStore();
if (!!userStore) {
diff --git a/src/app/services/app-chat.service.ts b/src/app/services/app-chat.service.ts
new file mode 100644
index 0000000..5738027
--- /dev/null
+++ b/src/app/services/app-chat.service.ts
@@ -0,0 +1,200 @@
+import { Observable } from 'rxjs';
+import { take, concatMap, map } from 'rxjs/operators';
+
+import { Injectable, Inject, ChangeDetectorRef } from '@angular/core';
+
+import { Store } from '@ngrx/store';
+
+import { LocaleCode } from '@ucap/core';
+
+import { PasswordUtil } from '@ucap/pi';
+import { LoginResponse, SSOMode } from '@ucap/protocol-authentication';
+import { NativeService } from '@ucap/native';
+
+import {
+ SessionStorageService,
+ LocalStorageService
+} from '@ucap/ng-web-storage';
+import { UCAP_NATIVE_SERVICE } from '@ucap/ng-native';
+import { InnerProtocolService } from '@ucap/ng-protocol-inner';
+import { AuthenticationProtocolService } from '@ucap/ng-protocol-authentication';
+import { LoginActions } from '@ucap/ng-store-authentication';
+
+import { LoginSession } from '@app/models/login-session';
+import { UserStore } from '@app/models/user-store';
+
+import { AppKey } from '@app/types/app-key.type';
+
+import { environment } from '@environments';
+import { RoomInfo, RoomType } from '@ucap/protocol-room';
+import { Dictionary } from '@ngrx/entity';
+import {
+ RoomUserMap,
+ RoomUserShortMap
+} from '@ucap/ng-store-chat/lib/store/room/state';
+
+import {
+ TranslatePipe as OrganizationTranslate,
+ TranslateService
+} from '@ucap/ng-ui-organization';
+import { I18nService } from '@ucap/ng-i18n';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class AppChatService {
+ defaultProfileImage = 'assets/images/ico/img_nophoto.svg';
+ defaultProfileImageMulti = 'assets/images/ico/img_nophoto.svg';
+
+ constructor(private i18nService: I18nService) {}
+
+ getRoomName(
+ organizationTranslate: OrganizationTranslate,
+ loginRes: LoginResponse,
+ roomInfo: RoomInfo,
+ roomUsersDictionary?: Dictionary,
+ roomUsersShortDictionary?: Dictionary
+ ): string {
+ if (!roomInfo) {
+ return '';
+ }
+
+ let roomName = '...';
+
+ switch (roomInfo.roomType) {
+ case RoomType.Mytalk:
+ roomName = 'MyTalk';
+ break;
+ default:
+ {
+ const roomId = roomInfo.roomId;
+ const roomUsers = !!roomUsersDictionary
+ ? roomUsersDictionary[roomId]
+ : undefined;
+ const roomUsersShort = !!roomUsersShortDictionary
+ ? roomUsersShortDictionary[roomId]
+ : undefined;
+
+ let users = [];
+ let existUsers = false;
+ if (!!roomUsers && roomUsers.userInfos.length > 0) {
+ existUsers = true;
+ users = roomUsers.userInfos.filter(
+ (userInfo) => userInfo.seq !== Number(loginRes.userSeq)
+ );
+ } else if (!!roomUsersShort && roomUsersShort.userInfos.length > 0) {
+ existUsers = true;
+ users = roomUsersShort.userInfos.filter(
+ (userInfo) => userInfo.seq !== Number(loginRes.userSeq)
+ );
+ }
+
+ const curRoomName = roomInfo.roomName;
+ if (!!curRoomName && curRoomName.trim().length > 0) {
+ // Exist RoomName.
+ roomName = curRoomName;
+ } else {
+ if (users.length > 0) {
+ roomName = organizationTranslate.transform(users, 'name', ',');
+ } else {
+ if (existUsers) {
+ roomName = this.i18nService.t('room.noRoomUser');
+ }
+ }
+ }
+ }
+ break;
+ }
+
+ return roomName;
+ }
+
+ getRoomProfileImage(
+ roomInfo: RoomInfo,
+ loginRes: LoginResponse,
+ roomUsersDictionary?: Dictionary,
+ roomUsersShortDictionary?: Dictionary,
+ defaultProfileImage?: string,
+ defaultProfileImageMulti?: string
+ ): string {
+ defaultProfileImage = defaultProfileImage || this.defaultProfileImage;
+ defaultProfileImageMulti =
+ defaultProfileImageMulti || this.defaultProfileImageMulti;
+
+ let roomImage = '';
+
+ if (!!roomInfo) {
+ switch (roomInfo.roomType) {
+ case RoomType.Mytalk:
+ {
+ if (!!loginRes && !!loginRes.userInfo) {
+ roomImage = loginRes.userInfo.profileImageFile;
+ }
+ }
+ break;
+ case RoomType.Multi:
+ {
+ roomImage = defaultProfileImageMulti;
+ }
+ break;
+ default:
+ {
+ const roomUsers = this.getRoomUserList(
+ loginRes,
+ roomInfo.roomId,
+ roomUsersDictionary,
+ roomUsersShortDictionary
+ );
+ if (
+ !!roomUsers &&
+ !!roomUsers.existUsers &&
+ roomUsers.users.length > 0
+ ) {
+ roomImage = roomUsers.users[0].profileImageFile;
+ }
+ }
+ break;
+ }
+ }
+
+ if (roomImage.trim().length === 0) {
+ roomImage = defaultProfileImage;
+ }
+
+ return roomImage;
+ }
+
+ getRoomUserList(
+ loginRes: LoginResponse,
+ roomId: string,
+ roomUsersDictionary?: Dictionary,
+ roomUsersShortDictionary?: Dictionary
+ ) {
+ let users = [];
+ let existUsers = false;
+
+ const roomUsers = !!roomUsersDictionary
+ ? roomUsersDictionary[roomId]
+ : undefined;
+ const roomUsersShort = !!roomUsersShortDictionary
+ ? roomUsersShortDictionary[roomId]
+ : undefined;
+
+ if (!!roomUsers && roomUsers.userInfos.length > 0) {
+ existUsers = true;
+ users = roomUsers.userInfos.filter(
+ (userInfo) => userInfo.seq !== Number(loginRes.userSeq)
+ );
+ } else if (!!roomUsersShort && roomUsersShort.userInfos.length > 0) {
+ existUsers = true;
+ users = roomUsersShort.userInfos.filter(
+ (userInfo) => userInfo.seq !== Number(loginRes.userSeq)
+ );
+ }
+
+ return {
+ existUsers,
+ users
+ };
+ }
+}
diff --git a/src/app/services/app-native.service.ts b/src/app/services/app-native.service.ts
index 07b5831..4c23dfa 100644
--- a/src/app/services/app-native.service.ts
+++ b/src/app/services/app-native.service.ts
@@ -1,17 +1,15 @@
+import { take } from 'rxjs/operators';
+
import { Injectable, Inject, NgZone } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
-import { Store } from '@ngrx/store';
-
-import { LoginResponse } from '@ucap/protocol-authentication';
+import { Store, select } from '@ngrx/store';
import { NativeService } from '@ucap/native';
import { UCAP_NATIVE_SERVICE } from '@ucap/ng-native';
-import { SessionStorageService } from '@ucap/ng-web-storage';
-
-import { AppKey } from '@app/types/app-key.type';
+import { LoginSelector } from '@ucap/ng-store-authentication';
@Injectable({
providedIn: 'root'
@@ -19,7 +17,6 @@ import { AppKey } from '@app/types/app-key.type';
export class AppNativeService {
constructor(
@Inject(UCAP_NATIVE_SERVICE) private nativeService: NativeService,
- private sessionStorageService: SessionStorageService,
private store: Store,
private matDialog: MatDialog,
private ngZone: NgZone
@@ -30,15 +27,13 @@ export class AppNativeService {
this.ngZone.run(() => {
this.matDialog.closeAll();
- const loginRes = this.sessionStorageService.get(
- AppKey.LoginResponse
- );
-
- // this.store.dispatch(AuthenticationActions.logoutConfirmation());
+ this.store
+ .pipe(take(1), select(LoginSelector.loginRes))
+ .subscribe((loginRes) => {});
});
});
- this.nativeService.changeStatus().subscribe(statusCode => {});
+ this.nativeService.changeStatus().subscribe((statusCode) => {});
this.nativeService.showSetting().subscribe(() => {
this.ngZone.run(() => {
diff --git a/src/app/types/app-key.type.ts b/src/app/types/app-key.type.ts
index 636fa26..dc701eb 100644
--- a/src/app/types/app-key.type.ts
+++ b/src/app/types/app-key.type.ts
@@ -2,9 +2,5 @@ export enum AppKey {
LoginTry = 'ucap::LG::LOGIN_TRY',
LoginSession = 'ucap::LG::LOGIN_SESSION',
LogoutSession = 'ucap::LG::LOGOUT_SESSION',
- UserStore = 'ucap::LG::USER_STORE',
- LoginResponse = 'ucap::LG::LOGIN_RESPONSE',
- AuthResponse = 'ucap::LG::AUTH_RESPONSE',
- UrlInfoResponse = 'ucap::LG::URL_INFO_RESPONSE',
- VerInfoResponse = 'ucap::LG::VER_INFO_RESPONSE'
+ UserStore = 'ucap::LG::USER_STORE'
}
diff --git a/src/app/types/index.ts b/src/app/types/index.ts
new file mode 100644
index 0000000..8a2e241
--- /dev/null
+++ b/src/app/types/index.ts
@@ -0,0 +1,3 @@
+export * from './app-key.type';
+export * from './select-user.dialog.type';
+export * from './tokens';
diff --git a/src/app/types/select-user.dialog.type.ts b/src/app/types/select-user.dialog.type.ts
new file mode 100644
index 0000000..9af3aa6
--- /dev/null
+++ b/src/app/types/select-user.dialog.type.ts
@@ -0,0 +1,14 @@
+export enum SelectUserDialogType {
+ /** 새로운 대화 */
+ NewChat = 'NEW_CHAT',
+ /** 새로운 그룹 생성 */
+ NewGroup = 'NEW_GROUP',
+ /** 기존 그룹 수정 */
+ ModifyGroup = 'MODIFY_GROUP',
+ /** 대화 전달 */
+ MessageForward = 'MESSAGE_FORWARD',
+ /** 그룹멤버 변경 */
+ EditMember = 'EDIT_MEMBER',
+ /** 대화상대 추가 */
+ EditChatMember = 'EDIT_CHAT_MEMBER'
+}
diff --git a/src/app/ucap/authentication/authentication.module.ts b/src/app/ucap/authentication/authentication.module.ts
new file mode 100644
index 0000000..91a2f9a
--- /dev/null
+++ b/src/app/ucap/authentication/authentication.module.ts
@@ -0,0 +1,41 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule, ReactiveFormsModule } from '@angular/forms';
+
+import { FlexLayoutModule } from '@angular/flex-layout';
+
+import { MatCheckboxModule } from '@angular/material/checkbox';
+
+import { PerfectScrollbarModule } from 'ngx-perfect-scrollbar';
+
+import { I18nModule, UCAP_I18N_NAMESPACE } from '@ucap/ng-i18n';
+import { UiModule } from '@ucap/ng-ui';
+import { AuthenticationUiModule } from '@ucap/ng-ui-authentication';
+
+import { COMPONENTS } from './components';
+
+@NgModule({
+ imports: [
+ CommonModule,
+ FormsModule,
+ ReactiveFormsModule,
+ FlexLayoutModule,
+
+ MatCheckboxModule,
+
+ PerfectScrollbarModule,
+ I18nModule,
+ UiModule,
+ AuthenticationUiModule
+ ],
+ exports: [...COMPONENTS],
+ declarations: [...COMPONENTS],
+ entryComponents: [],
+ providers: [
+ {
+ provide: UCAP_I18N_NAMESPACE,
+ useValue: ['authentication', 'common']
+ }
+ ]
+})
+export class AppAuthenticationModule {}
diff --git a/src/app/ucap/authentication/components/index.ts b/src/app/ucap/authentication/components/index.ts
new file mode 100644
index 0000000..d555737
--- /dev/null
+++ b/src/app/ucap/authentication/components/index.ts
@@ -0,0 +1,3 @@
+import { LoginComponent } from './login.component';
+
+export const COMPONENTS = [LoginComponent];
diff --git a/src/app/ucap/authentication/components/login.component.html b/src/app/ucap/authentication/components/login.component.html
new file mode 100644
index 0000000..ce65d43
--- /dev/null
+++ b/src/app/ucap/authentication/components/login.component.html
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
Welcome to Messenger
+
+
+
+
+
+
+ {{ 'login.labels.rememberMe' | ucapI18n }}
+
+
+
+
+
+ {{ 'login.labels.autoLogin' | ucapI18n }}
+
+
+
+
+
+
+
+
+
+
diff --git a/src/app/ucap/authentication/components/login.component.scss b/src/app/ucap/authentication/components/login.component.scss
new file mode 100644
index 0000000..dd33ab9
--- /dev/null
+++ b/src/app/ucap/authentication/components/login.component.scss
@@ -0,0 +1,117 @@
+@import '~@ucap/lg-scss/mixins';
+
+h1 {
+ @include font-family($font-light);
+ font-size: 24px;
+ text-align: center;
+ color: $txt-color01;
+ font-weight: 600;
+ line-height: 1.2;
+ @include screen(mid) {
+ font-size: 19px;
+ }
+ @include screen(xs) {
+ font-size: 14px;
+ }
+}
+
+.login-container {
+ width: 100%;
+ height: 100%;
+ overflow: auto;
+}
+
+.login-chk-area {
+ margin-top: 6px;
+ font-size: 13px;
+ text-align: left;
+ @include screen(xs) {
+ font-size: 12px;
+ }
+}
+.login-pass-info {
+ overflow: hidden;
+ margin-top: 83px;
+ ul {
+ display: flex;
+ justify-content: center;
+ li {
+ height: 24px;
+ position: relative;
+ display: inline-flex;
+ align-items: center;
+ padding: 0 12% 0 8%;
+ &::before {
+ content: '';
+ height: 11px;
+ width: 1px;
+ display: flex;
+ background-color: $gray-re4a;
+ position: absolute;
+ top: 6.5px;
+ left: 0;
+ }
+ &:first-child {
+ padding-left: 0;
+ &::before {
+ display: none;
+ }
+ }
+ &:last-child {
+ padding-right: 0;
+ }
+ a {
+ line-height: 24px;
+ font-size: 12px;
+ color: $gray-re4a;
+ padding-left: 34px;
+ position: relative;
+ white-space: nowrap;
+ &::before {
+ font-family: 'material Icons';
+ font-size: 18px;
+ text-align: center;
+ content: 'search';
+ color: $white;
+ display: block;
+ width: 24px;
+ height: 24px;
+ border-radius: 50%;
+ background-color: $black;
+ position: absolute;
+ top: 0;
+ left: 0;
+ }
+ &.fir-pass {
+ &::before {
+ content: 'sync';
+ }
+ }
+ }
+ }
+ }
+}
+
+.login-button-area {
+ margin-top: 14px;
+ @include screen(xs) {
+ margin-top: 20px;
+ }
+ button {
+ border: 0;
+ margin: 0;
+ width: 100%;
+ height: 46px;
+ border-radius: 4px;
+ background-color: #e0e3e7;
+ font-size: 12px;
+ color: $gray-re4a;
+ cursor: pointer;
+ @include screen(mid) {
+ height: 38px;
+ }
+ @include screen(xs) {
+ height: 34px;
+ }
+ }
+}
diff --git a/src/app/ucap/authentication/components/login.component.spec.ts b/src/app/ucap/authentication/components/login.component.spec.ts
new file mode 100644
index 0000000..997b6d3
--- /dev/null
+++ b/src/app/ucap/authentication/components/login.component.spec.ts
@@ -0,0 +1,32 @@
+import { TestBed, async } from '@angular/core/testing';
+import { RouterTestingModule } from '@angular/router/testing';
+import { LoginComponent } from './login.component';
+
+describe('app::ucap::authentication::LoginComponent', () => {
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ imports: [RouterTestingModule],
+ declarations: [LoginComponent]
+ }).compileComponents();
+ }));
+
+ it('should create the app', () => {
+ const fixture = TestBed.createComponent(LoginComponent);
+ const app = fixture.componentInstance;
+ expect(app).toBeTruthy();
+ });
+
+ it(`should have as title 'ucap-lg-web'`, () => {
+ const fixture = TestBed.createComponent(LoginComponent);
+ const app = fixture.componentInstance;
+ });
+
+ it('should render title', () => {
+ const fixture = TestBed.createComponent(LoginComponent);
+ fixture.detectChanges();
+ const compiled = fixture.nativeElement;
+ expect(compiled.querySelector('.content span').textContent).toContain(
+ 'ucap-lg-web app is running!'
+ );
+ });
+});
diff --git a/src/app/ucap/authentication/components/login.component.ts b/src/app/ucap/authentication/components/login.component.ts
new file mode 100644
index 0000000..49cbc6e
--- /dev/null
+++ b/src/app/ucap/authentication/components/login.component.ts
@@ -0,0 +1,184 @@
+import { Subject } from 'rxjs';
+import { take, takeUntil } from 'rxjs/operators';
+
+import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core';
+
+import { MatCheckbox } from '@angular/material/checkbox';
+
+import { Store, select } from '@ngrx/store';
+
+import { Company } from '@ucap/api-external';
+import { LoginTry } from '@ucap/pi';
+
+import { LogService } from '@ucap/ng-logger';
+import { I18nService } from '@ucap/ng-i18n';
+import {
+ SessionStorageService,
+ LocalStorageService
+} from '@ucap/ng-web-storage';
+import { PiService } from '@ucap/ng-pi';
+import { ProtocolService } from '@ucap/ng-protocol';
+import { CompanyActions, CompanySelector } from '@ucap/ng-store-organization';
+import { LoginActions } from '@ucap/ng-store-authentication';
+
+import { UserStore } from '@app/models/user-store';
+import { LoginSession } from '@app/models/login-session';
+import { AppKey } from '@app/types/app-key.type';
+import { AppAuthenticationService } from '@app/services/app-authentication.service';
+
+import { environment } from '@environments';
+
+@Component({
+ selector: 'app-authentication-login',
+ templateUrl: './login.component.html',
+ styleUrls: ['./login.component.scss']
+})
+export class LoginComponent implements OnInit, OnDestroy {
+ companyGroupCode = environment.companyConfig.companyGroupCode;
+ userStore: UserStore;
+
+ readonly useRememberMe =
+ environment.productConfig.authentication.useRememberMe;
+
+ readonly useAutoLogin = environment.productConfig.authentication.useAutoLogin;
+
+ readonly fixedCompanyCode = environment.companyConfig.fixedCompanyCode;
+
+ @ViewChild('chkUseRememberMe', { static: false })
+ chkUseRememberMe: MatCheckbox;
+
+ @ViewChild('chkUseAutoLogin', { static: false })
+ chkUseAutoLogin: MatCheckbox;
+
+ loginSession: LoginSession;
+ companyList: Company[];
+ disableLoginForm = false;
+ loginProcessing = false;
+ loginTry: LoginTry;
+
+ private ngOnDestroySubject: Subject;
+
+ constructor(
+ private piService: PiService,
+ private protocolService: ProtocolService,
+ private sessionStorageService: SessionStorageService,
+ private localStorageService: LocalStorageService,
+ private i18nService: I18nService,
+ private store: Store,
+ private appAuthenticationService: AppAuthenticationService,
+ private logService: LogService
+ ) {}
+
+ ngOnInit(): void {
+ this.ngOnDestroySubject = new Subject();
+
+ this.localStorageService
+ .encGet$(
+ AppKey.UserStore,
+ environment.productConfig.localEncriptionKey
+ )
+ .pipe(takeUntil(this.ngOnDestroySubject))
+ .subscribe((userStore) => (this.userStore = userStore));
+
+ this.appAuthenticationService
+ .getLoginSession$()
+ .pipe(takeUntil(this.ngOnDestroySubject))
+ .subscribe((loginSession) => (this.loginSession = loginSession));
+
+ this.sessionStorageService
+ .get$(AppKey.LoginTry)
+ .pipe(takeUntil(this.ngOnDestroySubject))
+ .subscribe((loginTry) => (this.loginTry = loginTry));
+
+ this.protocolService.disconnect();
+
+ this.store.dispatch(
+ CompanyActions.companies({
+ req: { companyGroupCode: this.companyGroupCode }
+ })
+ );
+
+ this.store
+ .pipe(
+ takeUntil(this.ngOnDestroySubject),
+ select(CompanySelector.companyList)
+ )
+ .subscribe((companyList) => {
+ this.companyList = companyList;
+ });
+ }
+
+ ngOnDestroy(): void {
+ if (!!this.ngOnDestroySubject) {
+ this.ngOnDestroySubject.next();
+ this.ngOnDestroySubject.complete();
+ }
+ }
+
+ onLogin(event: {
+ companyCode: string;
+ loginId: string;
+ loginPw: string;
+ notValid: () => void;
+ }) {
+ const useRememberMe: boolean = this.chkUseRememberMe.checked;
+ const useAutoLogin: boolean = this.chkUseAutoLogin.checked;
+
+ this.disableLoginForm = true;
+ this.loginProcessing = true;
+
+ this.piService
+ .login2({
+ companyCode: event.companyCode,
+ loginId: event.loginId,
+ loginPw: event.loginPw,
+ deviceType: this.loginSession.deviceType
+ })
+ .pipe(take(1))
+ .subscribe(
+ (res) => {
+ if ('success' !== res.status.toLowerCase()) {
+ this.onWebLoginFailure(event, res.status);
+ return;
+ } else {
+ this.store.dispatch(
+ LoginActions.webLoginSuccess({
+ companyCode: event.companyCode,
+ loginId: event.loginId,
+ loginPw: event.loginPw,
+ autoLogin: useAutoLogin,
+ rememberMe: useRememberMe,
+ login2Response: res
+ })
+ );
+ return;
+ }
+ },
+ (error) => {
+ this.onWebLoginFailure(event, error);
+ },
+ () => {
+ this.disableLoginForm = false;
+ this.loginProcessing = false;
+ }
+ );
+ }
+
+ onClickForgotPassword(lng: string) {
+ this.i18nService.changeLanguage(lng);
+ }
+
+ private onWebLoginFailure(
+ event: {
+ companyCode: string;
+ loginId: string;
+ loginPw: string;
+ notValid: () => void;
+ },
+ error: any
+ ) {
+ this.store.dispatch(LoginActions.webLoginFailure({ error }));
+
+ event.notValid();
+ }
+}
diff --git a/src/app/ucap/chat/chat.module.ts b/src/app/ucap/chat/chat.module.ts
new file mode 100644
index 0000000..a63f8f1
--- /dev/null
+++ b/src/app/ucap/chat/chat.module.ts
@@ -0,0 +1,61 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule, ReactiveFormsModule } from '@angular/forms';
+
+import { FlexLayoutModule } from '@angular/flex-layout';
+
+import { MatRippleModule } from '@angular/material/core';
+import { MatCheckboxModule } from '@angular/material/checkbox';
+
+import { I18nModule, UCAP_I18N_NAMESPACE } from '@ucap/ng-i18n';
+import { UiModule } from '@ucap/ng-ui';
+import { GroupUiModule } from '@ucap/ng-ui-group';
+import { OrganizationUiModule } from '@ucap/ng-ui-organization';
+import { ChatUiModule } from '@ucap/ng-ui-chat';
+
+import { COMPONENTS } from './components';
+import { AppLayoutsModule } from '@app/layouts/layouts.module';
+import { MatIconModule } from '@angular/material/icon';
+import { MatProgressBarModule } from '@angular/material/progress-bar';
+import { MatSliderModule } from '@angular/material/slider';
+import { MatTooltipModule } from '@angular/material/tooltip';
+import { MatTabsModule } from '@angular/material/tabs';
+import { MatSelectModule } from '@angular/material/select';
+import { MatSlideToggleModule } from '@angular/material/slide-toggle';
+
+@NgModule({
+ imports: [
+ CommonModule,
+ FormsModule,
+ ReactiveFormsModule,
+ FlexLayoutModule,
+
+ MatCheckboxModule,
+ MatRippleModule,
+ MatIconModule,
+ MatProgressBarModule,
+ MatSliderModule,
+ MatSlideToggleModule,
+ MatTooltipModule,
+ MatTabsModule,
+ MatSelectModule,
+
+ I18nModule,
+ UiModule,
+ OrganizationUiModule,
+ GroupUiModule,
+ ChatUiModule,
+
+ AppLayoutsModule
+ ],
+ exports: [...COMPONENTS],
+ declarations: [...COMPONENTS],
+ entryComponents: [],
+ providers: [
+ {
+ provide: UCAP_I18N_NAMESPACE,
+ useValue: ['chat', 'common']
+ }
+ ]
+})
+export class AppChatModule {}
diff --git a/src/app/ucap/chat/components/email-send.selector.component.html b/src/app/ucap/chat/components/email-send.selector.component.html
new file mode 100644
index 0000000..925a752
--- /dev/null
+++ b/src/app/ucap/chat/components/email-send.selector.component.html
@@ -0,0 +1,15 @@
+
+
+
대화내용 메일 전송
+
+
+
+
+
+
+
+
diff --git a/src/app/ucap/chat/components/email-send.selector.component.scss b/src/app/ucap/chat/components/email-send.selector.component.scss
new file mode 100644
index 0000000..ed7da72
--- /dev/null
+++ b/src/app/ucap/chat/components/email-send.selector.component.scss
@@ -0,0 +1,9 @@
+.bubble-main {
+ padding: 10px;
+ text-align: left;
+ span {
+ word-wrap: break-word;
+ white-space: pre-wrap;
+ word-break: break-word;
+ }
+}
diff --git a/src/app/ucap/chat/components/email-send.selector.component.spec.ts b/src/app/ucap/chat/components/email-send.selector.component.spec.ts
new file mode 100644
index 0000000..da87732
--- /dev/null
+++ b/src/app/ucap/chat/components/email-send.selector.component.spec.ts
@@ -0,0 +1,24 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { EmailSendSelectorComponent } from './email-send.selector.component';
+
+describe('Chat::MessageBox::EmailSendSelectorComponent', () => {
+ let component: EmailSendSelectorComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [EmailSendSelectorComponent]
+ }).compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(EmailSendSelectorComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/ucap/chat/components/email-send.selector.component.ts b/src/app/ucap/chat/components/email-send.selector.component.ts
new file mode 100644
index 0000000..29ca698
--- /dev/null
+++ b/src/app/ucap/chat/components/email-send.selector.component.ts
@@ -0,0 +1,21 @@
+import {
+ Component,
+ OnInit,
+ Input,
+ ElementRef,
+ AfterViewInit,
+ Inject
+} from '@angular/core';
+
+@Component({
+ selector: 'app-chat-selector-email-send',
+ templateUrl: './email-send.selector.component.html',
+ styleUrls: ['./email-send.selector.component.scss']
+})
+export class EmailSendSelectorComponent implements OnInit, AfterViewInit {
+ constructor() {}
+
+ ngOnInit() {}
+
+ ngAfterViewInit(): void {}
+}
diff --git a/src/app/ucap/chat/components/file-upload.selector.component.html b/src/app/ucap/chat/components/file-upload.selector.component.html
new file mode 100644
index 0000000..6dc8591
--- /dev/null
+++ b/src/app/ucap/chat/components/file-upload.selector.component.html
@@ -0,0 +1,44 @@
+
+
+
파일전송
+ 이 영역으로 파일을 드래그 하시면 업로드 됩니다.
+
+
+
+
+
{{ item.name }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/app/ucap/chat/components/file-upload.selector.component.scss b/src/app/ucap/chat/components/file-upload.selector.component.scss
new file mode 100644
index 0000000..ed7da72
--- /dev/null
+++ b/src/app/ucap/chat/components/file-upload.selector.component.scss
@@ -0,0 +1,9 @@
+.bubble-main {
+ padding: 10px;
+ text-align: left;
+ span {
+ word-wrap: break-word;
+ white-space: pre-wrap;
+ word-break: break-word;
+ }
+}
diff --git a/src/app/ucap/chat/components/file-upload.selector.component.spec.ts b/src/app/ucap/chat/components/file-upload.selector.component.spec.ts
new file mode 100644
index 0000000..da87732
--- /dev/null
+++ b/src/app/ucap/chat/components/file-upload.selector.component.spec.ts
@@ -0,0 +1,24 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { EmailSendSelectorComponent } from './email-send.selector.component';
+
+describe('Chat::MessageBox::EmailSendSelectorComponent', () => {
+ let component: EmailSendSelectorComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [EmailSendSelectorComponent]
+ }).compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(EmailSendSelectorComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/ucap/chat/components/file-upload.selector.component.ts b/src/app/ucap/chat/components/file-upload.selector.component.ts
new file mode 100644
index 0000000..aa28ffb
--- /dev/null
+++ b/src/app/ucap/chat/components/file-upload.selector.component.ts
@@ -0,0 +1,35 @@
+import {
+ Component,
+ OnInit,
+ Input,
+ ElementRef,
+ AfterViewInit,
+ Inject
+} from '@angular/core';
+
+@Component({
+ selector: 'app-chat-selector-file-upload',
+ templateUrl: './file-upload.selector.component.html',
+ styleUrls: ['./file-upload.selector.component.scss']
+})
+export class FileUploadSelectorComponent implements OnInit, AfterViewInit {
+ fileList: { name: string; progress: boolean }[];
+
+ constructor() {}
+
+ ngOnInit() {
+ this.fileList = [
+ { name: 'UCAP 메신저 UX 가이드_V0.1_0309.pptx', progress: false },
+ { name: 'UCAP 메신저 UX .1_0309.pptx', progress: false },
+ { name: 'UCAP 메신저 가이드_V0.1_0309.pptx', progress: true },
+ { name: 'UCAP 메신저 UX 가이드_V.1_0309.pptx', progress: true },
+ { name: 'UCAP 메신저 UX 가이드_V0.1_0309.pptx', progress: false },
+ { name: 'UCAP 메신저 UX 가이드_V0..pptx', progress: false },
+ { name: 'UCAP 메신저 UX 가이드_V0.1_0309.pptx', progress: false },
+ { name: ' 메신저 UX 가이드_V0.1_0309.pptx', progress: false },
+ { name: 'UCAP 메신저 UX 가이드_V0.1_0309.pptx', progress: false }
+ ];
+ }
+
+ ngAfterViewInit(): void {}
+}
diff --git a/src/app/ucap/chat/components/index.ts b/src/app/ucap/chat/components/index.ts
new file mode 100644
index 0000000..e5efec4
--- /dev/null
+++ b/src/app/ucap/chat/components/index.ts
@@ -0,0 +1,18 @@
+import { RoomExpansionComponent } from './room-expansion.component';
+
+import { TranslationSelectorComponent } from './translation.selector.component';
+import { EmailSendSelectorComponent } from './email-send.selector.component';
+import { StickerSelectorComponent } from './sticker.selector.component';
+import { FileUploadSelectorComponent } from './file-upload.selector.component';
+import { MessageBoxComponent } from './message-box.component';
+
+export const COMPONENTS = [
+ RoomExpansionComponent,
+
+ StickerSelectorComponent,
+ TranslationSelectorComponent,
+ EmailSendSelectorComponent,
+ FileUploadSelectorComponent,
+
+ MessageBoxComponent
+];
diff --git a/src/app/ucap/chat/components/message-box.component.html b/src/app/ucap/chat/components/message-box.component.html
new file mode 100644
index 0000000..2097f89
--- /dev/null
+++ b/src/app/ucap/chat/components/message-box.component.html
@@ -0,0 +1,166 @@
+
+
+ isMe : {{ isMe }}
+
+
+
+
+
+
+
+
+
+
+ Information...
+
+ -->
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ unreadCount }}
+
+
+
+
diff --git a/src/app/ucap/chat/components/message-box.component.scss b/src/app/ucap/chat/components/message-box.component.scss
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/ucap/chat/components/message-box.component.spec.ts b/src/app/ucap/chat/components/message-box.component.spec.ts
new file mode 100644
index 0000000..a6974e4
--- /dev/null
+++ b/src/app/ucap/chat/components/message-box.component.spec.ts
@@ -0,0 +1,24 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { MessageBoxComponent } from './message-box.component';
+
+describe('Chat::MessageBoxComponent', () => {
+ let component: MessageBoxComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [MessageBoxComponent]
+ }).compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(MessageBoxComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/ucap/chat/components/message-box.component.ts b/src/app/ucap/chat/components/message-box.component.ts
new file mode 100644
index 0000000..511d025
--- /dev/null
+++ b/src/app/ucap/chat/components/message-box.component.ts
@@ -0,0 +1,60 @@
+import {
+ Component,
+ OnInit,
+ Input,
+ EventEmitter,
+ Output,
+ AfterViewInit,
+ ElementRef,
+ ViewChild,
+ ChangeDetectorRef
+} from '@angular/core';
+import { Info, EventJson, EventType, FileType } from '@ucap/protocol-event';
+import {
+ UserInfo as RoomUserInfo,
+ UserInfoShort as RoomUserInfoShort
+} from '@ucap/protocol-room';
+
+@Component({
+ selector: 'app-chat-message-box',
+ templateUrl: './message-box.component.html',
+ styleUrls: ['./message-box.component.scss']
+})
+export class MessageBoxComponent implements OnInit, AfterViewInit {
+ @Input()
+ message: Info;
+
+ @Input()
+ isMe = false;
+ @Input()
+ senderInfo: RoomUserInfoShort | RoomUserInfo;
+ @Input()
+ defaultProfileImage = '';
+ @Input()
+ profileImageRoot = '';
+
+ @Input()
+ dateChanged = false;
+ @Input()
+ unreadCount: number;
+
+ EventType = EventType;
+
+ //////////////////////////////////
+ @Input()
+ messageType: string;
+ @Input()
+ isInformation = false;
+ @Input()
+ highlight = false;
+ @Input()
+ existReadToHere = false;
+ @Input()
+ fileType: FileType;
+
+ constructor() {}
+
+ ngOnInit() {}
+
+ ngAfterViewInit(): void {}
+}
diff --git a/src/app/ucap/chat/components/room-expansion.component.html b/src/app/ucap/chat/components/room-expansion.component.html
new file mode 100644
index 0000000..76253af
--- /dev/null
+++ b/src/app/ucap/chat/components/room-expansion.component.html
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/app/ucap/chat/components/room-expansion.component.scss b/src/app/ucap/chat/components/room-expansion.component.scss
new file mode 100644
index 0000000..3f67707
--- /dev/null
+++ b/src/app/ucap/chat/components/room-expansion.component.scss
@@ -0,0 +1,4 @@
+.room-expansion-container {
+ width: 100%;
+ height: 100%;
+}
diff --git a/src/app/ucap/chat/components/room-expansion.component.spec.ts b/src/app/ucap/chat/components/room-expansion.component.spec.ts
new file mode 100644
index 0000000..81d3928
--- /dev/null
+++ b/src/app/ucap/chat/components/room-expansion.component.spec.ts
@@ -0,0 +1,32 @@
+import { TestBed, async } from '@angular/core/testing';
+import { RouterTestingModule } from '@angular/router/testing';
+import { RoomExpansionComponent } from './room-expansion.component';
+
+describe('app::ucap::chat::RoomExpansionComponent', () => {
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ imports: [RouterTestingModule],
+ declarations: [RoomExpansionComponent]
+ }).compileComponents();
+ }));
+
+ it('should create the app', () => {
+ const fixture = TestBed.createComponent(RoomExpansionComponent);
+ const app = fixture.componentInstance;
+ expect(app).toBeTruthy();
+ });
+
+ it(`should have as title 'ucap-lg-web'`, () => {
+ const fixture = TestBed.createComponent(RoomExpansionComponent);
+ const app = fixture.componentInstance;
+ });
+
+ it('should render title', () => {
+ const fixture = TestBed.createComponent(RoomExpansionComponent);
+ fixture.detectChanges();
+ const compiled = fixture.nativeElement;
+ expect(compiled.querySelector('.content span').textContent).toContain(
+ 'ucap-lg-web app is running!'
+ );
+ });
+});
diff --git a/src/app/ucap/chat/components/room-expansion.component.ts b/src/app/ucap/chat/components/room-expansion.component.ts
new file mode 100644
index 0000000..4bc163a
--- /dev/null
+++ b/src/app/ucap/chat/components/room-expansion.component.ts
@@ -0,0 +1,349 @@
+import { Subject, combineLatest } from 'rxjs';
+import { takeUntil, take } from 'rxjs/operators';
+
+import {
+ Component,
+ OnInit,
+ OnDestroy,
+ ChangeDetectionStrategy,
+ ChangeDetectorRef,
+ Input,
+ Output,
+ EventEmitter
+} from '@angular/core';
+
+import { Store, select } from '@ngrx/store';
+
+import {
+ FixedSizeVirtualScrollStrategy,
+ VIRTUAL_SCROLL_STRATEGY
+} from '@angular/cdk/scrolling';
+
+import { VersionInfo2Response } from '@ucap/api-public';
+import { LoginResponse } from '@ucap/protocol-authentication';
+import { UserInfo, GroupDetailData } from '@ucap/protocol-sync';
+import { UserInfoSS, UserInfoF, UserInfoDN } from '@ucap/protocol-query';
+import {
+ RoomInfo,
+ RoomType,
+ ExitAllRequest,
+ UpdateRequest,
+ ExitRequest
+} from '@ucap/protocol-room';
+
+import { LogService } from '@ucap/ng-logger';
+
+import { RoomSelector, RoomActions } from '@ucap/ng-store-chat';
+import {
+ LoginSelector,
+ ConfigurationSelector
+} from '@ucap/ng-store-authentication';
+import { BuddySelector, GroupSelector } from '@ucap/ng-store-group';
+
+import {
+ TranslatePipe as OrganizationTranslate,
+ TranslateService
+} from '@ucap/ng-ui-organization';
+import { NodeType, FlatNode } from '@ucap/ng-ui-group';
+
+import { AppAuthenticationService } from '@app/services/app-authentication.service';
+import { LoginSession } from '@app/models/login-session';
+import { AppChatService } from '@app/services/app-chat.service';
+import {
+ DateService,
+ ConfirmDialogComponent,
+ ConfirmDialogData,
+ ConfirmDialogResult
+} from '@ucap/ng-ui';
+import { Dictionary } from '@ngrx/entity';
+import {
+ RoomUserMap,
+ RoomUserShortMap
+} from '@ucap/ng-store-chat/lib/store/room/state';
+import { MatDialog } from '@angular/material/dialog';
+import { I18nService } from '@ucap/ng-i18n';
+
+export type UserInfoTypes = UserInfo | UserInfoSS | UserInfoF | UserInfoDN;
+
+export class GroupVirtualScrollStrategy extends FixedSizeVirtualScrollStrategy {
+ constructor() {
+ super(60, 150, 200); // (itemSize, minBufferPx, maxBufferPx)
+ }
+}
+
+@Component({
+ selector: 'app-chat-room-expansion',
+ templateUrl: './room-expansion.component.html',
+ styleUrls: ['./room-expansion.component.scss'],
+ providers: [
+ {
+ provide: VIRTUAL_SCROLL_STRATEGY,
+ useClass: GroupVirtualScrollStrategy
+ }
+ ],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class RoomExpansionComponent implements OnInit, OnDestroy {
+ @Input()
+ checkable = false;
+
+ @Input()
+ selectedRoomList: RoomInfo[] = [];
+
+ @Output()
+ toggleItem = new EventEmitter<{
+ checked: boolean;
+ roomInfo: RoomInfo;
+ }>();
+
+ @Output()
+ openChatRoom: EventEmitter = new EventEmitter();
+
+ defaultProfileImage: string;
+ defaultProfileImageMulti: string;
+
+ versionInfo2Res: VersionInfo2Response;
+ loginRes: LoginResponse;
+
+ roomList: RoomInfo[];
+ roomUsersDictionary: Dictionary;
+ roomUsersShortDictionary: Dictionary;
+ roomGroup: { division: string; roomList: RoomInfo[] }[];
+
+ organizationTranslate: OrganizationTranslate;
+
+ private ngOnDestroySubject: Subject;
+
+ constructor(
+ private appChatService: AppChatService,
+ private dateService: DateService,
+ private i18nService: I18nService,
+ private translateService: TranslateService,
+ private dialog: MatDialog,
+ private store: Store,
+ private changeDetectorRef: ChangeDetectorRef
+ ) {
+ this.organizationTranslate = new OrganizationTranslate(
+ this.translateService,
+ this.changeDetectorRef
+ );
+
+ // default image setting
+ this.defaultProfileImage = this.appChatService.defaultProfileImage;
+ this.defaultProfileImageMulti = this.appChatService.defaultProfileImage;
+ }
+
+ ngOnInit(): void {
+ this.ngOnDestroySubject = new Subject();
+
+ this.store
+ .pipe(
+ takeUntil(this.ngOnDestroySubject),
+ select(ConfigurationSelector.versionInfo2Response)
+ )
+ .subscribe((versionInfo2Res) => {
+ this.versionInfo2Res = versionInfo2Res;
+ });
+
+ this.store
+ .pipe(takeUntil(this.ngOnDestroySubject), select(LoginSelector.loginRes))
+ .subscribe((loginRes) => {
+ this.loginRes = loginRes;
+ });
+
+ this.store
+ .pipe(takeUntil(this.ngOnDestroySubject), select(RoomSelector.rooms))
+ .subscribe((rooms) => {
+ rooms = (rooms || []).filter((info) => info.isJoinRoom);
+ this.roomList = rooms;
+
+ // groupping.
+ this.initGroup();
+
+ this.changeDetectorRef.detectChanges();
+ });
+
+ combineLatest([
+ this.store.pipe(
+ select(
+ (state: any) =>
+ state.chat.room.roomUsers.entities as Dictionary
+ )
+ ),
+ this.store.pipe(
+ select(
+ (state: any) =>
+ state.chat.room.roomUsersShort.entities as Dictionary<
+ RoomUserShortMap
+ >
+ )
+ )
+ ])
+ .pipe(takeUntil(this.ngOnDestroySubject))
+ .subscribe(([roomUsers, roomUsersShort]) => {
+ this.roomUsersDictionary = roomUsers;
+ this.roomUsersShortDictionary = roomUsersShort;
+ this.changeDetectorRef.detectChanges();
+ });
+ }
+
+ ngOnDestroy(): void {
+ if (!!this.ngOnDestroySubject) {
+ this.ngOnDestroySubject.next();
+ this.ngOnDestroySubject.complete();
+ }
+ }
+
+ initGroup() {
+ this.roomGroup = [];
+
+ this.roomList.forEach((roomInfo) => {
+ const date = roomInfo.finalEventDate;
+ let division = '';
+ try {
+ const value = this.dateService.get(date, 'LL');
+
+ if (value === 'Invalid date') {
+ division = date;
+ } else {
+ division = value;
+ }
+ } catch (error) {
+ division = date;
+ }
+
+ const index = this.roomGroup.findIndex(
+ (info) => info.division === division
+ );
+ if (index > -1) {
+ this.roomGroup[index] = {
+ ...this.roomGroup[index],
+ roomList: [...this.roomGroup[index].roomList, roomInfo]
+ };
+ } else {
+ this.roomGroup.push({
+ division,
+ roomList: [roomInfo]
+ });
+ }
+ });
+ }
+
+ getRoomName(roomInfo: RoomInfo): string {
+ if (!roomInfo) {
+ return '';
+ }
+
+ const roomName = this.appChatService.getRoomName(
+ this.organizationTranslate,
+ this.loginRes,
+ roomInfo,
+ this.roomUsersDictionary,
+ this.roomUsersShortDictionary
+ );
+
+ return roomName;
+ }
+
+ getRoomProfileImage(roomInfo: RoomInfo): string {
+ let roomImage = '';
+ if (!!roomInfo) {
+ roomImage = this.appChatService.getRoomProfileImage(
+ roomInfo,
+ this.loginRes,
+ this.roomUsersDictionary,
+ this.roomUsersShortDictionary
+ );
+ }
+
+ return roomImage;
+ }
+
+ isToday(date: any) {
+ return this.dateService.isToday(date);
+ }
+
+ onToggleAlarm(roomInfo: RoomInfo): void {
+ if (!roomInfo) {
+ return;
+ }
+
+ this.store.dispatch(
+ RoomActions.update({
+ req: {
+ roomId: roomInfo.roomId,
+ roomName:
+ roomInfo.roomName.trim().length === 0
+ ? ' '
+ : roomInfo.roomName.trim(),
+ receiveAlarm: !roomInfo.receiveAlarm,
+ syncAll: false
+ } as UpdateRequest
+ })
+ );
+ this.changeDetectorRef.detectChanges();
+ }
+
+ onDelRoom(roomInfo: RoomInfo): void {
+ if (!roomInfo) {
+ return;
+ }
+
+ const dialogRef = this.dialog.open<
+ ConfirmDialogComponent,
+ ConfirmDialogData,
+ ConfirmDialogResult
+ >(ConfirmDialogComponent, {
+ data: {
+ title: this.i18nService.t('room.dialog.titleExitFromRoom'),
+ html: this.i18nService.t('room.dialog.confirmExitFromRoom')
+ }
+ });
+
+ dialogRef
+ .afterClosed()
+ .pipe(take(1))
+ .subscribe((result) => {
+ if (!!result && !!result.choice) {
+ this.store.dispatch(
+ RoomActions.del({
+ req: {
+ roomId: roomInfo.roomId
+ } as ExitRequest
+ })
+ );
+ }
+ });
+ this.changeDetectorRef.detectChanges();
+ }
+
+ getChecked(roomInfo: RoomInfo): boolean {
+ if (this.selectedRoomList.some((info) => info.roomId === roomInfo.roomId)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ onToggleItem(event: { checked: boolean; roomInfo: RoomInfo }): void {
+ this.toggleItem.emit(event);
+ }
+
+ onClickRoomItem(event: MouseEvent, roomInfo: RoomInfo): void {
+ event.preventDefault();
+ event.stopPropagation();
+
+ if (!!this.checkable) {
+ this.onToggleItem({
+ checked: !this.getChecked(roomInfo),
+ roomInfo
+ });
+ } else {
+ this.openChatRoom.emit(roomInfo);
+ }
+ }
+
+ onOpenChatRoom(roomInfo: RoomInfo): void {
+ this.openChatRoom.emit(roomInfo);
+ }
+}
diff --git a/src/app/ucap/chat/components/room-expansion.strategy.ts b/src/app/ucap/chat/components/room-expansion.strategy.ts
new file mode 100644
index 0000000..5624900
--- /dev/null
+++ b/src/app/ucap/chat/components/room-expansion.strategy.ts
@@ -0,0 +1,31 @@
+import { Observable, Subject } from 'rxjs';
+
+import {
+ VirtualScrollStrategy,
+ CdkVirtualScrollViewport
+} from '@angular/cdk/scrolling';
+import { distinctUntilChanged } from 'rxjs/operators';
+
+export class ChatRoomVirtualScrollStrategy implements VirtualScrollStrategy {
+ scrolledIndexChange: Observable;
+
+ private indexSubject = new Subject();
+ private viewport: CdkVirtualScrollViewport | null = null;
+
+ constructor() {
+ this.scrolledIndexChange = this.indexSubject.pipe(distinctUntilChanged());
+ }
+
+ attach(viewport: CdkVirtualScrollViewport): void {
+ this.viewport = viewport;
+ }
+ detach(): void {
+ this.indexSubject.complete();
+ this.viewport = null;
+ }
+ onContentScrolled(): void {}
+ onDataLengthChanged(): void {}
+ onContentRendered(): void {}
+ onRenderedOffsetChanged(): void {}
+ scrollToIndex(index: number, behavior: ScrollBehavior): void {}
+}
diff --git a/src/app/ucap/chat/components/sticker.selector.component.html b/src/app/ucap/chat/components/sticker.selector.component.html
new file mode 100644
index 0000000..8b33ca2
--- /dev/null
+++ b/src/app/ucap/chat/components/sticker.selector.component.html
@@ -0,0 +1,55 @@
+
+
+
이모티콘
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/app/ucap/chat/components/sticker.selector.component.scss b/src/app/ucap/chat/components/sticker.selector.component.scss
new file mode 100644
index 0000000..ed7da72
--- /dev/null
+++ b/src/app/ucap/chat/components/sticker.selector.component.scss
@@ -0,0 +1,9 @@
+.bubble-main {
+ padding: 10px;
+ text-align: left;
+ span {
+ word-wrap: break-word;
+ white-space: pre-wrap;
+ word-break: break-word;
+ }
+}
diff --git a/src/app/ucap/chat/components/sticker.selector.component.spec.ts b/src/app/ucap/chat/components/sticker.selector.component.spec.ts
new file mode 100644
index 0000000..da87732
--- /dev/null
+++ b/src/app/ucap/chat/components/sticker.selector.component.spec.ts
@@ -0,0 +1,24 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { EmailSendSelectorComponent } from './email-send.selector.component';
+
+describe('Chat::MessageBox::EmailSendSelectorComponent', () => {
+ let component: EmailSendSelectorComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [EmailSendSelectorComponent]
+ }).compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(EmailSendSelectorComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/ucap/chat/components/sticker.selector.component.ts b/src/app/ucap/chat/components/sticker.selector.component.ts
new file mode 100644
index 0000000..563efe4
--- /dev/null
+++ b/src/app/ucap/chat/components/sticker.selector.component.ts
@@ -0,0 +1,263 @@
+import {
+ Component,
+ OnInit,
+ Input,
+ ElementRef,
+ AfterViewInit,
+ Inject
+} from '@angular/core';
+
+@Component({
+ selector: 'app-chat-selector-sticker',
+ templateUrl: './sticker.selector.component.html',
+ styleUrls: ['./sticker.selector.component.scss']
+})
+export class StickerSelectorComponent implements OnInit, AfterViewInit {
+ stickerHistory: string[] = [];
+
+ stickerBasePath = '../../../../assets/sticker/';
+ stickerInfoList: StickerInfo[] = [];
+ stickerFileInfoList: StickerFilesInfo[] = [];
+ currentSticker: StickerFilesInfo;
+
+ currentTabIndex: number;
+
+ constructor() {}
+
+ ngOnInit() {
+ this.stickerInfoList = StickerUtil.getStickerInfoList();
+ this.currentTabIndex = 0;
+ }
+
+ ngAfterViewInit(): void {}
+
+ onSelectedIndexChange(value: number) {
+ this.currentTabIndex = value;
+ }
+
+ getStickerTitleImage(
+ stickerInfo: StickerInfo,
+ hover: boolean,
+ tabIndex: number
+ ) {
+ if (this.currentTabIndex === tabIndex) {
+ return this.stickerBasePath + stickerInfo.iconPathOn;
+ }
+
+ if (!hover) {
+ return this.stickerBasePath + stickerInfo.iconPath;
+ } else {
+ return this.stickerBasePath + stickerInfo.iconPathOn;
+ }
+ }
+
+ getStickerInfos(stickerInfo: StickerInfo) {
+ if (stickerInfo.index === '00') {
+ const rtnArray: StickerFilesInfo[] = [];
+
+ if (!!this.stickerHistory && this.stickerHistory.length > 0) {
+ this.stickerHistory.forEach((sticker) => {
+ const arr: string[] = sticker.split('_');
+ if (arr.length === 2) {
+ const sInfo: StickerInfo[] = StickerMap.filter(
+ (s) => s.index === arr[0]
+ );
+ if (!!sInfo && sInfo.length > 0) {
+ rtnArray.push(
+ ...sInfo[0].fileInfos.filter(
+ (fileInfo) => fileInfo.index === sticker
+ )
+ );
+ }
+ }
+ });
+ }
+ return rtnArray;
+ } else {
+ return stickerInfo.fileInfos;
+ }
+ }
+
+ getStickerContentsImage(sticker: StickerFilesInfo) {
+ return this.stickerBasePath + sticker.path;
+ }
+
+ onClickSelectSticker(sticker: StickerFilesInfo) {
+ this.currentSticker = sticker;
+ }
+
+ onClickClose() {}
+}
+
+export interface StickerInfo {
+ index: string;
+ title: string;
+ iconPath: string;
+ iconPathOn: string;
+ useYn: boolean;
+ fileInfos: StickerFilesInfo[];
+}
+export interface StickerFilesInfo {
+ index: string;
+ path: string;
+}
+export const StickerMap: StickerInfo[] = [
+ {
+ index: '00',
+ title: 'History',
+ iconPath: 'sticker_cate00.png',
+ iconPathOn: 'sticker_cate00_f.png',
+ useYn: true,
+ fileInfos: []
+ },
+ {
+ index: '01',
+ title: '꼼므',
+ iconPath: 'sticker_cate01.png',
+ iconPathOn: 'sticker_cate01_f.png',
+ useYn: true,
+ fileInfos: [
+ { index: '01_01', path: 'sticker_s_01_01.png' },
+ { index: '01_02', path: 'sticker_s_01_02.png' },
+ { index: '01_03', path: 'sticker_s_01_03.png' },
+ { index: '01_04', path: 'sticker_s_01_04.png' },
+ { index: '01_05', path: 'sticker_s_01_05.png' },
+ { index: '01_06', path: 'sticker_s_01_06.png' },
+ { index: '01_07', path: 'sticker_s_01_07.png' },
+ { index: '01_08', path: 'sticker_s_01_08.png' }
+ ]
+ },
+ {
+ index: '02',
+ title: '까미',
+ iconPath: 'sticker_cate02.png',
+ iconPathOn: 'sticker_cate02_f.png',
+ useYn: true,
+ fileInfos: [
+ { index: '02_02', path: 'sticker_s_02_02.png' },
+ { index: '02_03', path: 'sticker_s_02_03.png' },
+ { index: '02_04', path: 'sticker_s_02_04.png' },
+ { index: '02_05', path: 'sticker_s_02_05.png' },
+ { index: '02_06', path: 'sticker_s_02_06.png' },
+ { index: '02_07', path: 'sticker_s_02_07.png' },
+ { index: '02_08', path: 'sticker_s_02_08.png' }
+ ]
+ },
+ {
+ index: '03',
+ title: '왈도',
+ iconPath: 'sticker_cate03.png',
+ iconPathOn: 'sticker_cate03_f.png',
+ useYn: true,
+ fileInfos: [
+ { index: '03_01', path: 'sticker_s_03_01.png' },
+ { index: '03_02', path: 'sticker_s_03_02.png' },
+ { index: '03_03', path: 'sticker_s_03_03.png' },
+ { index: '03_04', path: 'sticker_s_03_04.png' },
+ { index: '03_05', path: 'sticker_s_03_05.png' },
+ { index: '03_06', path: 'sticker_s_03_06.png' },
+ { index: '03_07', path: 'sticker_s_03_07.png' },
+ { index: '03_08', path: 'sticker_s_03_08.png' }
+ ]
+ },
+ {
+ index: '04',
+ title: '웅쓰',
+ iconPath: 'sticker_cate04.png',
+ iconPathOn: 'sticker_cate04_f.png',
+ useYn: true,
+ fileInfos: [
+ { index: '04_01', path: 'sticker_s_04_01.png' },
+ { index: '04_02', path: 'sticker_s_04_02.png' },
+ { index: '04_03', path: 'sticker_s_04_03.png' },
+ { index: '04_04', path: 'sticker_s_04_04.png' },
+ { index: '04_05', path: 'sticker_s_04_05.png' },
+ { index: '04_06', path: 'sticker_s_04_06.png' },
+ { index: '04_07', path: 'sticker_s_04_07.png' },
+ { index: '04_08', path: 'sticker_s_04_08.png' }
+ ]
+ },
+ {
+ index: '05',
+ title: '말풍선',
+ iconPath: 'sticker_cate05.png',
+ iconPathOn: 'sticker_cate05_f.png',
+ useYn: true,
+ fileInfos: [
+ { index: '05_01', path: 'sticker_s_05_01.png' },
+ { index: '05_02', path: 'sticker_s_05_02.png' },
+ { index: '05_03', path: 'sticker_s_05_03.png' },
+ { index: '05_04', path: 'sticker_s_05_04.png' },
+ { index: '05_05', path: 'sticker_s_05_05.png' },
+ { index: '05_06', path: 'sticker_s_05_06.png' },
+ { index: '05_07', path: 'sticker_s_05_07.png' },
+ { index: '05_08', path: 'sticker_s_05_08.png' },
+ { index: '05_09', path: 'sticker_s_05_09.png' },
+ { index: '05_10', path: 'sticker_s_05_10.png' },
+ { index: '05_11', path: 'sticker_s_05_11.png' },
+ { index: '05_12', path: 'sticker_s_05_12.png' },
+ { index: '05_13', path: 'sticker_s_05_13.png' },
+ { index: '05_14', path: 'sticker_s_05_14.png' },
+ { index: '05_15', path: 'sticker_s_05_15.png' },
+ { index: '05_16', path: 'sticker_s_05_16.png' }
+ ]
+ },
+ {
+ index: '12',
+ title: '황소',
+ iconPath: 'sticker_cate12.png',
+ iconPathOn: 'sticker_cate12_f.png',
+ useYn: true,
+ fileInfos: [
+ { index: '12_01', path: 'sticker_s_12_01.gif' },
+ { index: '12_02', path: 'sticker_s_12_02.gif' },
+ { index: '12_03', path: 'sticker_s_12_03.gif' },
+ { index: '12_04', path: 'sticker_s_12_04.gif' },
+ { index: '12_05', path: 'sticker_s_12_05.gif' },
+ { index: '12_06', path: 'sticker_s_12_06.gif' },
+ { index: '12_07', path: 'sticker_s_12_07.gif' },
+ { index: '12_08', path: 'sticker_s_12_08.gif' },
+ { index: '12_09', path: 'sticker_s_12_09.gif' },
+ { index: '12_10', path: 'sticker_s_12_10.gif' },
+ { index: '12_11', path: 'sticker_s_12_11.gif' },
+ { index: '12_12', path: 'sticker_s_12_12.gif' },
+ { index: '12_13', path: 'sticker_s_12_13.gif' },
+ { index: '12_14', path: 'sticker_s_12_14.gif' },
+ { index: '12_15', path: 'sticker_s_12_15.gif' },
+ { index: '12_16', path: 'sticker_s_12_16.gif' }
+ ]
+ }
+];
+
+const ActiveAndOrdering: string[] = ['00', '01', '02', '03', '04', '05'];
+
+export class StickerUtil {
+ static getStickerInfoList(): StickerInfo[] {
+ const rtnStickerList: StickerInfo[] = [];
+
+ ActiveAndOrdering.forEach((idx) => {
+ const stickerInfos: StickerInfo[] = StickerMap.filter(
+ (sticker) => sticker.index === idx && sticker.useYn
+ );
+
+ if (!!stickerInfos && stickerInfos.length > 0) {
+ rtnStickerList.push(stickerInfos[0]);
+ }
+ });
+
+ return rtnStickerList;
+ }
+
+ static getStickerInfoSub(index: string): StickerFilesInfo[] {
+ const stickerFilesList: StickerFilesInfo[] = [];
+ const stickerInfos: StickerInfo[] = StickerMap.filter(
+ (sticker) => sticker.index === index && sticker.useYn
+ );
+
+ if (!!stickerInfos && stickerInfos.length > 0) {
+ stickerFilesList.concat(stickerInfos[0].fileInfos);
+ }
+
+ return stickerFilesList;
+ }
+}
diff --git a/src/app/ucap/chat/components/translation.selector.component.html b/src/app/ucap/chat/components/translation.selector.component.html
new file mode 100644
index 0000000..c26bd54
--- /dev/null
+++ b/src/app/ucap/chat/components/translation.selector.component.html
@@ -0,0 +1,39 @@
+
+
+
번역
+
+
+
+
+
+
+ 미리보기 영역입니다.
+
+
+
+
+
+
+
+ 대상언어
+
+
+ 번역없음
+ 영어
+ 일어
+
+
+
+
+ 간략보기
+
+
+ 미리보기
+
+
+
+
diff --git a/src/app/ucap/chat/components/translation.selector.component.scss b/src/app/ucap/chat/components/translation.selector.component.scss
new file mode 100644
index 0000000..ed7da72
--- /dev/null
+++ b/src/app/ucap/chat/components/translation.selector.component.scss
@@ -0,0 +1,9 @@
+.bubble-main {
+ padding: 10px;
+ text-align: left;
+ span {
+ word-wrap: break-word;
+ white-space: pre-wrap;
+ word-break: break-word;
+ }
+}
diff --git a/src/app/ucap/chat/components/translation.selector.component.spec.ts b/src/app/ucap/chat/components/translation.selector.component.spec.ts
new file mode 100644
index 0000000..da87732
--- /dev/null
+++ b/src/app/ucap/chat/components/translation.selector.component.spec.ts
@@ -0,0 +1,24 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { EmailSendSelectorComponent } from './email-send.selector.component';
+
+describe('Chat::MessageBox::EmailSendSelectorComponent', () => {
+ let component: EmailSendSelectorComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [EmailSendSelectorComponent]
+ }).compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(EmailSendSelectorComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/ucap/chat/components/translation.selector.component.ts b/src/app/ucap/chat/components/translation.selector.component.ts
new file mode 100644
index 0000000..8546cd8
--- /dev/null
+++ b/src/app/ucap/chat/components/translation.selector.component.ts
@@ -0,0 +1,21 @@
+import {
+ Component,
+ OnInit,
+ Input,
+ ElementRef,
+ AfterViewInit,
+ Inject
+} from '@angular/core';
+
+@Component({
+ selector: 'app-chat-selector-translation',
+ templateUrl: './translation.selector.component.html',
+ styleUrls: ['./translation.selector.component.scss']
+})
+export class TranslationSelectorComponent implements OnInit, AfterViewInit {
+ constructor() {}
+
+ ngOnInit() {}
+
+ ngAfterViewInit(): void {}
+}
diff --git a/src/app/ucap/group/components/expansion.component.html b/src/app/ucap/group/components/expansion.component.html
new file mode 100644
index 0000000..d5c1acc
--- /dev/null
+++ b/src/app/ucap/group/components/expansion.component.html
@@ -0,0 +1,222 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ group.name }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/app/ucap/group/components/expansion.component.scss b/src/app/ucap/group/components/expansion.component.scss
new file mode 100644
index 0000000..72eaf11
--- /dev/null
+++ b/src/app/ucap/group/components/expansion.component.scss
@@ -0,0 +1,27 @@
+@charset 'UTF-8';
+
+@import '~@ucap/lg-scss/mixins';
+
+.expansion-container {
+ width: 100%;
+ height: 100%;
+ .ucap-clickable {
+ display: flex;
+ min-height: 60px;
+ border-top: 10px solid #f1f2f6;
+ border-bottom: 1px solid $gray-rec;
+ li {
+ width: 100%;
+ .path {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+ .group-info {
+ justify-self: self-start;
+ flex-grow: 1;
+ }
+ }
+ }
+ }
+}
diff --git a/src/app/ucap/group/components/expansion.component.spec.ts b/src/app/ucap/group/components/expansion.component.spec.ts
new file mode 100644
index 0000000..0f0057a
--- /dev/null
+++ b/src/app/ucap/group/components/expansion.component.spec.ts
@@ -0,0 +1,32 @@
+import { TestBed, async } from '@angular/core/testing';
+import { RouterTestingModule } from '@angular/router/testing';
+import { ExpansionComponent } from './expansion.component';
+
+describe('app::ucap::group::ExpansionComponent', () => {
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ imports: [RouterTestingModule],
+ declarations: [ExpansionComponent]
+ }).compileComponents();
+ }));
+
+ it('should create the app', () => {
+ const fixture = TestBed.createComponent(ExpansionComponent);
+ const app = fixture.componentInstance;
+ expect(app).toBeTruthy();
+ });
+
+ it(`should have as title 'ucap-lg-web'`, () => {
+ const fixture = TestBed.createComponent(ExpansionComponent);
+ const app = fixture.componentInstance;
+ });
+
+ it('should render title', () => {
+ const fixture = TestBed.createComponent(ExpansionComponent);
+ fixture.detectChanges();
+ const compiled = fixture.nativeElement;
+ expect(compiled.querySelector('.content span').textContent).toContain(
+ 'ucap-lg-web app is running!'
+ );
+ });
+});
diff --git a/src/app/ucap/group/components/expansion.component.ts b/src/app/ucap/group/components/expansion.component.ts
new file mode 100644
index 0000000..f784b07
--- /dev/null
+++ b/src/app/ucap/group/components/expansion.component.ts
@@ -0,0 +1,740 @@
+import { Subject, combineLatest, of } from 'rxjs';
+import { takeUntil, map, take, catchError } from 'rxjs/operators';
+
+import {
+ Component,
+ OnInit,
+ OnDestroy,
+ ChangeDetectionStrategy,
+ ChangeDetectorRef,
+ Input,
+ EventEmitter,
+ Output,
+ ViewChild,
+ ElementRef,
+ Self
+} from '@angular/core';
+
+import { Store, select } from '@ngrx/store';
+
+import {
+ FixedSizeVirtualScrollStrategy,
+ VIRTUAL_SCROLL_STRATEGY
+} from '@angular/cdk/scrolling';
+
+import { VersionInfo2Response } from '@ucap/api-public';
+import { LoginResponse } from '@ucap/protocol-authentication';
+import { UserInfo, GroupDetailData } from '@ucap/protocol-sync';
+import { UserInfoSS, UserInfoF, UserInfoDN } from '@ucap/protocol-query';
+import { UserInfo as RoomUserInfo } from '@ucap/protocol-room';
+
+import { LogService } from '@ucap/ng-logger';
+import { NodeType, FlatNode } from '@ucap/ng-ui-group';
+import {
+ LoginSelector,
+ ConfigurationSelector
+} from '@ucap/ng-store-authentication';
+import {
+ BuddySelector,
+ GroupSelector,
+ BuddyActions,
+ GroupActions
+} from '@ucap/ng-store-group';
+
+import { AppAuthenticationService } from '@app/services/app-authentication.service';
+import { LoginSession } from '@app/models/login-session';
+import { MatMenuTrigger } from '@angular/material/menu';
+import { MatDialog } from '@angular/material/dialog';
+
+import { ExpansionComponent as UcapExpansionComponent } from '@ucap/ng-ui-group';
+
+import { environment } from '@environments';
+import { PresenceSelector } from '@ucap/ng-store-organization';
+import { StatusCode } from '@ucap/core';
+
+import { EditInlineInputDialogComponent } from '@app/sections/group/dialogs/edit-inline-input.dialog.component';
+import {
+ AlertDialogComponent,
+ AlertDialogData,
+ AlertDialogResult,
+ ConfirmDialogComponent,
+ ConfirmDialogData,
+ ConfirmDialogResult
+} from '@ucap/ng-ui';
+import { I18nService } from '@ucap/ng-i18n';
+
+export type UserInfoTypes =
+ | UserInfo
+ | UserInfoSS
+ | UserInfoF
+ | UserInfoDN
+ | RoomUserInfo;
+
+export class GroupVirtualScrollStrategy extends FixedSizeVirtualScrollStrategy {
+ constructor() {
+ super(60, 150, 200); // (itemSize, minBufferPx, maxBufferPx)
+ }
+}
+
+@Component({
+ selector: 'app-group-expansion',
+ templateUrl: './expansion.component.html',
+ styleUrls: ['./expansion.component.scss'],
+ providers: [
+ {
+ provide: VIRTUAL_SCROLL_STRATEGY,
+ useClass: GroupVirtualScrollStrategy
+ }
+ ],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class ExpansionComponent implements OnInit, OnDestroy {
+ @Input()
+ checkable = false;
+
+ @Input()
+ editable = false;
+
+ @Input()
+ isDialog = false;
+
+ @Input()
+ selectedUserList: UserInfoTypes[];
+
+ @Input()
+ showType: string;
+
+ @Output()
+ checked = new EventEmitter<{
+ isChecked: boolean;
+ userInfo: UserInfoTypes;
+ }>();
+
+ @Output()
+ profileMenu: EventEmitter = new EventEmitter();
+
+ @Output()
+ clicked = new EventEmitter<{ event: MouseEvent; userInfo: UserInfoTypes }>();
+
+ @Output()
+ selectGroupMenu = new EventEmitter<{
+ menuType: string;
+ group: GroupDetailData;
+ }>();
+
+ // @Output()
+ // editGroupName = new EventEmitter<{
+ // editName: string;
+ // group: GroupDetailData;
+ // }>();
+
+ @Output()
+ checkGroup = new EventEmitter<{
+ isChecked: boolean;
+ groupBuddyList: { group: GroupDetailData; buddyList: UserInfo[] };
+ }>();
+
+ @ViewChild('groupExpansion', { static: false })
+ groupExpansion: UcapExpansionComponent;
+
+ @ViewChild('groupContextMenuTrigger', { static: true })
+ groupContextMenuTrigger: MatMenuTrigger;
+ groupContextMenuPosition = { x: '0px', y: '0px' };
+
+ @ViewChild('editGroupNameTrigger', { static: true })
+ editGroupNameTrigger: MatMenuTrigger;
+ editGroupNamePosition = { x: '0px', y: '0px' };
+
+ @ViewChild('profileContextMenuTrigger', { static: true })
+ profileContextMenuTrigger: MatMenuTrigger;
+ profileContextMenuPosition = { x: '0px', y: '0px' };
+
+ loginSession: LoginSession;
+ versionInfo2Res: VersionInfo2Response;
+ loginRes: LoginResponse;
+ isProfileClicked = false;
+ groupMenuEvent: MouseEvent;
+ isSearchData = false;
+
+ displayOrder: NodeType[] = [
+ NodeType.Profile,
+ NodeType.Favorite,
+ NodeType.Buddy,
+ NodeType.Default
+ ];
+
+ profile: UserInfo;
+ favorites: UserInfo[];
+ groupBuddies: { group: GroupDetailData; buddyList: UserInfo[] }[];
+ onlineBuddies: { group: GroupDetailData; buddyList: UserInfo[] }[];
+ onOffBuddies: { group: GroupDetailData; buddyList: UserInfo[] }[];
+
+ editablGroup: GroupDetailData = null;
+
+ private ngOnDestroySubject: Subject;
+
+ constructor(
+ private appAuthenticationService: AppAuthenticationService,
+ private store: Store,
+ private changeDetectorRef: ChangeDetectorRef,
+ private logService: LogService,
+ private dialog: MatDialog,
+ private i18nService: I18nService,
+ @Self() private elementRef: ElementRef
+ ) {}
+
+ ngOnInit(): void {
+ this.ngOnDestroySubject = new Subject();
+
+ this.appAuthenticationService
+ .getLoginSession$()
+ .pipe(takeUntil(this.ngOnDestroySubject))
+ .subscribe((loginSession) => (this.loginSession = loginSession));
+
+ this.store
+ .pipe(
+ takeUntil(this.ngOnDestroySubject),
+ select(ConfigurationSelector.versionInfo2Response)
+ )
+ .subscribe((versionInfo2Res) => {
+ this.versionInfo2Res = versionInfo2Res;
+ });
+
+ this.store
+ .pipe(takeUntil(this.ngOnDestroySubject), select(LoginSelector.loginRes))
+ .subscribe((loginRes) => {
+ this.loginRes = loginRes;
+ });
+
+ combineLatest([
+ this.store.pipe(select(BuddySelector.buddies)),
+ this.store.pipe(select(GroupSelector.groups)),
+ this.store.pipe(select(PresenceSelector.selectAllStatusBulkInfo))
+ ])
+ .pipe(takeUntil(this.ngOnDestroySubject))
+ .subscribe(([buddies, groups, bulkInfos]) => {
+ buddies = buddies || [];
+ groups = groups || [];
+ bulkInfos = bulkInfos || [];
+
+ const favorites = buddies
+ .filter((buddy) => buddy.isFavorit)
+ .sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
+
+ if (!!favorites && 0 < favorites.length) {
+ this.favorites = favorites;
+ this.changeDetectorRef.markForCheck();
+ }
+
+ const tempOrder: GroupDetailData[] = [];
+ let defaultGroup: GroupDetailData;
+ const buddyGroup: GroupDetailData[] = [];
+ groups.forEach((group) => {
+ if (0 === group.seq) {
+ defaultGroup = group;
+ } else {
+ buddyGroup.push(group);
+ }
+ });
+
+ tempOrder.push(
+ ...buddyGroup.sort((a, b) =>
+ a.name < b.name ? -1 : a.name > b.name ? 1 : 0
+ )
+ );
+
+ if (!!defaultGroup) {
+ tempOrder.push(defaultGroup);
+ }
+
+ groups = tempOrder;
+
+ if (!!groups && 0 < groups.length) {
+ this.groupBuddies = [];
+
+ for (const group of groups) {
+ this.groupBuddies.push({
+ group,
+ buddyList: buddies.filter((buddy) => {
+ return -1 < group.userSeqs.indexOf(buddy.seq as any);
+ })
+ });
+ }
+ this.changeDetectorRef.markForCheck();
+ }
+
+ // 접속한 동료 리스트
+ if (!!bulkInfos && bulkInfos.length > 0) {
+ this.onlineBuddies = [];
+ for (const group of groups) {
+ const tempBuddyList = [];
+
+ this.onlineBuddies.push({
+ group,
+ buddyList: buddies.filter((buddy) => {
+ if (-1 < group.userSeqs.indexOf(buddy.seq as any)) {
+ const tempBulkinfos = bulkInfos.filter(
+ (bulk) => bulk.userSeq === buddy.seq + ''
+ );
+ if (!!tempBulkinfos && tempBulkinfos.length > 0) {
+ const pcStatus = tempBulkinfos[0].pcStatus;
+ const mStatus = tempBulkinfos[0].mobileStatus;
+
+ if (
+ pcStatus === StatusCode.OnLine ||
+ mStatus === StatusCode.OnLine
+ ) {
+ return tempBuddyList.push(buddy);
+ }
+ }
+ }
+ })
+ });
+ }
+ }
+
+ // 온/오프라인 리스트
+ if (!!bulkInfos && bulkInfos.length > 0) {
+ this.onOffBuddies = [];
+ const onlineGroup = {
+ SENDER_SEQ: this.loginRes.SENDER_SEQ,
+ SSVC_TYPE: 5,
+ SVC_TYPE: 82,
+ seq: -998,
+ name: '온라인',
+ isActive: true
+ } as GroupDetailData;
+ const offlineGroup = {
+ SENDER_SEQ: this.loginRes.SENDER_SEQ,
+ SSVC_TYPE: 5,
+ SVC_TYPE: 82,
+ seq: -999,
+ name: '오프라인',
+ isActive: true
+ } as GroupDetailData;
+
+ const tempGroups = [onlineGroup, offlineGroup];
+
+ for (const group of tempGroups) {
+ const tempBuddyList = [];
+ this.onOffBuddies.push({
+ group,
+ buddyList: buddies.filter((buddy) => {
+ const tempBulkinfos = bulkInfos.filter(
+ (bulk) => bulk.userSeq === buddy.seq + ''
+ );
+ if (!!tempBulkinfos && tempBulkinfos.length > 0) {
+ const pcStatus = tempBulkinfos[0].pcStatus;
+ const mStatus = tempBulkinfos[0].mobileStatus;
+
+ if (group.name.localeCompare('온라인') === 0) {
+ if (
+ pcStatus === StatusCode.OnLine ||
+ mStatus === StatusCode.OnLine
+ ) {
+ return tempBuddyList.push(buddy);
+ }
+ } else if (group.name.localeCompare('오프라인') === 0) {
+ if (
+ pcStatus === StatusCode.Offline &&
+ mStatus === StatusCode.Offline
+ ) {
+ return tempBuddyList.push(buddy);
+ }
+ }
+ }
+ })
+ });
+ }
+ }
+ });
+ }
+
+ ngOnDestroy(): void {
+ if (!!this.ngOnDestroySubject) {
+ this.ngOnDestroySubject.next();
+ this.ngOnDestroySubject.complete();
+ }
+ }
+
+ onProfileMenu(
+ event: MouseEvent,
+ userInfo: UserInfoTypes,
+ group: GroupDetailData,
+ isSearchData: boolean
+ ) {
+ this.profileMenu.emit({ event, userInfo, group, isSearchData });
+ }
+
+ onCheckedUser(params: { isChecked: boolean; userInfo: UserInfoTypes }) {
+ this.checked.emit(params);
+ }
+
+ onClickUser(event: MouseEvent, userInfo: UserInfo) {
+ this.clicked.emit({ event, userInfo });
+ }
+
+ onClickMoreMenu(params: { event: MouseEvent; node: FlatNode }) {
+ if (this.showType.localeCompare('ON_OFF') === 0) {
+ return;
+ }
+
+ params.event.preventDefault();
+ params.event.stopPropagation();
+ this.groupMenuEvent = params.event;
+ this.groupContextMenuPosition.x = params.event.clientX + 'px';
+ this.groupContextMenuPosition.y = params.event.clientY + 'px';
+ this.groupContextMenuTrigger.menu.focusFirstItem('mouse');
+ this.groupContextMenuTrigger.menuData = {
+ group: params.node.node.groupDetail
+ };
+ this.groupContextMenuTrigger.openMenu();
+ }
+
+ onShowEditGroup(group: GroupDetailData) {
+ const x = this.groupMenuEvent.clientX - 20;
+ const y = this.groupMenuEvent.clientY - 30;
+
+ this.editGroupNamePosition.x = x + 'px';
+ this.editGroupNamePosition.y = y + 'px';
+ this.editGroupNameTrigger.menu.focusFirstItem('mouse');
+ this.editGroupNameTrigger.menuData = {
+ group
+ };
+ this.editGroupNameTrigger.openMenu();
+ }
+
+ onExpandMore() {
+ this.groupExpansion.expandMore();
+ }
+ onExpandLess() {
+ this.groupExpansion.expandLess();
+ }
+
+ getShowGroupContextMenu(menuType: string, group: GroupDetailData) {
+ // 즐겨찾기 그룹 숨김메뉴
+ if (
+ menuType === 'DIV1' ||
+ menuType === 'RENAME' ||
+ menuType === 'MANAGE_MEMBER' ||
+ menuType === 'DELETE'
+ ) {
+ if (
+ !group ||
+ group === undefined ||
+ (group.seq < 0 && group.name === 'Favorite')
+ ) {
+ return false;
+ }
+
+ /** 수정불가 그룹 핸들링. */
+ if (!!group && !!environment.productConfig.group.fixedGroupSeqs) {
+ const fixedGroupSeqs: number[] =
+ environment.productConfig.group.fixedGroupSeqs;
+ if (!!fixedGroupSeqs && fixedGroupSeqs.length > 0) {
+ if (fixedGroupSeqs.indexOf(group.seq) > -1) {
+ return false;
+ }
+ }
+ }
+ }
+
+ // 기본 그룹 숨김메뉴
+ if (menuType === 'RENAME' || menuType === 'DELETE') {
+ if (!!group && group.seq === 0) {
+ return false;
+ }
+ }
+
+ // 그룹원 0명인 그룹 메뉴 정리
+ if (
+ menuType === 'CHAT' ||
+ menuType === 'SEND_MESSAGE' ||
+ menuType === 'DIV1'
+ ) {
+ if (!!group && !!group.userSeqs && group.userSeqs.length > 0) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ onSelectGroupMenu(menuType: string, group: GroupDetailData) {
+ if (menuType.localeCompare('RENAME') === 0) {
+ // this.editablGroup = group;
+ // this.onShowEditGroup(group);
+ const target = this.elementRef.nativeElement.querySelector(
+ '.mat-tree-node'
+ );
+ const rect = target.getBoundingClientRect();
+
+ const clickEventY = this.groupMenuEvent.clientY;
+ const tartgetY = Math.floor((clickEventY - 150) * 0.1) * 10;
+
+ const dialogRef = this.dialog.open(EditInlineInputDialogComponent, {
+ width: rect.width,
+ height: rect.height,
+ panelClass: 'ucap-edit-group-name-dialog',
+ data: {
+ curValue: group.name,
+ placeholder: '그룹명을 입력하세요.',
+ left: rect.left,
+ top: clickEventY - 100 + rect.height
+ }
+ });
+
+ dialogRef
+ .afterClosed()
+ .pipe(
+ take(1),
+ map((result) => {
+ if (
+ !!result &&
+ result.choice &&
+ result.curValue.localeCompare(group.name) !== 0
+ ) {
+ this.store.dispatch(
+ GroupActions.update({
+ req: {
+ groupSeq: group.seq,
+ groupName: result.curValue,
+ userSeqs: group.userSeqs
+ }
+ })
+ );
+ }
+ }),
+ catchError((err) => {
+ return of(err);
+ })
+ )
+ .subscribe();
+ }
+ this.selectGroupMenu.emit({ menuType, group });
+ }
+
+ // onApplyEditGroupName(editName: string, group: GroupDetailData) {
+ // // this.editablGroup = null;
+ // this.editGroupName.emit({
+ // editName,
+ // group
+ // });
+ // }
+
+ onCheckGroup(params: {
+ isChecked: boolean;
+ groupBuddyList: { group: GroupDetailData; buddyList: UserInfo[] };
+ }) {
+ this.checkGroup.emit({
+ isChecked: params.isChecked,
+ groupBuddyList: params.groupBuddyList
+ });
+ }
+
+ getCheckedUser(userInfo: UserInfoTypes) {
+ if (!!this.selectedUserList && this.selectedUserList.length > 0) {
+ return (
+ this.selectedUserList.filter((item) => item.seq === userInfo.seq)
+ .length > 0
+ );
+ }
+ return false;
+ }
+
+ getStatusBulkInfo(buddy: UserInfoTypes) {
+ return this.store.pipe(
+ select(PresenceSelector.selectEntitiesStatusBulkInfo),
+ map((statusBulkInfo) =>
+ !!statusBulkInfo ? statusBulkInfo[buddy.seq] : undefined
+ )
+ );
+ }
+
+ getBuddiesForShowType(): { group: GroupDetailData; buddyList: UserInfo[] }[] {
+ if (!this.showType || this.showType.localeCompare('ALL') === 0) {
+ return this.groupBuddies;
+ } else if (this.showType.localeCompare('ONLINE_BUDDY') === 0) {
+ return this.onlineBuddies;
+ } else if (this.showType.localeCompare('ON_OFF') === 0) {
+ return this.onOffBuddies;
+ }
+ return null;
+ }
+
+ onClikeMoreProfile(params: {
+ event: MouseEvent;
+ userInfo: UserInfoTypes;
+ group: GroupDetailData;
+ rect: any;
+ }) {
+ params.event.preventDefault();
+ params.event.stopPropagation();
+ this.profileContextMenuPosition.x = params.event.clientX + 'px';
+ this.profileContextMenuPosition.y = params.event.clientY + 'px';
+ this.profileContextMenuTrigger.menu.focusFirstItem('mouse');
+ this.profileContextMenuTrigger.menuData = {
+ userInfo: params.userInfo,
+ group: params.group,
+ rect: params.rect
+ };
+ this.profileContextMenuTrigger.openMenu();
+ }
+
+ onClickProfileContextMenu(
+ event: MouseEvent,
+ menuType: string,
+ userInfo: UserInfoF,
+ group: GroupDetailData,
+ rect: any
+ ) {
+ event.preventDefault();
+ event.stopPropagation();
+ switch (menuType) {
+ case 'VIEW_PROFILE':
+ this.onClickUser(event, userInfo as UserInfo);
+ break;
+ case 'REGISTER_FAVORITE':
+ this.store.dispatch(
+ BuddyActions.update({
+ req: {
+ seq: Number(userInfo.seq),
+ isFavorit: !userInfo.isFavorit
+ }
+ })
+ );
+ break;
+ case 'NICKNAME':
+ {
+ this.editNickname(event, userInfo, rect);
+ }
+ break;
+ case 'COPY_BUDDY':
+ break;
+ case 'MOVE_BUDDY':
+ break;
+ case 'REMOVE_BUDDY':
+ {
+ this.removeBuddy(userInfo, group);
+ }
+ break;
+ }
+ }
+
+ private removeBuddy(userInfo: UserInfoF, group: GroupDetailData) {
+ const dialogRef = this.dialog.open<
+ ConfirmDialogComponent,
+ ConfirmDialogData,
+ ConfirmDialogResult
+ >(ConfirmDialogComponent, {
+ data: {
+ title: '',
+ html: this.i18nService.t('label.confirmRemoveBuddy')
+ }
+ });
+ dialogRef
+ .afterClosed()
+ .pipe(
+ take(1),
+ map((result) => {
+ if (!!result && result.choice) {
+ const trgtUserSeq = group.userSeqs.filter(
+ (user) => user + '' !== userInfo.seq + ''
+ );
+
+ this.store.dispatch(
+ GroupActions.updateMember({
+ targetGroup: group,
+ targetUserSeqs: trgtUserSeq
+ })
+ );
+ }
+ }),
+ catchError((err) => {
+ return of(err);
+ })
+ )
+ .subscribe();
+ }
+ private editNickname(event: MouseEvent, userInfo: UserInfoF, rect: any) {
+ const clickEventY = event.clientY;
+
+ const dialogRef = this.dialog.open(EditInlineInputDialogComponent, {
+ width: rect.width - 30 + '',
+ height: rect.height,
+ panelClass: 'ucap-edit-group-name-dialog',
+ data: {
+ curValue: userInfo.nickName,
+ placeholder: '닉네임을 설정하세요.',
+ left: rect.left + 70,
+ top: rect.top
+ }
+ });
+
+ dialogRef
+ .afterClosed()
+ .pipe(
+ take(1),
+ map((result) => {
+ if (
+ !!result &&
+ result.choice &&
+ result.curValue.localeCompare(userInfo.nickName) !== 0
+ ) {
+ this.store.dispatch(
+ BuddyActions.nickname({
+ req: {
+ userSeq: Number(userInfo.seq),
+ nickname: result.curValue
+ }
+ })
+ );
+ }
+ }),
+ catchError((err) => {
+ return of(err);
+ })
+ )
+ .subscribe();
+ }
+
+ getShowProfileContextMenu(menuType: string, group: GroupDetailData) {
+ return true;
+ if (!!this.isSearchData) {
+ if (menuType === 'VIEW_PROFILE' || menuType === 'SEND_MESSAGE') {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ if (!group || group === undefined) {
+ if (
+ menuType === 'REGISTER_FAVORITE' ||
+ menuType === 'SEND_MESSAGE' ||
+ menuType === 'REGISTER_NICKNAME'
+ ) {
+ // continue;
+ } else {
+ return false;
+ }
+ }
+
+ /** 수정불가 그룹 핸들링. */
+ if (!!group && !!environment.productConfig.group.fixedGroupSeqs) {
+ const fixedGroupSeqs: number[] =
+ environment.productConfig.group.fixedGroupSeqs;
+ if (!!fixedGroupSeqs && fixedGroupSeqs.length > 0) {
+ if (fixedGroupSeqs.indexOf(group.seq) > -1) {
+ if (menuType === 'REMOVE_FROM_GROUP' || menuType === 'MOVE_BUDDY') {
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/src/app/ucap/group/components/expansion.strategy.ts b/src/app/ucap/group/components/expansion.strategy.ts
new file mode 100644
index 0000000..cb565a2
--- /dev/null
+++ b/src/app/ucap/group/components/expansion.strategy.ts
@@ -0,0 +1,31 @@
+import { Observable, Subject } from 'rxjs';
+
+import {
+ VirtualScrollStrategy,
+ CdkVirtualScrollViewport
+} from '@angular/cdk/scrolling';
+import { distinctUntilChanged } from 'rxjs/operators';
+
+export class GroupVirtualScrollStrategy implements VirtualScrollStrategy {
+ scrolledIndexChange: Observable;
+
+ private indexSubject = new Subject();
+ private viewport: CdkVirtualScrollViewport | null = null;
+
+ constructor() {
+ this.scrolledIndexChange = this.indexSubject.pipe(distinctUntilChanged());
+ }
+
+ attach(viewport: CdkVirtualScrollViewport): void {
+ this.viewport = viewport;
+ }
+ detach(): void {
+ this.indexSubject.complete();
+ this.viewport = null;
+ }
+ onContentScrolled(): void {}
+ onDataLengthChanged(): void {}
+ onContentRendered(): void {}
+ onRenderedOffsetChanged(): void {}
+ scrollToIndex(index: number, behavior: ScrollBehavior): void {}
+}
diff --git a/src/app/ucap/group/components/index.ts b/src/app/ucap/group/components/index.ts
new file mode 100644
index 0000000..b622c05
--- /dev/null
+++ b/src/app/ucap/group/components/index.ts
@@ -0,0 +1,4 @@
+import { ExpansionComponent } from './expansion.component';
+import { ProfileListItemComponent } from './profile-list-item.component';
+
+export const COMPONENTS = [ExpansionComponent, ProfileListItemComponent];
diff --git a/src/app/ucap/group/components/profile-list-item.component.html b/src/app/ucap/group/components/profile-list-item.component.html
new file mode 100644
index 0000000..6ee735d
--- /dev/null
+++ b/src/app/ucap/group/components/profile-list-item.component.html
@@ -0,0 +1,98 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ userInfo | ucapOrganizationTranslate: 'name' }}
+
+
+ {{ userInfo | ucapOrganizationTranslate: 'grade' }}
+
+
+
+ {{ userInfo | ucapOrganizationTranslate: 'deptName' }}
+
+
+
+
+ {{ userInfo.intro }}
+
+
+ {{ userInfo.nickName }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/app/ucap/group/components/profile-list-item.component.scss b/src/app/ucap/group/components/profile-list-item.component.scss
new file mode 100644
index 0000000..1111e59
--- /dev/null
+++ b/src/app/ucap/group/components/profile-list-item.component.scss
@@ -0,0 +1,136 @@
+@import '~@ucap/lg-scss/mixins';
+
+.user-list {
+ display: flex;
+ flex-flow: row nowrap;
+ justify-content: space-between;
+ padding: 0 16px;
+ height: 70px;
+ align-items: center;
+ &.line-top {
+ border-top: 1px solid $gray-rec;
+ }
+ .user-profile-info {
+ display: inline-flex;
+ flex-direction: row;
+ flex-grow: 2.3;
+ .user-profile-thumb {
+ @include profile-avatar-default(
+ 0 5px 5px 0,
+ 8,
+ $green,
+ 18px
+ ); //오른 아래 공간, 모바일 온라인 아이콘 크기, 모바일 아이콘 색, 모바일 아이콘 bg크기
+ .presence {
+ //PC 상태
+ @include presence-state(8px); //원크기
+ }
+ .profile-image {
+ @include avatar-img(36px, 2px); //아바타 크기, 왼쪽공간
+ }
+ }
+ .user-info {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ justify-content: flex-start;
+ padding-left: 16px;
+ .user-n-g {
+ display: flex;
+ flex-flow: row-reverse nowrap;
+ align-items: flex-end;
+ height: 22px;
+ .user-name {
+ @include ellipsis-column(1);
+ height: 22px;
+ font: {
+ size: 16px;
+ weight: 600;
+ }
+ color: $gray-re21;
+ order: 1;
+ -ms-flex-order: 1;
+ }
+ .user-grade {
+ @include ellipsis(1);
+ align-self: stretch;
+ font: {
+ size: 13px;
+ }
+ color: $gray-re70;
+ margin-left: 4px;
+ order: 0;
+ -ms-flex-order: 0;
+ }
+ }
+ .dept-name {
+ @include ellipsis(1);
+ font-size: 12px;
+ color: $gray-re6;
+ line-height: 16px;
+ }
+ }
+ }
+ .intro {
+ display: inline-flex;
+ flex-flow: row nowrap;
+ flex-basis: 35%;
+ flex-grow: 0;
+ align-items: baseline;
+ p {
+ font-size: 11px;
+ line-height: 1.4;
+ @include ellipsis(2);
+ height: 30px;
+ }
+ &:before {
+ content: 'chat';
+ @include font-family-ico($font-ico-default, 12, center, $lipstick);
+ flex-direction: row;
+ align-items: flex-start;
+ width: 12px;
+ height: 12px;
+ line-height: 12px;
+ margin-right: 4.8px;
+ position: relative;
+ top: 2px;
+ }
+ }
+ .btn-partner-set {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 0 25px;
+ border-top: 1px solid rgba(255, 255, 255, 0.8);
+ border-bottom: 1px solid rgba(255, 255, 255, 0.8);
+ height: 20px;
+ margin-top: 20px;
+ img {
+ vertical-align: top;
+ }
+ }
+ .intro-name {
+ display: inline-flex;
+ flex-flow: row nowrap;
+ flex-basis: 35%;
+ align-items: center;
+ justify-content: center;
+ overflow: hidden;
+ span {
+ display: inline-block;
+ text-align: center;
+ width: 100%;
+ height: 20px;
+ line-height: 20px;
+ color: $gray-re70;
+ font-size: 11px;
+ padding: 0 10px;
+ border-radius: 30px;
+ border: solid 1px $warm-pink;
+ background-color: #ffffff;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+ }
+}
diff --git a/src/app/ucap/group/components/profile-list-item.component.spec.ts b/src/app/ucap/group/components/profile-list-item.component.spec.ts
new file mode 100644
index 0000000..9965458
--- /dev/null
+++ b/src/app/ucap/group/components/profile-list-item.component.spec.ts
@@ -0,0 +1,26 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { By } from '@angular/platform-browser';
+import { DebugElement } from '@angular/core';
+
+import { ProfileListItemComponent } from './profile-list-item.component';
+
+describe('ucap::ucap::organization::ProfileListItemComponent', () => {
+ let component: ProfileListItemComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [ProfileListItemComponent]
+ }).compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(ProfileListItemComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/ucap/group/components/profile-list-item.component.ts b/src/app/ucap/group/components/profile-list-item.component.ts
new file mode 100644
index 0000000..49027ec
--- /dev/null
+++ b/src/app/ucap/group/components/profile-list-item.component.ts
@@ -0,0 +1,214 @@
+import {
+ Component,
+ OnInit,
+ OnDestroy,
+ ChangeDetectionStrategy,
+ ChangeDetectorRef,
+ Input,
+ EventEmitter,
+ Output,
+ ElementRef,
+ Self
+} from '@angular/core';
+import { UserInfo, GroupDetailData } from '@ucap/protocol-sync';
+import { UserInfoSS, UserInfoF, UserInfoDN } from '@ucap/protocol-query';
+import { UserInfo as RoomUserInfo } from '@ucap/protocol-room';
+import { StatusBulkInfo, StatusInfo } from '@ucap/protocol-status';
+import { PresenceType, StatusCode } from '@ucap/core';
+import { I18nService } from '@ucap/ng-i18n';
+import { ucapAnimations } from '@ucap/ng-ui';
+
+export type UserInfoTypes =
+ | UserInfo
+ | UserInfoSS
+ | UserInfoF
+ | UserInfoDN
+ | RoomUserInfo;
+
+@Component({
+ selector: 'app-group-profile-list-item',
+ templateUrl: './profile-list-item.component.html',
+ styleUrls: ['./profile-list-item.component.scss'],
+ animations: ucapAnimations,
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class ProfileListItemComponent implements OnInit, OnDestroy {
+ @Input()
+ userInfo: UserInfoF;
+
+ @Input()
+ group: GroupDetailData;
+
+ @Input()
+ isSearchData = false;
+
+ @Input()
+ defaultProfileImage: string;
+
+ @Input()
+ profileImageRoot: string;
+
+ @Input()
+ set presence(info: StatusBulkInfo | StatusInfo) {
+ this._presence = info;
+ }
+ get presence(): StatusBulkInfo | StatusInfo {
+ return this._presence;
+ }
+ _presence: StatusBulkInfo | StatusInfo;
+
+ @Input()
+ showMenu = true;
+
+ @Input()
+ checkable = false;
+ @Input()
+ set isChecked(checked: boolean) {
+ this._isChecked = checked;
+ }
+
+ get isChecked(): boolean {
+ return this._isChecked;
+ }
+ _isChecked = false;
+
+ @Output()
+ checked = new EventEmitter<{
+ isChecked: boolean;
+ userInfo: UserInfoTypes;
+ }>();
+
+ @Output()
+ moreMenu: EventEmitter<{
+ event: MouseEvent;
+ userInfo: UserInfoTypes;
+ group: GroupDetailData;
+ rect: any;
+ }> = new EventEmitter();
+
+ isClicked = false;
+ isShowMenu = false;
+
+ @Input()
+ isMe = false;
+
+ PresenceType = PresenceType;
+ isClickMore = false;
+
+ constructor(
+ private changeDetectorRef: ChangeDetectorRef,
+ private i18nService: I18nService,
+ @Self() private elementRef: ElementRef
+ ) {
+ this.i18nService.setDefaultNamespace('organization');
+ }
+
+ ngOnInit(): void {}
+
+ ngOnDestroy(): void {}
+
+ onClickProfileImage(event: Event, userInfo: UserInfoTypes): void {}
+
+ onChangeCheck(value: boolean, userInfo: UserInfoTypes) {
+ this.checked.emit({
+ isChecked: value,
+ userInfo
+ });
+ }
+
+ getPresence(type: PresenceType): string {
+ let status: string;
+ let rtnClass = '';
+ switch (type) {
+ case PresenceType.PC:
+ status = !!this.presence ? this.presence.pcStatus : undefined;
+ break;
+ case PresenceType.MOBILE:
+ status = !!this.presence ? this.presence.mobileStatus : undefined;
+ break;
+ }
+
+ switch (status) {
+ case StatusCode.OnLine:
+ rtnClass = 'online';
+ break;
+ case StatusCode.Away:
+ rtnClass = 'absence';
+ break;
+ case StatusCode.Busy:
+ rtnClass = 'other-business';
+ break;
+ default:
+ rtnClass = 'offline';
+ break;
+ }
+
+ return rtnClass;
+ }
+
+ getPresenceMsg(): string {
+ let presenceMsg = this.i18nService.t('presence.offline');
+
+ if (!!this.presence) {
+ switch (this.presence.pcStatus) {
+ case StatusCode.OnLine:
+ presenceMsg = this.i18nService.t('presence.online');
+ break;
+ case StatusCode.Away:
+ presenceMsg = this.i18nService.t('presence.away');
+ break;
+ case StatusCode.Busy:
+ if (
+ !!this.presence.statusMessage &&
+ this.presence.statusMessage !== '.'
+ ) {
+ presenceMsg = this.presence.statusMessage;
+ } else {
+ presenceMsg = this.i18nService.t('presence.statusMessage1');
+ }
+ break;
+ }
+ }
+
+ return presenceMsg;
+ }
+
+ onClickMore(event: MouseEvent) {
+ this.isClickMore = true;
+ const rect = this.elementRef.nativeElement.getBoundingClientRect();
+
+ this.moreMenu.emit({
+ event,
+ userInfo: this.userInfo,
+ group: this.group,
+ rect
+ });
+ }
+
+ onClickProfileContextMenu(event: MouseEvent, type: string) {}
+ onClickProfile(event: MouseEvent) {
+ event.preventDefault();
+ event.stopPropagation();
+ if (this.showMenu && !this.isMe) {
+ this.isShowMenu = true;
+ }
+ }
+ onMouseover(event: MouseEvent): void {
+ if (this.showMenu && !this.isMe) {
+ this.isShowMenu = true;
+ if (this.isClickMore) {
+ this.isClickMore = false;
+ }
+ }
+ event.preventDefault();
+ event.stopPropagation();
+ }
+ onMouseleave(event: MouseEvent): void {
+ if (this.showMenu && !this.isMe) {
+ this.isShowMenu = false;
+ }
+
+ event.preventDefault();
+ event.stopPropagation();
+ }
+}
diff --git a/src/app/ucap/group/group.module.ts b/src/app/ucap/group/group.module.ts
new file mode 100644
index 0000000..4b05f46
--- /dev/null
+++ b/src/app/ucap/group/group.module.ts
@@ -0,0 +1,54 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule, ReactiveFormsModule } from '@angular/forms';
+
+import { FlexLayoutModule } from '@angular/flex-layout';
+
+import { MatCheckboxModule } from '@angular/material/checkbox';
+import { MatFormFieldModule } from '@angular/material/form-field';
+import { MatIconModule } from '@angular/material/icon';
+import { MatDividerModule } from '@angular/material/divider';
+import { MatMenuModule } from '@angular/material/menu';
+import { MatInputModule } from '@angular/material/input';
+import { MatButtonModule } from '@angular/material/button';
+import { MatTooltipModule } from '@angular/material/tooltip';
+
+import { I18nModule, UCAP_I18N_NAMESPACE } from '@ucap/ng-i18n';
+import { UiModule } from '@ucap/ng-ui';
+import { GroupUiModule } from '@ucap/ng-ui-group';
+import { OrganizationUiModule } from '@ucap/ng-ui-organization';
+
+import { COMPONENTS } from './components';
+
+@NgModule({
+ imports: [
+ CommonModule,
+ FormsModule,
+ ReactiveFormsModule,
+ FlexLayoutModule,
+
+ MatInputModule,
+ MatIconModule,
+ MatCheckboxModule,
+ MatFormFieldModule,
+ MatDividerModule,
+ MatMenuModule,
+ MatButtonModule,
+ MatTooltipModule,
+
+ I18nModule,
+ UiModule,
+ OrganizationUiModule,
+ GroupUiModule
+ ],
+ exports: [...COMPONENTS],
+ declarations: [...COMPONENTS],
+ entryComponents: [],
+ providers: [
+ {
+ provide: UCAP_I18N_NAMESPACE,
+ useValue: ['group', 'common', 'organization']
+ }
+ ]
+})
+export class AppGroupModule {}
diff --git a/src/app/ucap/organization/components/index.ts b/src/app/ucap/organization/components/index.ts
new file mode 100644
index 0000000..4503211
--- /dev/null
+++ b/src/app/ucap/organization/components/index.ts
@@ -0,0 +1,11 @@
+import { Profile01Component } from './profile-01.component';
+import { ProfileListComponent } from './profile-list.component';
+import { SearchForTenantComponent } from './search-for-tenant.component';
+import { TreeComponent } from './tree.component';
+
+export const COMPONENTS = [
+ Profile01Component,
+ ProfileListComponent,
+ SearchForTenantComponent,
+ TreeComponent
+];
diff --git a/src/app/ucap/organization/components/profile-01.component.html b/src/app/ucap/organization/components/profile-01.component.html
new file mode 100644
index 0000000..d114692
--- /dev/null
+++ b/src/app/ucap/organization/components/profile-01.component.html
@@ -0,0 +1,30 @@
+
+
+
+
diff --git a/src/app/ucap/organization/components/profile-01.component.scss b/src/app/ucap/organization/components/profile-01.component.scss
new file mode 100644
index 0000000..bcd8c56
--- /dev/null
+++ b/src/app/ucap/organization/components/profile-01.component.scss
@@ -0,0 +1,220 @@
+@charset 'UTF-8';
+
+@import '~@ucap/ui-scss/ucap';
+
+.profile-container {
+ width: 100%;
+ height: 100%;
+ background-image: url(/assets/images/bg/bg_profile1.svg),
+ url(../../../../assets/images/bg/bg_profile2.svg),
+ url(../../../../assets/images/bg/bg_profile3.svg),
+ url(../../../../assets/images/bg/bg_profile4.svg),
+ url(../../../../assets/images/bg/bg_profile5.svg),
+ linear-gradient(57deg, #ffe1ba 13%, #f5878c 87%);
+ background-repeat: no-repeat;
+ background-position: -213px -223px, 433px 95px, 489px 72px, 433px 517px,
+ 335px 634px, 0 0;
+}
+
+.ucap-organization-profile-01-container {
+ width: 100%;
+ height: 100%;
+
+ min-width: 450px;
+ @include screen(mid) {
+ height: auto;
+ }
+
+ .profile-card-box {
+ display: flex;
+ flex-direction: column;
+ padding: 60px 8.7%;
+ width: 100%;
+ position: relative;
+ .user-profile-info {
+ display: inline-flex;
+ flex-direction: row;
+ align-items: center;
+ // Profile thumb//////////////////
+ .user-profile-thumb {
+ // @include profile-avatar-default(
+ // 10px 0 0,
+ // 18.6,
+ // $green,
+ // 30px
+ // ); //오른 아래 공간, 모바일 온라인 아이콘 크기, 모바일 아이콘 색, 모바일 아이콘 bg크기
+ padding: 10px 0 0;
+ margin-left: -14px;
+ align-self: start;
+ .presence {
+ //PC 상태
+ // @include presence-state(14px); //원크기
+ margin-top: -10px;
+ }
+ .profileImage {
+ // @include avatar-img(128px, 0); //아바타 크기, 왼쪽공간
+ border: 3px solid #ffffff;
+ }
+ .btn-profile-ctrl {
+ position: absolute;
+ bottom: 0;
+ right: 0;
+ }
+ }
+ //////////////////Profile thumb //
+ .userInfo {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ justify-content: center;
+ padding-left: 38px;
+ .user-n-g {
+ display: flex;
+ flex-flow: row-reverse nowrap;
+ align-items: flex-end;
+ height: 44px;
+ .name {
+ font: {
+ size: 26px;
+ weight: 600;
+ }
+ order: 1;
+ -ms-flex-order: 1;
+ }
+ .grade {
+ font: {
+ size: 18px;
+ }
+ color: #f1f1f1;
+ margin-left: 6px;
+ order: 0;
+ -ms-flex-order: 0;
+ }
+ & + .deptName {
+ margin-top: 9px;
+ }
+ }
+ .deptName {
+ font-size: 22px;
+ line-height: 25px;
+ font-weight: 600;
+ }
+ .nickName {
+ display: flex;
+ flex-direction: row;
+ margin-top: 18px;
+ align-items: center;
+ .nickName-info {
+ padding: 0 16px;
+ height: 30px;
+ line-height: 30px;
+ border-radius: 15px;
+ border: solid 1px #fc5182;
+ background-color: rgba(255, 255, 255, 0.95);
+ font-size: 14px;
+ }
+ button {
+ }
+ }
+ .address-txt {
+ font-size: 16px;
+ line-height: 21px;
+ margin-top: 20px;
+ }
+ }
+ .btn-profile-add {
+ position: absolute;
+ z-index: 5;
+ top: 40px;
+ right: 40px;
+ button {
+ margin: 0 2px;
+ &.btn-star-add {
+ line-height: 24px !important;
+ }
+ }
+ }
+ }
+ .btn-partner-set {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 0 25px;
+ border-top: 1px solid rgba(255, 255, 255, 0.8);
+ border-bottom: 1px solid rgba(255, 255, 255, 0.8);
+ height: 70px;
+ margin-top: 30px;
+ img {
+ vertical-align: top;
+ }
+ }
+ .my-input {
+ display: flex;
+ flex-flow: row nowrap;
+ justify-content: space-between;
+ margin: 0;
+ width: 100%;
+ margin-top: 78px;
+ .my-in-input {
+ font-size: 16px;
+ flex-grow: 1;
+ height: 24px;
+ line-height: 24px;
+ margin-top: 8px;
+ }
+ button {
+ margin-bottom: 5px;
+ }
+ }
+ .user-profile-info-list {
+ margin-top: 60px;
+ ul {
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ height: 250px;
+ li {
+ font-size: 16px;
+ font-weight: 600;
+ color: #7f5360;
+ span {
+ width: 100px;
+ height: 34px;
+ border-radius: 18px;
+ border: solid 1px #f8f9fd;
+ background-color: #aaa0a5;
+ font-size: 14px;
+ font-weight: 600;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ color: #ffffff;
+ margin-right: 40px;
+ }
+ }
+ }
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////// 제거
+ .profile-card {
+ //mat-card
+ width: 100%;
+ background-color: transparent;
+ box-shadow: none;
+ .profileImage {
+ width: 126px;
+ height: 126px;
+ border-radius: 50%;
+ border: 3px solid #ffffff;
+ box-sizing: border-box;
+ overflow: hidden;
+ display: flex;
+ img {
+ display: flex;
+ align-items: center;
+ }
+ }
+ }
+ /////////////////////////////////////////////////////////////////////////////////////
+}
diff --git a/src/app/ucap/organization/components/profile-01.component.spec.ts b/src/app/ucap/organization/components/profile-01.component.spec.ts
new file mode 100644
index 0000000..c9660ae
--- /dev/null
+++ b/src/app/ucap/organization/components/profile-01.component.spec.ts
@@ -0,0 +1,32 @@
+import { TestBed, async } from '@angular/core/testing';
+import { RouterTestingModule } from '@angular/router/testing';
+import { Profile01Component } from './profile-01.component';
+
+describe('app::ucap::organization::Profile01Component', () => {
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ imports: [RouterTestingModule],
+ declarations: [Profile01Component]
+ }).compileComponents();
+ }));
+
+ it('should create the app', () => {
+ const fixture = TestBed.createComponent(Profile01Component);
+ const app = fixture.componentInstance;
+ expect(app).toBeTruthy();
+ });
+
+ it(`should have as title 'ucap-lg-web'`, () => {
+ const fixture = TestBed.createComponent(Profile01Component);
+ const app = fixture.componentInstance;
+ });
+
+ it('should render title', () => {
+ const fixture = TestBed.createComponent(Profile01Component);
+ fixture.detectChanges();
+ const compiled = fixture.nativeElement;
+ expect(compiled.querySelector('.content span').textContent).toContain(
+ 'ucap-lg-web app is running!'
+ );
+ });
+});
diff --git a/src/app/ucap/organization/components/profile-01.component.ts b/src/app/ucap/organization/components/profile-01.component.ts
new file mode 100644
index 0000000..1f6f615
--- /dev/null
+++ b/src/app/ucap/organization/components/profile-01.component.ts
@@ -0,0 +1,243 @@
+import { Subject } from 'rxjs';
+import { takeUntil, take, map, withLatestFrom, tap } from 'rxjs/operators';
+
+import {
+ Component,
+ OnInit,
+ OnDestroy,
+ ChangeDetectionStrategy,
+ ChangeDetectorRef,
+ Input,
+ ElementRef,
+ ViewChild
+} from '@angular/core';
+
+import { Store, select } from '@ngrx/store';
+
+import { LoginResponse } from '@ucap/protocol-authentication';
+import { AuthResponse, UserInfoSS } from '@ucap/protocol-query';
+
+import { LogService } from '@ucap/ng-logger';
+import {
+ LoginSelector,
+ AuthorizationSelector,
+ ConfigurationSelector,
+ LoginActions
+} from '@ucap/ng-store-authentication';
+import { VersionInfo2Response } from '@ucap/api-public';
+import { QueryProtocolService } from '@ucap/ng-protocol-query';
+import { PresenceType, StatusCode } from '@ucap/core';
+import { PresenceSelector } from '@ucap/ng-store-organization';
+import { StatusBulkInfo } from '@ucap/protocol-status';
+import { I18nService } from '@ucap/ng-i18n';
+import { FileUploadItem } from '@ucap/api';
+import { BuddySelector, BuddyActions } from '@ucap/ng-store-group';
+
+import { UserInfoUpdateType } from '@ucap/protocol-info';
+@Component({
+ selector: 'app-organization-profile-01',
+ templateUrl: './profile-01.component.html',
+ styleUrls: ['./profile-01.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class Profile01Component implements OnInit, OnDestroy {
+ @Input()
+ set userSeq(seq: string) {
+ this._userSeq = seq;
+ if (!!this.loginRes) {
+ this.refreshProfile();
+ }
+ }
+ get userSeq(): string {
+ return this._userSeq;
+ }
+ _userSeq: string;
+
+ @ViewChild('profileImageFileInput', { static: false })
+ profileImageFileInput: ElementRef;
+
+ profileImageFileUploadItem: FileUploadItem;
+
+ userInfo: UserInfoSS;
+
+ loginRes: LoginResponse;
+ authRes: AuthResponse;
+ versionInfo2Res: VersionInfo2Response;
+ presence: StatusBulkInfo;
+
+ String = String;
+
+ private ngOnDestroySubject: Subject;
+
+ constructor(
+ private queryProtocolService: QueryProtocolService,
+ private store: Store,
+ private changeDetectorRef: ChangeDetectorRef,
+ private logService: LogService,
+ private i18nService: I18nService
+ ) {}
+
+ isBuddy = false;
+ PresenceType = PresenceType;
+ isFavorite = false;
+ ngOnInit(): void {
+ this.ngOnDestroySubject = new Subject();
+
+ this.store
+ .pipe(
+ takeUntil(this.ngOnDestroySubject),
+ select(ConfigurationSelector.versionInfo2Response)
+ )
+ .subscribe((versionInfo2Res) => {
+ this.versionInfo2Res = versionInfo2Res;
+ });
+
+ this.store
+ .pipe(takeUntil(this.ngOnDestroySubject), select(LoginSelector.loginRes))
+ .subscribe((loginRes) => {
+ this.loginRes = loginRes;
+ this.userSeq = this.loginRes.userSeq;
+ });
+
+ this.store
+ .pipe(
+ takeUntil(this.ngOnDestroySubject),
+ select(AuthorizationSelector.authResponse)
+ )
+ .subscribe((authRes) => {
+ this.authRes = authRes;
+ });
+
+ this.store
+ .pipe(
+ takeUntil(this.ngOnDestroySubject),
+ select(PresenceSelector.selectEntitiesStatusBulkInfo)
+ )
+ .subscribe((statusBulkInfo) => {
+ this.presence = !!statusBulkInfo
+ ? statusBulkInfo[this.userSeq]
+ : undefined;
+ });
+ }
+
+ ngOnDestroy(): void {
+ if (!!this.ngOnDestroySubject) {
+ this.ngOnDestroySubject.complete();
+ }
+ }
+
+ getPresence(type: PresenceType): string {
+ let status: string;
+ let rtnClass = '';
+ switch (type) {
+ case PresenceType.PC:
+ status = !!this.presence ? this.presence.pcStatus : undefined;
+ break;
+ case PresenceType.MOBILE:
+ status = !!this.presence ? this.presence.mobileStatus : undefined;
+ break;
+ }
+
+ switch (status) {
+ case StatusCode.OnLine:
+ rtnClass = 'online';
+ break;
+ case StatusCode.Away:
+ rtnClass = 'absence';
+ break;
+ case StatusCode.Busy:
+ rtnClass = 'other-business';
+ break;
+ default:
+ rtnClass = 'offline';
+ break;
+ }
+
+ return rtnClass;
+ }
+
+ getPresenceMsg(): string {
+ let presenceMsg = this.i18nService.t('presence.offline');
+
+ if (!!this.presence) {
+ switch (this.presence.pcStatus) {
+ case StatusCode.OnLine:
+ presenceMsg = this.i18nService.t('presence.online');
+ break;
+ case StatusCode.Away:
+ presenceMsg = this.i18nService.t('presence.away');
+ break;
+ case StatusCode.Busy:
+ if (
+ !!this.presence.statusMessage &&
+ this.presence.statusMessage !== '.'
+ ) {
+ presenceMsg = this.presence.statusMessage;
+ } else {
+ presenceMsg = this.i18nService.t('presence.statusMessage1');
+ }
+ break;
+ }
+ }
+
+ return presenceMsg;
+ }
+
+ refreshProfile() {
+ this.queryProtocolService
+ .dataUser({
+ divCd: 'OPENPROF',
+ seq: Number(this.userSeq),
+ senderCompanyCode: this.loginRes.userInfo.companyCode,
+ senderEmployeeType: this.loginRes.userInfo.employeeType
+ })
+ .pipe(
+ take(1),
+ withLatestFrom(this.store.pipe(select(BuddySelector.buddies))),
+ tap(([res, buddies]) => {
+ this.userInfo = res.userInfo;
+ const users = buddies.filter(
+ (buddy) => buddy.seq + '' === this.userInfo.seq
+ );
+ this.isBuddy = users.length > 0;
+ if (this.isBuddy) {
+ this.isFavorite = users[0].isFavorit;
+ }
+ this.changeDetectorRef.detectChanges();
+ })
+ )
+ .subscribe();
+ }
+ onProfileImageView() {}
+ onChangeFileInput() {}
+ onChangeIntro(intro: string) {
+ console.log(intro);
+ this.store.dispatch(
+ LoginActions.infoUser({
+ req: {
+ type: UserInfoUpdateType.Intro,
+ info: intro
+ }
+ })
+ );
+ }
+ onChangeNickname(nickname: string) {
+ console.log(nickname);
+ }
+
+ onToggleFavorite(params: { userInfo: UserInfoSS; isFavorite: boolean }) {
+ this.store.dispatch(
+ BuddyActions.update({
+ req: { seq: Number(params.userInfo.seq), isFavorit: params.isFavorite }
+ })
+ );
+ }
+ onToggleBuddy(params: { userInfo: UserInfoSS; isBuddy: boolean }) {}
+ onOpenChat(userInfo: UserInfoSS) {}
+ onSendCall(calleeNumber: string) {}
+ onSendSms(smsNum: string) {}
+ onCreateConference(userSeq: number) {}
+ onSendMessage(userInfo: UserInfoSS) {}
+
+ onUploadProfileImage(profileImageFileUploadItem: FileUploadItem) {}
+}
diff --git a/src/app/ucap/organization/components/profile-list.component.html b/src/app/ucap/organization/components/profile-list.component.html
new file mode 100644
index 0000000..e35e67a
--- /dev/null
+++ b/src/app/ucap/organization/components/profile-list.component.html
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/app/ucap/organization/components/profile-list.component.scss b/src/app/ucap/organization/components/profile-list.component.scss
new file mode 100644
index 0000000..be45828
--- /dev/null
+++ b/src/app/ucap/organization/components/profile-list.component.scss
@@ -0,0 +1,4 @@
+.profile-list-container {
+ width: 100%;
+ height: 100%;
+}
diff --git a/src/app/ucap/organization/components/profile-list.component.spec.ts b/src/app/ucap/organization/components/profile-list.component.spec.ts
new file mode 100644
index 0000000..c9660ae
--- /dev/null
+++ b/src/app/ucap/organization/components/profile-list.component.spec.ts
@@ -0,0 +1,32 @@
+import { TestBed, async } from '@angular/core/testing';
+import { RouterTestingModule } from '@angular/router/testing';
+import { Profile01Component } from './profile-01.component';
+
+describe('app::ucap::organization::Profile01Component', () => {
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ imports: [RouterTestingModule],
+ declarations: [Profile01Component]
+ }).compileComponents();
+ }));
+
+ it('should create the app', () => {
+ const fixture = TestBed.createComponent(Profile01Component);
+ const app = fixture.componentInstance;
+ expect(app).toBeTruthy();
+ });
+
+ it(`should have as title 'ucap-lg-web'`, () => {
+ const fixture = TestBed.createComponent(Profile01Component);
+ const app = fixture.componentInstance;
+ });
+
+ it('should render title', () => {
+ const fixture = TestBed.createComponent(Profile01Component);
+ fixture.detectChanges();
+ const compiled = fixture.nativeElement;
+ expect(compiled.querySelector('.content span').textContent).toContain(
+ 'ucap-lg-web app is running!'
+ );
+ });
+});
diff --git a/src/app/ucap/organization/components/profile-list.component.ts b/src/app/ucap/organization/components/profile-list.component.ts
new file mode 100644
index 0000000..5127429
--- /dev/null
+++ b/src/app/ucap/organization/components/profile-list.component.ts
@@ -0,0 +1,160 @@
+import { Subject, of } from 'rxjs';
+import { takeUntil, take, map, catchError } from 'rxjs/operators';
+
+import {
+ Component,
+ OnInit,
+ OnDestroy,
+ ChangeDetectionStrategy,
+ ChangeDetectorRef,
+ Input
+} from '@angular/core';
+
+import { Store, select } from '@ngrx/store';
+
+import { VersionInfo2Response } from '@ucap/api-public';
+import { LoginResponse } from '@ucap/protocol-authentication';
+import { AuthResponse, UserInfoSS, DeptSearchType } from '@ucap/protocol-query';
+
+import { LogService } from '@ucap/ng-logger';
+import {
+ LoginSelector,
+ AuthorizationSelector,
+ ConfigurationSelector
+} from '@ucap/ng-store-authentication';
+import { QueryProtocolService } from '@ucap/ng-protocol-query';
+import {
+ DepartmentSelector,
+ PresenceActions
+} from '@ucap/ng-store-organization';
+
+@Component({
+ selector: 'app-organization-profile-list',
+ templateUrl: './profile-list.component.html',
+ styleUrls: ['./profile-list.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class ProfileListComponent implements OnInit, OnDestroy {
+ @Input()
+ set searchData(data: {
+ companyCode: string;
+ searchWord: string;
+ isSearch: boolean;
+ }) {
+ if (!this.loginRes) {
+ return;
+ }
+ this.processing = true;
+
+ this.queryProtocolService
+ .deptUser({
+ divCd: 'ORGS',
+ companyCode: data.companyCode,
+ searchRange: DeptSearchType.All,
+ search: data.searchWord,
+ senderCompanyCode: this.loginRes.userInfo.companyCode,
+ senderEmployeeType: this.loginRes.userInfo.employeeType
+ })
+ .pipe(take(1))
+ .subscribe(
+ (res) => {
+ // 검색 결과 처리.
+ this.userInfos = res.userInfos.sort((a, b) =>
+ a.name < b.name ? -1 : a.name > b.name ? 1 : 0
+ );
+ this.changeDetectorRef.detectChanges();
+
+ // 검색 결과에 따른 프레즌스 조회.
+ const userSeqList: string[] = [];
+ this.userInfos.map((user) => userSeqList.push(user.seq));
+
+ if (userSeqList.length > 0) {
+ this.store.dispatch(
+ PresenceActions.bulkInfo({
+ divCd: 'orgSrch',
+ userSeqs: userSeqList
+ })
+ );
+ }
+ },
+ (error) => {},
+ () => {
+ this.processing = false;
+ }
+ );
+ }
+
+ loginRes: LoginResponse;
+ versionInfo2Res: VersionInfo2Response;
+
+ userInfos: UserInfoSS[] = [];
+ selectedUserInfos: UserInfoSS[] = [];
+ processing = false;
+
+ private ngOnDestroySubject: Subject;
+
+ constructor(
+ private queryProtocolService: QueryProtocolService,
+ private store: Store,
+ private changeDetectorRef: ChangeDetectorRef,
+ private logService: LogService
+ ) {}
+
+ ngOnInit(): void {
+ this.ngOnDestroySubject = new Subject();
+
+ this.store
+ .pipe(
+ takeUntil(this.ngOnDestroySubject),
+ select(ConfigurationSelector.versionInfo2Response)
+ )
+ .subscribe((versionInfo2Res) => {
+ this.versionInfo2Res = versionInfo2Res;
+ });
+
+ this.store
+ .pipe(takeUntil(this.ngOnDestroySubject), select(LoginSelector.loginRes))
+ .subscribe((loginRes) => {
+ this.loginRes = loginRes;
+ });
+ }
+
+ ngOnDestroy(): void {
+ if (!!this.ngOnDestroySubject) {
+ this.ngOnDestroySubject.complete();
+ }
+ }
+
+ /** 개별 체크여부 */
+ getCheckedUser(userInfo: UserInfoSS) {
+ if (!!this.selectedUserInfos && this.selectedUserInfos.length > 0) {
+ return (
+ this.selectedUserInfos.filter((item) => item.seq === userInfo.seq)
+ .length > 0
+ );
+ }
+ return false;
+ }
+
+ /** 개별선택(토글) 이벤트 */
+ onToggleUser(param: { isChecked: boolean; userInfo: UserInfoSS }) {
+ if (!this.loginRes || param.userInfo.seq === this.loginRes.userSeq) {
+ return;
+ }
+
+ if (
+ !this.selectedUserInfos.some((user) => user.seq === param.userInfo.seq)
+ ) {
+ this.selectedUserInfos = [...this.selectedUserInfos, param.userInfo];
+ } else {
+ this.selectedUserInfos = this.selectedUserInfos.filter(
+ (item) => item.seq !== param.userInfo.seq
+ );
+ }
+ this.changeDetectorRef.detectChanges();
+ }
+
+ onOpenProfile(userInfo: UserInfoSS): void {
+ alert('Open Profile');
+ }
+}
diff --git a/src/app/ucap/organization/components/search-for-tenant.component.html b/src/app/ucap/organization/components/search-for-tenant.component.html
new file mode 100644
index 0000000..ec94b56
--- /dev/null
+++ b/src/app/ucap/organization/components/search-for-tenant.component.html
@@ -0,0 +1,10 @@
+
+
+
+
diff --git a/src/app/ucap/organization/components/search-for-tenant.component.scss b/src/app/ucap/organization/components/search-for-tenant.component.scss
new file mode 100644
index 0000000..6d08ee7
--- /dev/null
+++ b/src/app/ucap/organization/components/search-for-tenant.component.scss
@@ -0,0 +1,10 @@
+.search-container {
+ width: 100%;
+ height: 100%;
+
+ padding: 4px 30px;
+ background-color: white;
+ height: 48px;
+ flex-basis: 48px;
+ align-self: center;
+}
diff --git a/src/app/ucap/organization/components/search-for-tenant.component.spec.ts b/src/app/ucap/organization/components/search-for-tenant.component.spec.ts
new file mode 100644
index 0000000..81f2e64
--- /dev/null
+++ b/src/app/ucap/organization/components/search-for-tenant.component.spec.ts
@@ -0,0 +1,32 @@
+import { TestBed, async } from '@angular/core/testing';
+import { RouterTestingModule } from '@angular/router/testing';
+import { TreeComponent } from './tree.component';
+
+describe('app::ucap::organization::TreeComponent', () => {
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ imports: [RouterTestingModule],
+ declarations: [TreeComponent]
+ }).compileComponents();
+ }));
+
+ it('should create the app', () => {
+ const fixture = TestBed.createComponent(TreeComponent);
+ const app = fixture.componentInstance;
+ expect(app).toBeTruthy();
+ });
+
+ it(`should have as title 'ucap-lg-web'`, () => {
+ const fixture = TestBed.createComponent(TreeComponent);
+ const app = fixture.componentInstance;
+ });
+
+ it('should render title', () => {
+ const fixture = TestBed.createComponent(TreeComponent);
+ fixture.detectChanges();
+ const compiled = fixture.nativeElement;
+ expect(compiled.querySelector('.content span').textContent).toContain(
+ 'ucap-lg-web app is running!'
+ );
+ });
+});
diff --git a/src/app/ucap/organization/components/search-for-tenant.component.ts b/src/app/ucap/organization/components/search-for-tenant.component.ts
new file mode 100644
index 0000000..d4b7182
--- /dev/null
+++ b/src/app/ucap/organization/components/search-for-tenant.component.ts
@@ -0,0 +1,93 @@
+import { Subject } from 'rxjs';
+import { takeUntil } from 'rxjs/operators';
+
+import {
+ Component,
+ OnInit,
+ OnDestroy,
+ ChangeDetectionStrategy,
+ ChangeDetectorRef,
+ Output,
+ EventEmitter
+} from '@angular/core';
+
+import { Store, select } from '@ngrx/store';
+
+import { Company } from '@ucap/api-external';
+
+import { LogService } from '@ucap/ng-logger';
+import { CompanySelector } from '@ucap/ng-store-organization';
+
+import { AppAuthenticationService } from '@app/services/app-authentication.service';
+
+@Component({
+ selector: 'app-organization-search-for-tenant',
+ templateUrl: './search-for-tenant.component.html',
+ styleUrls: ['./search-for-tenant.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class SearchForTenantComponent implements OnInit, OnDestroy {
+ @Output()
+ changed: EventEmitter<{
+ isShowSearch: boolean;
+ companyCode: string;
+ searchWord: string;
+ }> = new EventEmitter();
+
+ @Output()
+ canceled: EventEmitter = new EventEmitter();
+
+ companyList: Company[];
+ defaultCompany: string;
+
+ private ngOnDestroySubject = new Subject();
+
+ constructor(
+ private appAuthenticationService: AppAuthenticationService,
+ private store: Store,
+ private changeDetectorRef: ChangeDetectorRef,
+ private logService: LogService
+ ) {}
+
+ ngOnInit(): void {
+ this.ngOnDestroySubject = new Subject();
+
+ const userStore = this.appAuthenticationService.getUserStore();
+ this.defaultCompany = userStore.companyCode;
+
+ this.store
+ .pipe(
+ takeUntil(this.ngOnDestroySubject),
+ select(CompanySelector.companyList)
+ )
+ .subscribe((companyList) => {
+ this.companyList = companyList;
+ });
+ }
+
+ ngOnDestroy(): void {
+ if (!!this.ngOnDestroySubject) {
+ this.ngOnDestroySubject.complete();
+ }
+ }
+
+ onChanged(data: { companyCode: string; searchWord: string }): void {
+ this.changed.emit({
+ ...data,
+ isShowSearch: true
+ });
+ }
+
+ onCanceled(): void {
+ this.changed.emit({
+ isShowSearch: false,
+ companyCode: '',
+ searchWord: ''
+ });
+ this.canceled.emit();
+ }
+
+ // onCanceled(): void {
+ // this.canceled.emit();
+ // }
+}
diff --git a/src/app/ucap/organization/components/tree.component.html b/src/app/ucap/organization/components/tree.component.html
new file mode 100644
index 0000000..683b4b3
--- /dev/null
+++ b/src/app/ucap/organization/components/tree.component.html
@@ -0,0 +1,9 @@
+
+
+
+
diff --git a/src/app/ucap/organization/components/tree.component.scss b/src/app/ucap/organization/components/tree.component.scss
new file mode 100644
index 0000000..582f4ce
--- /dev/null
+++ b/src/app/ucap/organization/components/tree.component.scss
@@ -0,0 +1,4 @@
+.tree-container {
+ width: 100%;
+ height: 100%;
+}
diff --git a/src/app/ucap/organization/components/tree.component.spec.ts b/src/app/ucap/organization/components/tree.component.spec.ts
new file mode 100644
index 0000000..81f2e64
--- /dev/null
+++ b/src/app/ucap/organization/components/tree.component.spec.ts
@@ -0,0 +1,32 @@
+import { TestBed, async } from '@angular/core/testing';
+import { RouterTestingModule } from '@angular/router/testing';
+import { TreeComponent } from './tree.component';
+
+describe('app::ucap::organization::TreeComponent', () => {
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ imports: [RouterTestingModule],
+ declarations: [TreeComponent]
+ }).compileComponents();
+ }));
+
+ it('should create the app', () => {
+ const fixture = TestBed.createComponent(TreeComponent);
+ const app = fixture.componentInstance;
+ expect(app).toBeTruthy();
+ });
+
+ it(`should have as title 'ucap-lg-web'`, () => {
+ const fixture = TestBed.createComponent(TreeComponent);
+ const app = fixture.componentInstance;
+ });
+
+ it('should render title', () => {
+ const fixture = TestBed.createComponent(TreeComponent);
+ fixture.detectChanges();
+ const compiled = fixture.nativeElement;
+ expect(compiled.querySelector('.content span').textContent).toContain(
+ 'ucap-lg-web app is running!'
+ );
+ });
+});
diff --git a/src/app/ucap/organization/components/tree.component.ts b/src/app/ucap/organization/components/tree.component.ts
new file mode 100644
index 0000000..6a2e35b
--- /dev/null
+++ b/src/app/ucap/organization/components/tree.component.ts
@@ -0,0 +1,135 @@
+import { Subject, combineLatest } from 'rxjs';
+import { takeUntil } from 'rxjs/operators';
+
+import {
+ Component,
+ OnInit,
+ OnDestroy,
+ ChangeDetectionStrategy,
+ ChangeDetectorRef,
+ EventEmitter,
+ Output,
+ ViewChild,
+ Input
+} from '@angular/core';
+
+import { Store, select } from '@ngrx/store';
+
+import {
+ FixedSizeVirtualScrollStrategy,
+ VIRTUAL_SCROLL_STRATEGY
+} from '@angular/cdk/scrolling';
+
+import { LoginResponse } from '@ucap/protocol-authentication';
+import { DeptInfo } from '@ucap/protocol-query';
+
+import { LogService } from '@ucap/ng-logger';
+import { LoginSelector } from '@ucap/ng-store-authentication';
+import { DepartmentSelector } from '@ucap/ng-store-organization';
+
+import { TreeComponent as UCAPTreeComponent } from '@ucap/ng-ui-organization';
+
+import { environment } from '@environments';
+
+export class TreeVirtualScrollStrategy extends FixedSizeVirtualScrollStrategy {
+ constructor() {
+ super(46, 250, 500); // (itemSize, minBufferPx, maxBufferPx)
+ }
+}
+
+@Component({
+ selector: 'app-organization-tree',
+ templateUrl: './tree.component.html',
+ styleUrls: ['./tree.component.scss'],
+ providers: [
+ {
+ provide: VIRTUAL_SCROLL_STRATEGY,
+ useClass: TreeVirtualScrollStrategy
+ }
+ ],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class TreeComponent implements OnInit, OnDestroy {
+ @Input()
+ set initialExpanded(seq: number) {
+ if (!!this.treeData) {
+ this.treeList.expand(seq);
+ return;
+ }
+ this._initialExpanded = seq;
+ }
+ // tslint:disable-next-line: variable-name
+ _initialExpanded: number;
+
+ @Output()
+ clicked: EventEmitter = new EventEmitter();
+
+ @ViewChild('treeList', { static: true })
+ treeList: UCAPTreeComponent;
+
+ treeData: {
+ deptInfoList: DeptInfo[];
+ displayRoot?: boolean;
+ expanded?: number[];
+ };
+
+ loginRes: LoginResponse;
+
+ private ngOnDestroySubject: Subject;
+
+ constructor(
+ private store: Store,
+ private changeDetectorRef: ChangeDetectorRef,
+ private logService: LogService
+ ) {}
+
+ ngOnInit(): void {
+ this.ngOnDestroySubject = new Subject();
+
+ combineLatest([
+ this.store.pipe(select(LoginSelector.loginRes)),
+ this.store.pipe(select(DepartmentSelector.departmentInfoList))
+ ])
+ .pipe(takeUntil(this.ngOnDestroySubject))
+ .subscribe(([loginRes, deptInfoList]) => {
+ this.loginRes = loginRes;
+ this.treeData = {
+ deptInfoList,
+ displayRoot: environment.productConfig.organization.displayRoot,
+ expanded: !!this._initialExpanded
+ ? [this._initialExpanded]
+ : undefined
+ };
+ this.changeDetectorRef.markForCheck();
+ if (!!loginRes && !!deptInfoList) {
+ this._initialExpanded = undefined;
+ }
+ });
+ }
+
+ ngOnDestroy(): void {
+ if (!!this.ngOnDestroySubject) {
+ this.ngOnDestroySubject.complete();
+ }
+ }
+
+ onClickNode(node: DeptInfo) {
+ this.clicked.emit(node);
+ }
+
+ expand(deptSeq: number) {
+ this.treeList.expand(deptSeq);
+ }
+
+ expandAll() {
+ this.treeList.expandAll();
+ }
+
+ collapse(deptSeq: number) {
+ this.treeList.collapse(deptSeq);
+ }
+
+ collapseAll() {
+ this.treeList.collapseAll();
+ }
+}
diff --git a/src/app/ucap/organization/components/tree.strategy.ts b/src/app/ucap/organization/components/tree.strategy.ts
new file mode 100644
index 0000000..564488d
--- /dev/null
+++ b/src/app/ucap/organization/components/tree.strategy.ts
@@ -0,0 +1,32 @@
+import { Observable, Subject } from 'rxjs';
+
+import {
+ VirtualScrollStrategy,
+ CdkVirtualScrollViewport
+} from '@angular/cdk/scrolling';
+import { distinctUntilChanged } from 'rxjs/operators';
+
+export class OrganizationTreeVirtualScrollStrategy
+ implements VirtualScrollStrategy {
+ scrolledIndexChange: Observable;
+
+ private indexSubject = new Subject();
+ private viewport: CdkVirtualScrollViewport | null = null;
+
+ constructor() {
+ this.scrolledIndexChange = this.indexSubject.pipe(distinctUntilChanged());
+ }
+
+ attach(viewport: CdkVirtualScrollViewport): void {
+ this.viewport = viewport;
+ }
+ detach(): void {
+ this.indexSubject.complete();
+ this.viewport = null;
+ }
+ onContentScrolled(): void {}
+ onDataLengthChanged(): void {}
+ onContentRendered(): void {}
+ onRenderedOffsetChanged(): void {}
+ scrollToIndex(index: number, behavior: ScrollBehavior): void {}
+}
diff --git a/src/app/ucap/organization/organization.module.ts b/src/app/ucap/organization/organization.module.ts
new file mode 100644
index 0000000..c29a7fd
--- /dev/null
+++ b/src/app/ucap/organization/organization.module.ts
@@ -0,0 +1,57 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule, ReactiveFormsModule } from '@angular/forms';
+
+import { FlexLayoutModule } from '@angular/flex-layout';
+
+import { PerfectScrollbarModule } from 'ngx-perfect-scrollbar';
+
+import { MatButtonModule } from '@angular/material/button';
+import { MatCheckboxModule } from '@angular/material/checkbox';
+import { MatFormFieldModule } from '@angular/material/form-field';
+import { MatIconModule } from '@angular/material/icon';
+import { MatInputModule } from '@angular/material/input';
+import { MatRippleModule } from '@angular/material/core';
+import { MatSelectModule } from '@angular/material/select';
+import { MatTreeModule } from '@angular/material/tree';
+import { MatTooltipModule } from '@angular/material/tooltip';
+
+import { I18nModule, UCAP_I18N_NAMESPACE } from '@ucap/ng-i18n';
+import { UiModule } from '@ucap/ng-ui';
+import { OrganizationUiModule } from '@ucap/ng-ui-organization';
+
+import { COMPONENTS } from './components';
+
+@NgModule({
+ imports: [
+ CommonModule,
+ FormsModule,
+ ReactiveFormsModule,
+ FlexLayoutModule,
+
+ MatButtonModule,
+ MatCheckboxModule,
+ MatFormFieldModule,
+ MatIconModule,
+ MatInputModule,
+ MatRippleModule,
+ MatSelectModule,
+ MatTreeModule,
+ MatTooltipModule,
+
+ PerfectScrollbarModule,
+ I18nModule,
+ UiModule,
+ OrganizationUiModule
+ ],
+ exports: [...COMPONENTS],
+ declarations: [...COMPONENTS],
+ entryComponents: [],
+ providers: [
+ {
+ provide: UCAP_I18N_NAMESPACE,
+ useValue: ['organization', 'common']
+ }
+ ]
+})
+export class AppOrganizationModule {}
diff --git a/src/assets/i18n/en/authentication.json b/src/assets/i18n/en/authentication.json
index daabf74..fcf54b4 100644
--- a/src/assets/i18n/en/authentication.json
+++ b/src/assets/i18n/en/authentication.json
@@ -7,7 +7,8 @@
"instructionsOfLogin": "LOGIN TO YOUR ACCOUNT",
"forgotPassword": "Forgot Password",
"resetPassword": "Reset Password",
- "NotesOnUse": "Notes on use"
+ "notesOnUse": "Notes on use",
+ "selectCompany": "Select company"
},
"fields": {
"company": "Company",
diff --git a/src/assets/i18n/en/chat.json b/src/assets/i18n/en/chat.json
index 0967ef4..008be84 100644
--- a/src/assets/i18n/en/chat.json
+++ b/src/assets/i18n/en/chat.json
@@ -1 +1,30 @@
-{}
+{
+ "room": {
+ "searchRoomByName": "Search by room name and user",
+ "noRoomUser": "No chat users",
+ "today": "Today",
+ "openRoom": "Open room",
+ "turnOnRoomAlert": "Turn on room alert",
+ "turnOffRoomAlert": "Turn off room alert",
+ "exitFromRoom": "Exit room",
+ "dialog": {
+ "titleExitFromRoom": "Exit room",
+ "confirmExitFromRoom": "Do you want to exit the chat room?
Exiting will delete your chat history and chat room information."
+ }
+ },
+ "label": {
+ "menu": "Menu",
+ "search": "Search",
+ "notificationIsOn": "Notification is on",
+ "notificationIsOff": "Notification is off",
+ "showRoomUsers": "Show room users",
+ "send": "Send",
+ "attachFile": "Attach file",
+ "attachImage": "Attach image",
+ "screenshot": "Send Screenshot",
+ "imoticon": "Imoticon",
+ "emailSend": "Send email for chat.",
+ "translation": "Trnaslation",
+ "gams": "+GAMS"
+ }
+}
diff --git a/src/assets/i18n/en/common.json b/src/assets/i18n/en/common.json
index 0967ef4..a838071 100644
--- a/src/assets/i18n/en/common.json
+++ b/src/assets/i18n/en/common.json
@@ -1 +1,22 @@
-{}
+{
+ "common": {
+ "messages": {
+ "no": "No",
+ "yes": "Yes"
+ },
+ "units": {
+ "date": "Date",
+ "time": "Time",
+ "hour": "hour",
+ "hourFrom": "hour",
+ "minute": "minute",
+ "second": "second",
+ "persons": "person(s)",
+ "hourLaterWith": "(An) {{hour}} hour(s) later",
+ "tomorrowMorning": "Tomorrow morning",
+ "tomorrowAfternoon": "Tomorrow afternoon",
+ "weekLaterWith": "(A) {{week}} week(s) later",
+ "monthLaterWith": "(A) {{month}} month(s) later"
+ }
+ }
+}
diff --git a/src/assets/i18n/en/group.json b/src/assets/i18n/en/group.json
index 0967ef4..348512d 100644
--- a/src/assets/i18n/en/group.json
+++ b/src/assets/i18n/en/group.json
@@ -1 +1,58 @@
-{}
+{
+ "label": {
+ "confirmRemoveBuddy": "Do you want to delete the member?
Deleted member apply only to your group and are not deleted from their group."
+ },
+ "category": {
+ "favorite": "Favorite",
+ "default": "Default",
+ "myDept": "My Dept"
+ },
+ "moreMenu": {
+ "show": {
+ "all": "View all",
+ "onlineBuddy": "View only connected buddy",
+ "onOff": "View online offline "
+ },
+ "group": {
+ "addNew": "Add new group",
+ "expandMore": "Expand all groups",
+ "expandLess": "Collapse all groups",
+ "changeOrder": "Change order",
+ "startChatWithGroup": "Chat with group",
+ "sendMessageToGroup": "Send message to group",
+ "groupMemberManagement": "Group Member Management",
+ "changeGroupName": "Change Group Name",
+ "removeGroup": "Remove group"
+ },
+ "profile": {
+ "open": "Open profile",
+ "favorite": "Favorite",
+ "nickname": "Nickname",
+ "moveBuddy": "Move Member",
+ "copyBuddy": "Copy Member",
+ "removeBuddy": "Remove Member"
+ },
+ "error": {
+ "label": "Group errors",
+ "requireName": "Group name is required."
+ }
+ },
+ "profile": {
+ "labels": {
+ "myProfile": "My profile",
+ "company": "Company",
+ "email": "Email",
+ "linePhoneNumber": "Office",
+ "mobilePhoneNumber": "Mobile",
+ "department": "Department",
+ "chat": "Chat",
+ "sms": "SMS",
+ "videoConference": "Conference",
+ "message": "Message"
+ },
+ "fields": {
+ "intro": "인트로"
+ },
+ "errors": {}
+ }
+}
diff --git a/src/assets/i18n/en/organization.json b/src/assets/i18n/en/organization.json
index 0967ef4..f98d3fe 100644
--- a/src/assets/i18n/en/organization.json
+++ b/src/assets/i18n/en/organization.json
@@ -1 +1,18 @@
-{}
+{
+ "label": {
+ "selectedUsers": "Selected Users",
+ "addGroup": "Add Group",
+ "chat": "Chat",
+ "message": "Message",
+ "call": "Call",
+ "videoConference": "Video Conference"
+ },
+ "presence": {
+ "offline": "Offline",
+ "online": "Online",
+ "away": "Away",
+ "statusMessage1": "Busy",
+ "statusMessage2": "In conference",
+ "statusMessage3": "In intensive work"
+ }
+}
diff --git a/src/assets/i18n/ko/authentication.json b/src/assets/i18n/ko/authentication.json
index 63b386f..5ef18c6 100644
--- a/src/assets/i18n/ko/authentication.json
+++ b/src/assets/i18n/ko/authentication.json
@@ -7,7 +7,8 @@
"instructionsOfLogin": "계정에 로그인 하세요.",
"forgotPassword": "비밀번호 찾기",
"resetPassword": "비밀번호 초기화",
- "NotesOnUse": "이용시 주의 사항"
+ "notesOnUse": "이용시 주의 사항",
+ "selectCompany": "회사 선택"
},
"fields": {
"company": "회사명",
diff --git a/src/assets/i18n/ko/chat.json b/src/assets/i18n/ko/chat.json
index 0967ef4..7db67db 100644
--- a/src/assets/i18n/ko/chat.json
+++ b/src/assets/i18n/ko/chat.json
@@ -1 +1,30 @@
-{}
+{
+ "room": {
+ "searchRoomByName": "대화방명, 대화 참석자 검색",
+ "noRoomUser": "대화상대 없음",
+ "today": "오늘",
+ "openRoom": "대화방 열기",
+ "turnOnRoomAlert": "대화방 알람 켜기",
+ "turnOffRoomAlert": "대화방 알람 끄기",
+ "exitFromRoom": "대화방 나가기",
+ "dialog": {
+ "titleExitFromRoom": "대화방 나가기",
+ "confirmExitFromRoom": "대화방을 나가시겠습니까?
나가기를 하면 대화내용 및 대화방 정보가 삭제됩니다."
+ }
+ },
+ "label": {
+ "menu": "메뉴",
+ "search": "검색",
+ "notificationIsOn": "알림 켜짐",
+ "notificationIsOff": "알림 꺼짐",
+ "showRoomUsers": "대화방 참여인원 보기",
+ "send": "전송",
+ "attachFile": "첨부파일",
+ "attachImage": "첨부이미지",
+ "screenshot": "캡쳐화면 전송",
+ "imoticon": "이모티콘",
+ "emailSend": "대화내용 메일전송",
+ "translation": "대화내용 번역",
+ "gams": "+GAMS"
+ }
+}
diff --git a/src/assets/i18n/ko/common.json b/src/assets/i18n/ko/common.json
index 0967ef4..53d9ed3 100644
--- a/src/assets/i18n/ko/common.json
+++ b/src/assets/i18n/ko/common.json
@@ -1 +1,22 @@
-{}
+{
+ "common": {
+ "messages": {
+ "no": "아니요",
+ "yes": "예"
+ },
+ "units": {
+ "date": "날짜",
+ "time": "시간",
+ "hour": "시",
+ "hourFrom": "시간",
+ "minute": "분",
+ "second": "초",
+ "persons": "명",
+ "hourLaterWith": "{{hour}}시간 뒤",
+ "tomorrowMorning": "내일 아침",
+ "tomorrowAfternoon": "내일 오후",
+ "weekLaterWith": "{{week}}주일 뒤",
+ "monthLaterWith": "{{month}}달 뒤"
+ }
+ }
+}
diff --git a/src/assets/i18n/ko/group.json b/src/assets/i18n/ko/group.json
index 0967ef4..049495e 100644
--- a/src/assets/i18n/ko/group.json
+++ b/src/assets/i18n/ko/group.json
@@ -1 +1,62 @@
-{}
+{
+ "label": {
+ "confirmRemoveBuddy": "선택한 멤버를 삭제하시겠습니까?\n해당 그룹에서만 선택하신 멤버가 삭제됩니다."
+ },
+ "category": {
+ "favorite": "즐겨찾기",
+ "default": "기본",
+ "myDept": "소속부서"
+ },
+ "moreMenu": {
+ "show": {
+ "all": "전체 보기",
+ "onlineBuddy": "접속한 동료만 보기",
+ "onOff": "온/오프라인 보기"
+ },
+ "group": {
+ "addNew": "새 그룹 추가",
+ "expandMore": "그룹 전체 열기",
+ "expandLess": "그룹 전체 닫기",
+ "changeOrder": "그룹 순서 바꾸기",
+ "startChatWithGroup": "그룹 대화하기",
+ "sendMessageToGroup": "그룹 쪽지 보내기",
+ "groupMemberManagement": "그룹 멤버 관리",
+ "changeGroupName": "그룹 이름 바꾸기",
+ "removeGroup": "그룹 삭제"
+ },
+ "profile": {
+ "open": "프로필 보기",
+ "favorite": "즐겨찾기 설정",
+ "nickname": "닉네임 설정",
+ "moveBuddy": "대화상대 이동",
+ "copyBuddy": "대화상대 복사",
+ "removeBuddy": "이 그룹에서 삭제"
+ },
+ "confirm": {
+ "removeGroup": "그룹을 삭제하시겠습니까?
그룹 멤버는 해당 그룹에서만 삭제됩니다."
+ },
+ "error": {
+ "label": "그룹 에러",
+ "requireName": "그룹명은 필수입력입니다."
+ }
+ },
+
+ "profile": {
+ "labels": {
+ "myProfile": "내 프로필",
+ "company": "회사",
+ "email": "이메일",
+ "linePhoneNumber": "사무실",
+ "mobilePhoneNumber": "핸드폰",
+ "department": "부서",
+ "chat": "대화",
+ "sms": "SMS",
+ "videoConference": "화상회의",
+ "message": "쪽지"
+ },
+ "fields": {
+ "intro": "인트로"
+ },
+ "errors": {}
+ }
+}
diff --git a/src/assets/i18n/ko/organization.json b/src/assets/i18n/ko/organization.json
index 0967ef4..2a16749 100644
--- a/src/assets/i18n/ko/organization.json
+++ b/src/assets/i18n/ko/organization.json
@@ -1 +1,18 @@
-{}
+{
+ "label": {
+ "selectedUsers": "선택된 대화상대",
+ "addGroup": "그룹추가",
+ "chat": "대화",
+ "message": "쪽지",
+ "call": "전화",
+ "videoConference": "화상"
+ },
+ "presence": {
+ "offline": "오프라인",
+ "online": "온라인",
+ "away": "부재중",
+ "statusMessage1": "다른용무중",
+ "statusMessage2": "회의중",
+ "statusMessage3": "집중근무중"
+ }
+}
diff --git a/src/assets/images/bg/bg_profile1.svg b/src/assets/images/bg/bg_profile1.svg
new file mode 100644
index 0000000..ff59766
--- /dev/null
+++ b/src/assets/images/bg/bg_profile1.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/assets/images/bg/bg_profile2.svg b/src/assets/images/bg/bg_profile2.svg
new file mode 100644
index 0000000..96d5c83
--- /dev/null
+++ b/src/assets/images/bg/bg_profile2.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/assets/images/bg/bg_profile3.svg b/src/assets/images/bg/bg_profile3.svg
new file mode 100644
index 0000000..81b3a49
--- /dev/null
+++ b/src/assets/images/bg/bg_profile3.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/assets/images/bg/bg_profile4.svg b/src/assets/images/bg/bg_profile4.svg
new file mode 100644
index 0000000..b2b52bf
--- /dev/null
+++ b/src/assets/images/bg/bg_profile4.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/assets/images/bg/bg_profile5.svg b/src/assets/images/bg/bg_profile5.svg
new file mode 100644
index 0000000..d05cf4e
--- /dev/null
+++ b/src/assets/images/bg/bg_profile5.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/assets/images/ico/btn_favorite_w24_s.svg b/src/assets/images/ico/btn_favorite_w24_s.svg
new file mode 100644
index 0000000..d668cf7
--- /dev/null
+++ b/src/assets/images/ico/btn_favorite_w24_s.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/src/assets/images/ico/btn_gnb_hompage.svg b/src/assets/images/ico/btn_gnb_hompage.svg
new file mode 100644
index 0000000..5f7989c
--- /dev/null
+++ b/src/assets/images/ico/btn_gnb_hompage.svg
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/assets/images/ico/btn_group_add_w24.svg b/src/assets/images/ico/btn_group_add_w24.svg
new file mode 100644
index 0000000..920e2b1
--- /dev/null
+++ b/src/assets/images/ico/btn_group_add_w24.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/src/assets/images/ico/btn_lise_chat_a24.svg b/src/assets/images/ico/btn_lise_chat_a24.svg
new file mode 100644
index 0000000..ff92ba7
--- /dev/null
+++ b/src/assets/images/ico/btn_lise_chat_a24.svg
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/src/assets/images/ico/btn_list_call_a24.svg b/src/assets/images/ico/btn_list_call_a24.svg
new file mode 100644
index 0000000..1bb41a5
--- /dev/null
+++ b/src/assets/images/ico/btn_list_call_a24.svg
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/src/assets/images/ico/btn_list_message_a24.svg b/src/assets/images/ico/btn_list_message_a24.svg
new file mode 100644
index 0000000..310f80d
--- /dev/null
+++ b/src/assets/images/ico/btn_list_message_a24.svg
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/src/assets/images/ico/btn_list_mobile_a24.svg b/src/assets/images/ico/btn_list_mobile_a24.svg
new file mode 100644
index 0000000..d167f0a
--- /dev/null
+++ b/src/assets/images/ico/btn_list_mobile_a24.svg
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/assets/images/ico/btn_list_vc-a24.svg b/src/assets/images/ico/btn_list_vc-a24.svg
new file mode 100644
index 0000000..e934217
--- /dev/null
+++ b/src/assets/images/ico/btn_list_vc-a24.svg
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/src/assets/images/ico/icon_bot_empty.svg b/src/assets/images/ico/icon_bot_empty.svg
new file mode 100644
index 0000000..59f247a
--- /dev/null
+++ b/src/assets/images/ico/icon_bot_empty.svg
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/assets/images/ico/img_nophoto.svg b/src/assets/images/ico/img_nophoto.svg
new file mode 100644
index 0000000..d90ebec
--- /dev/null
+++ b/src/assets/images/ico/img_nophoto.svg
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/assets/scss/_lg.scss b/src/assets/scss/_lg.scss
new file mode 100644
index 0000000..b1d7392
--- /dev/null
+++ b/src/assets/scss/_lg.scss
@@ -0,0 +1,9 @@
+// Material theming tools
+@import '~@angular/material/theming';
+
+// Include core Angular Material styles
+@include mat-core();
+
+//creative
+@import 'global/default';
+@import 'global/material-ui';
diff --git a/src/assets/scss/_mixins.scss b/src/assets/scss/_mixins.scss
new file mode 100644
index 0000000..a1ca939
--- /dev/null
+++ b/src/assets/scss/_mixins.scss
@@ -0,0 +1,12 @@
+// Material theming tools
+@import '~@angular/material/theming';
+
+@import '~@ucap/ng-ui-material/material';
+
+@import 'setting/variables';
+
+@import 'mixins/font';
+@import 'mixins/dom';
+
+@import 'mixins/ucap-organization';
+@import 'mixins/ucap-chat';
diff --git a/src/assets/scss/_ucap-theme.scss b/src/assets/scss/_ucap-theme.scss
new file mode 100644
index 0000000..42d347c
--- /dev/null
+++ b/src/assets/scss/_ucap-theme.scss
@@ -0,0 +1,77 @@
+@import '~@ucap/ng-ui/theme';
+@import '~@ucap/ng-ui-organization/theme';
+@import '~@ucap/ng-ui-authentication/theme';
+@import '~@ucap/ng-ui-group/theme';
+@import '~@ucap/ng-ui-chat/theme';
+
+@mixin ucap-ui-theme($theme, $typography) {
+ /// ng-ui
+ @include ucap-file-upload-queue-theme($theme);
+ @include ucap-file-viewer-theme($theme);
+ @include ucap-float-action-button-theme($theme);
+ @include ucap-inline-edit-input-theme($theme);
+ @include ucap-pick-date-theme($theme);
+ @include ucap-pick-time-theme($theme);
+ @include ucap-split-button-theme($theme);
+ @include ucap-step-input-theme($theme);
+ @include ucap-title-bar-theme($theme);
+ @include ucap-binary-viewer-theme($theme);
+ @include ucap-document-viewer-theme($theme);
+ @include ucap-image-viewer-theme($theme);
+ @include ucap-sound-viewer-theme($theme);
+ @include ucap-video-viewer-theme($theme);
+
+ /// organization
+ @include ucap-organization-profile-01-theme($theme);
+ @include ucap-organization-profile-list-item-01-theme($theme);
+ @include ucap-organization-profile-list-theme($theme);
+ @include ucap-organization-search-for-tenant-theme($theme);
+ @include ucap-organization-tree-theme($theme);
+
+ /// authentication
+ @include ucap-authentication-change-password-theme($theme);
+ @include ucap-authentication-login-theme($theme);
+
+ /// group
+ @include ucap-group-expansion-theme($theme);
+
+ /// chat
+ @include ucap-chat-room-expansion-theme($theme);
+ @include ucap-chat-room-list-item-01-theme($theme);
+
+ @if $typography != null {
+ /// ng-ui
+ @include ucap-file-upload-queue-typography($typography);
+ @include ucap-file-viewer-typography($typography);
+ @include ucap-float-action-button-typography($typography);
+ @include ucap-inline-edit-input-typography($typography);
+ @include ucap-pick-date-typography($typography);
+ @include ucap-pick-time-typography($typography);
+ @include ucap-split-button-typography($typography);
+ @include ucap-step-input-typography($typography);
+ @include ucap-title-bar-typography($typography);
+ @include ucap-binary-viewer-typography($typography);
+ @include ucap-document-viewer-typography($typography);
+ @include ucap-image-viewer-typography($typography);
+ @include ucap-sound-viewer-typography($typography);
+ @include ucap-video-viewer-typography($typography);
+
+ /// organization
+ @include ucap-organization-profile-01-typography($typography);
+ @include ucap-organization-profile-list-item-01-typography($typography);
+ @include ucap-organization-profile-list-typography($typography);
+ @include ucap-organization-search-for-tenant-typography($typography);
+ @include ucap-organization-tree-typography($typography);
+
+ /// authentication
+ @include ucap-authentication-change-password-typography($typography);
+ @include ucap-authentication-login-typography($typography);
+
+ /// group
+ @include ucap-group-expansion-typography($typography);
+
+ /// chat
+ @include ucap-chat-room-expansion-typography($typography);
+ @include ucap-chat-room-list-item-01-typography($typography);
+ }
+}
diff --git a/src/assets/scss/global/_material-ui.scss b/src/assets/scss/global/_material-ui.scss
new file mode 100644
index 0000000..605ee7b
--- /dev/null
+++ b/src/assets/scss/global/_material-ui.scss
@@ -0,0 +1,647 @@
+// .cdk-global-scrollblock {
+// overflow: hidden !important;
+// }
+
+@mixin ucap-material-theme($theme) {
+ @include ucap-core-theme($theme);
+}
+
+@mixin ucap-core-theme($theme) {
+ $accent: map-get($theme, accent);
+ $warn: map-get($theme, warn);
+ $primary: map-get($theme, primary);
+ $background: map-get($theme, background);
+ $foreground: map-get($theme, foreground);
+
+ $gradient-darkest: mat-color($accent, G900);
+ $gradient-light: mat-color($accent, G100);
+
+ //basic
+ .bg-primary-darkest {
+ background: mat-color($primary, 900);
+ color: mat-color($primary, default-contrast);
+ }
+ .bg-primary-dark {
+ background: mat-color($primary, 900);
+ color: mat-color($primary, default-contrast);
+ }
+ .bg-primary-light {
+ background: mat-color($primary, 300);
+ color: mat-color($primary, default-contrast);
+ }
+ .bg-primary-color {
+ background: mat-color($primary);
+ color: mat-color($primary, default-contrast);
+ }
+ .bg-accent-darkest {
+ background: mat-color($accent, 800);
+ color: mat-color($primary, default-contrast);
+ }
+ .bg-accent-dark {
+ background: mat-color($accent, B200);
+ color: mat-color($primary, default-contrast);
+ }
+ .bg-accent-light {
+ background: mat-color($accent, 300);
+ color: mat-color($primary, default-contrast);
+ }
+ .bg-accent-bright {
+ background-color: mat-color($accent, 100);
+ }
+ .bg-accent-brightest {
+ background: mat-color($accent, 50);
+ color: mat-color($primary, $dark-primary-text);
+ }
+ .bg-accent-color {
+ background: mat-color($accent);
+ color: mat-color($accent, default-contrast);
+ }
+ .bg-warn-color {
+ background-color: mat-color($warn, 300);
+ }
+ .bg-warn-darkest {
+ background-color: mat-color($warn, 600);
+ }
+ .text-primary-light {
+ color: mat-color($primary, 500);
+ }
+ .text-primary-color {
+ color: mat-color($primary);
+ }
+ .text-accent-dark {
+ color: mat-color($accent, 600);
+ }
+ .text-accent-darkest {
+ color: mat-color($accent, 800);
+ }
+ .text-accent-color {
+ color: mat-color($accent);
+ }
+ .text-warn-color {
+ color: mat-color($warn, 800);
+ }
+ .border-primary-color {
+ border-color: 1px solid mat-color($primary);
+ }
+ .border-accent-bright {
+ border-color: mat-color($accent, 300);
+ }
+ .border-accent-color {
+ border-color: 1px solid mat-color($accent);
+ }
+ .stroke-accent-darkest {
+ stroke: mat-color($accent, 800);
+ }
+ .border-warn-color {
+ border: mat-color($warn);
+ }
+ .stroke-warn-color {
+ stroke: mat-color($warn, 900);
+ }
+ // sass 정의
+ .mat-toolbar {
+ background-color: mat-color($accent, B100);
+ }
+ ///// gnb
+ .gnb .mat-toolbar {
+ background-color: $gray-ref0;
+ }
+
+ .main-container {
+ border-color: mat-color($accent, B100);
+ }
+ .global-menu {
+ background-color: mat-color($accent, B100);
+ }
+
+ .ucap-clickable {
+ cursor: pointer;
+ }
+
+ .policy {
+ color: mat-color($accent, B100);
+ }
+
+ .mat-tab-group.mat-primary .mat-ink-bar,
+ body.theme-default .mat-tab-nav-bar.mat-primary .mat-ink-bar {
+ background-color: mat-color($accent, 500);
+ }
+ .mat-chip.mat-standard-chip.mat-chip-selected.mat-primary {
+ background-color: mat-color($accent, 700);
+ cursor: pointer;
+ }
+
+ .load-container .loader:after {
+ background-color: mat-color($accent, 300);
+ }
+
+ .global-menu {
+ .mat-tab-label[aria-selected='true'] {
+ .mat-tab-label-content {
+ .icon-item {
+ background: mat-color($accent, 300);
+ }
+ }
+ }
+ }
+
+ .mat-form-field-appearance-legacy {
+ .mat-form-field-label {
+ color: mat-color($primary);
+ }
+
+ .mat-hint {
+ color: mat-color($primary);
+ }
+
+ .mat-form-field-underline {
+ background-color: mat-color($primary);
+ }
+ }
+ $pixels-amount: 0;
+ .mat-form-field-infix {
+ padding: $pixels-amount;
+ border-top: $pixels-amount;
+ }
+ //login input... ////////////////////
+ .idpass-type {
+ .mat-form-field-empty {
+ &.mat-form-field-label {
+ top: 11px;
+ }
+ }
+ .mat-form-field-can-float {
+ &.mat-form-field-should-float {
+ .mat-form-field-label {
+ top: 11px;
+ width: 0;
+ }
+ }
+ }
+ .mat-form-field {
+ &.mat-focused {
+ .mat-form-field-label {
+ opacity: 0;
+ }
+ }
+ }
+ }
+ // input
+ .ucap-mat-input-container {
+ .mat-form-field-empty {
+ &.mat-form-field-label {
+ top: 11px;
+ }
+ }
+ .mat-form-field-can-float {
+ &.mat-form-field-should-float {
+ .mat-form-field-label {
+ top: 11px;
+ width: 0;
+ }
+ }
+ }
+ .mat-form-field {
+ &.mat-focused {
+ .mat-form-field-label {
+ opacity: 0;
+ }
+ }
+ .mat-form-field-wrapper {
+ padding-bottom: 0;
+ .mat-form-field-infix {
+ align-self: center;
+ width: inherit;
+ }
+ }
+ }
+ }
+ //////////////////// login input...//
+ //panel /////////////////////////////
+ .panelType {
+ @include ucapMatExpansionPanel(60px, 0 0 0 0, 1, 0);
+ }
+ ///////////////////////////// panel//
+ //btn //////////////////////////////
+ .btnType {
+ .mat-fab .mat-button-wrapper {
+ padding: 16px 0;
+ display: inline-block;
+ line-height: 24px;
+ .mat-icon {
+ background-repeat: no-repeat;
+ display: inline-block;
+ fill: currentColor;
+ height: 24px;
+ width: 24px;
+ }
+ }
+ }
+ ////////////////////////////// btn//
+
+ .mat-button-toggle-appearance-standard .mat-button-toggle-label-content {
+ height: 34px;
+ line-height: 34px;
+ padding: 0 12px;
+ }
+
+ .app-dialog-full .mat-dialog-container {
+ overflow: hidden;
+ padding: 0px;
+ background-color: rgba($color: #000000, $alpha: 0.7);
+ box-shadow: none;
+ border-radius: 0px;
+ }
+
+ .current-head {
+ display: flex;
+ justify-content: center;
+ padding: 0 10px;
+ background-color: #eeeeee;
+ background: $gradient-light;
+ background: -webkit-linear-gradient(
+ to right,
+ $gradient-darkest,
+ $gradient-light
+ );
+ background: linear-gradient(to right, $gradient-darkest, $gradient-light);
+ color: #ffffff;
+ }
+
+ //[S]탭개수에 의한 width 정의
+ .mat-tab-group {
+ &.tab_num2 {
+ .mat-tab-label {
+ width: 50%;
+ min-width: 50%;
+ }
+ }
+ &.tab_num3 {
+ .mat-tab-label {
+ width: 33%;
+ min-width: 33%;
+ }
+ }
+ .mat-ink-bar {
+ opacity: 1;
+ }
+ .mat-tab-body-wrapper {
+ height: 100%;
+ border: none;
+ }
+ }
+ //[E]탭개수에 의한 width 정의
+
+ //[S]쪽지 라디오 버튼 정렬
+ .message-box {
+ .mat-radio-label {
+ .mat-radio-label-content {
+ padding-left: 4px;
+ }
+ }
+ }
+ //[E]쪽지 라디오 버튼 정렬
+
+ //[S]mat-tab-group
+ /*mat-tab-group {
+ display: flex;
+ flex-direction: row;
+ .mat-tab-header {
+ width: 100%;
+ .mat-tab-label-container {
+ width: 100%;
+ }
+ }
+ }*/
+
+ mat-tab-group[vertical] {
+ display: flex;
+ flex-direction: column;
+ .mat-tab-header {
+ border-bottom: none;
+ .mat-tab-labels {
+ display: flex;
+ flex-direction: column;
+ align-content: flex-start;
+ .mat-tab-label {
+ justify-content: flex-start;
+ padding: 0;
+ &[aria-selected='true'] {
+ opacity: 1;
+ }
+ .mat-checkbox {
+ margin-right: 8px;
+ }
+ }
+ }
+ }
+ .mat-ink-bar {
+ opacity: 0;
+ }
+ .mat-tab-body-wrapper {
+ flex: 1 1 auto;
+ border-left: 1px solid #dddddd;
+ padding: 0;
+ }
+ }
+
+ //설정 general-theme
+ .theme-list {
+ .mat-tab-header {
+ width: 100%;
+ border: none !important;
+ .mat-tab-labels {
+ position: relative;
+ width: 100%;
+ flex-direction: row !important;
+ .mat-tab-label {
+ width: 140px;
+ height: 120px;
+ &.mat-tab-label-active {
+ opacity: 1;
+ }
+ .mat-tab-label-content {
+ position: relative;
+ flex-flow: column;
+ width: 100%;
+ }
+ }
+ }
+ }
+ }
+ //[E]mat-tab-group
+
+ nav[mat-tab-nav-bar][vertical] {
+ display: flex;
+ flex-direction: row !important;
+
+ .mat-tab-links {
+ display: flex;
+ flex-direction: column !important;
+ }
+ }
+
+ .mat-form-field-appearance-legacy[inlineEdit] .mat-form-field-underline {
+ background-color: transparent;
+ }
+
+ app-page-messenger-main mat-drawer-content {
+ overflow: hidden !important;
+ }
+
+ .mat-tab-labels .cdk-focused {
+ background-color: transparent !important;
+ }
+
+ .messages .container {
+ background: mat-color($accent, 50);
+ }
+ .profile-img {
+ .responsive-chats-button:last-child {
+ background-color: mat-color($accent, B100);
+ }
+ }
+
+ //[S]대화 말풍선 global 적용
+ .message-row {
+ .message-main {
+ .bubble {
+ background-color: mat-color($accent, 100);
+ border: 1px solid mat-color($accent, 200);
+ overflow: hidden;
+ .bubble-main {
+ word-wrap: break-word;
+ white-space: pre-wrap;
+ word-break: break-word;
+ a {
+ color: #0367a6;
+ text-decoration: underline;
+ }
+ }
+ }
+ }
+ &.me {
+ .message-main {
+ .bubble {
+ background-color: #ffffff;
+ border: 1px solid mat-color($primary, 300);
+ .event-info {
+ border-radius: 0 0 0 6px;
+ }
+ }
+ }
+ }
+ }
+ //[E]대화 말풍선 global 적용
+
+ //[S]스티커
+ .sticker-selector {
+ .mat-tab-header {
+ }
+ }
+ //[E]스티커
+
+ //[S]번역
+ .translationForm {
+ background-color: mat-color($accent, 200, 0.4);
+ .select-language {
+ .mat-form-field-wrapper {
+ width: 100%;
+ line-height: 0.8em;
+ .mat-form-field-infix {
+ padding: 0.2em 0 0;
+ }
+ }
+ .mat-form-field-underline {
+ bottom: 0.8em;
+ }
+ }
+ }
+ .translation-preview {
+ background-color: mat-color($accent, 900, 0.8);
+ }
+ //[E] 번역
+
+ .mat-calendar {
+ mat-calendar-header {
+ .mat-calendar-header {
+ .mat-calendar-controls {
+ margin: 0;
+ }
+ }
+ }
+ }
+ .mat-calendar-body-selected {
+ background-color: mat-color($accent);
+ }
+ .list-item {
+ &.selected {
+ background-color: mat-color($accent, 100);
+ border-right: 2px solid mat-color($accent, 800);
+ }
+ }
+ .mat-simple-snackbar-action {
+ button {
+ background-color: mat-color($accent, 500);
+ }
+ }
+ .chatroom-search {
+ .search-form {
+ border: 2px solid mat-color($accent, B100);
+ }
+ .btns {
+ background-color: mat-color($accent, B100);
+ }
+ }
+
+ /////////2020
+ //color
+ .color-white {
+ color: $white;
+ }
+ //mat dadge
+ .mat-badge-accent .mat-badge-content,
+ .weblink .mat-badge-content {
+ background-color: mat-color($warn, 400);
+ }
+ //mat-card
+ .allim-card {
+ .mat-card-header {
+ margin-bottom: 0;
+ .mat-card-header-text {
+ margin: 0 0 0 10px;
+ .mat-card-title {
+ line-height: 21px;
+ height: 21px;
+ @include font-family(LG Smart Light, normal);
+ font-size: 16px !important;
+ font-weight: bold;
+ color: $brown;
+ margin-bottom: 3px;
+ }
+ .mat-card-subtitle {
+ line-height: 14px;
+ @include font-family(LG Smart Light, normal);
+ font-size: 12px;
+ color: $gray-re9;
+ margin-top: 0;
+ margin-bottom: 18px;
+ }
+ }
+ }
+ .mat-card-content {
+ border-top: solid 1px #d4d4d4;
+ margin-bottom: 0;
+ min-height: 114px;
+ display: flex;
+ justify-content: space-around;
+ flex-direction: column;
+ align-items: center;
+ color: #d4d4d4;
+ font-size: 12px;
+ }
+ & > .mat-card-actions:last-child {
+ margin-left: -17px;
+ margin-right: -17px;
+ margin-bottom: 0 !important;
+ padding: 0;
+ }
+ }
+
+ //mat ico font
+ .material-icons-outlined {
+ font-family: 'Material Icons Outlined';
+ font-weight: normal;
+ font-style: normal;
+ font-size: 24px;
+ line-height: 1;
+ letter-spacing: normal;
+ text-transform: none;
+ display: inline-block;
+ white-space: nowrap;
+ word-wrap: normal;
+ direction: ltr;
+ font-feature-settings: 'liga';
+ -webkit-font-smoothing: antialiased;
+ }
+
+ //mat ico btn (Mini Fab(36*36) Buttons)
+ .mat-mini36-fab {
+ line-height: 32px;
+ width: 36px;
+ height: 36px;
+ .mat-button-wrapper {
+ line-height: 20px !important;
+ .mat-icon {
+ height: 20px;
+ width: 20px;
+ &.material-icons-outlined {
+ font-size: 20px;
+ }
+ }
+ }
+ }
+
+ //mat tree
+ .tree-has-child {
+ display: flex;
+ align-items: center;
+ min-height: 36px;
+ flex: 1;
+ word-wrap: break-word;
+ li {
+ width: 100%;
+ .tree-node-body {
+ display: flex;
+ flex-direction: row;
+ align-content: center;
+ background-color: mat-color($accent, 100);
+ border: 1px solid mat-color($accent, 200);
+ }
+ }
+ }
+ .group-list-box {
+ .mat-tree-node {
+ &:first-of-type {
+ li {
+ .user-list {
+ &.line-top {
+ border-top: 0;
+ background-color: $grayf7-a;
+ border-bottom: 1px solid $gray-rec;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Btn Float
+ .btn-main-float {
+ right: 22px !important;
+ bottom: 60px !important;
+ .bg-accent-dark {
+ //background: mat-color($accent, 600);
+ color: mat-color($primary, default-contrast);
+ width: 64px;
+ height: 64px;
+ -webkit-backdrop-filter: blur(30px);
+ backdrop-filter: blur(30px);
+ box-shadow: 0 3px 6px 0 rgba(0, 0, 0, 0.16);
+ background-image: linear-gradient(143deg, #e97466 21%, #eb2d67 57%);
+ &:hover {
+ background-color: $lipstick;
+ background-image: none;
+ }
+ .mat-button-wrapper {
+ padding: 10px 0;
+ line-height: 44px;
+ .mat-icon {
+ height: 44px;
+ width: 44px;
+ font-size: 44px;
+ }
+ }
+ }
+ }
+}
diff --git a/src/assets/scss/lg.scss b/src/assets/scss/lg.scss
new file mode 100644
index 0000000..6360582
--- /dev/null
+++ b/src/assets/scss/lg.scss
@@ -0,0 +1,4 @@
+// Material theming tools
+@import '~@angular/material/theming';
+
+@import 'setting/variables';
diff --git a/src/assets/scss/mixins/_dom.scss b/src/assets/scss/mixins/_dom.scss
new file mode 100644
index 0000000..9f19570
--- /dev/null
+++ b/src/assets/scss/mixins/_dom.scss
@@ -0,0 +1,58 @@
+// scrollbar
+@mixin custom-scrollbar() {
+ &::-webkit-scrollbar {
+ width: 4px;
+ height: 4px;
+ padding: 1px;
+ background-color: transparent;
+ }
+ &::-webkit-scrollbar-thumb {
+ background-color: #999;
+ border-radius: 4px;
+ }
+ &::-webkit-scrollbar-track {
+ background-color: $white;
+ border-radius: 4px;
+ box-shadow: inset 0px 0px 2px $white;
+ }
+}
+@mixin no-scrollbar() {
+ &::-webkit-scrollbar {
+ height: 0;
+ width: 0;
+ }
+
+ &::-webkit-scrollbar-thumb {
+ height: 0;
+ width: 0;
+ }
+
+ &::-webkit-scrollbar-track {
+ height: 0;
+ width: 0;
+ }
+}
+
+@mixin ellipsis($row) {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ @if $row == 1 {
+ display: block;
+ white-space: nowrap;
+ word-wrap: normal;
+ } @else if $row >= 2 {
+ display: -webkit-box;
+ -webkit-line-clamp: $row;
+ -webkit-box-orient: vertical;
+ word-wrap: break-word;
+ }
+}
+
+@mixin ellipsis-column($column) {
+ display: block;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ display: -webkit-box;
+ -webkit-line-clamp: $column;
+ -webkit-box-orient: vertical;
+}
diff --git a/src/assets/scss/mixins/_font.scss b/src/assets/scss/mixins/_font.scss
new file mode 100644
index 0000000..d22b033
--- /dev/null
+++ b/src/assets/scss/mixins/_font.scss
@@ -0,0 +1,37 @@
+// font-family
+@mixin font-family($font, $sytle: normal) {
+ font-family: $font, Malgun Gothic, '맑은고딕', Arial, Dotum, '돋움', Gulim,
+ '굴림';
+ @if ($font == $font-regular) or ($font == $font-bold) {
+ @if $sytle == italic {
+ font-style: italic;
+ } @else if $sytle == normal {
+ font-style: normal;
+ }
+ }
+ else {
+ font-style: normal;
+ }
+ font-weight: normal;
+}
+@mixin font-family-ico(
+ $ico-font-family,
+ $fontSize: 24,
+ $txtAlign: center,
+ $txtColor: $black
+) {
+ font-family: $ico-font-family;
+ font-size: $fontSize + px;
+ text-align: $txtAlign;
+ color: $txtColor;
+}
+@mixin font-family-txt(
+ $textSize: 12,
+ $textAlign: left,
+ $textColor: $gray-re70
+) {
+ font-family: '나눔고딕, Malgun Gothic, 맑은고딕, Arial, Muli, Helvetica Neue, Arial, sans-serif';
+ font-size: $textSize + px;
+ text-align: $textAlign;
+ color: $textColor;
+}
diff --git a/src/assets/scss/mixins/_ucap-chat.scss b/src/assets/scss/mixins/_ucap-chat.scss
new file mode 100644
index 0000000..e69de29
diff --git a/src/assets/scss/mixins/_ucap-organization.scss b/src/assets/scss/mixins/_ucap-organization.scss
new file mode 100644
index 0000000..5282b13
--- /dev/null
+++ b/src/assets/scss/mixins/_ucap-organization.scss
@@ -0,0 +1,64 @@
+@mixin presence-state($circle-size: 8px) {
+ //pc상태설정
+ display: flex;
+ align-items: flex-start;
+ width: $circle-size;
+ height: $circle-size;
+ border-radius: 50%;
+ background-color: $green;
+ transform: none;
+ @extend %blind;
+ &.offline {
+ background-color: $gray-rec;
+ }
+ &.absence {
+ background-color: $red-absence;
+ }
+ &.other-business {
+ background-color: $yellow-other;
+ }
+ @content;
+}
+@mixin avatar-img($avatar-size: 36px, $avatar-left: 0) {
+ //아바타 이미지 설정
+ border-radius: 50%;
+ overflow: hidden;
+ width: $avatar-size;
+ height: $avatar-size;
+ margin-left: $avatar-left;
+ @content;
+}
+
+//profile avatar
+@mixin profile-avatar-default(
+ $mobile-on: 0 5px 5px 0,
+ $mico-size: 8,
+ $mico-color: $green,
+ $mico-bg: 18px
+) {
+ //기본설정
+ display: inline-flex;
+ position: relative;
+ &.mobile-ing {
+ padding: $mobile-on;
+ &:after {
+ @include font-family-ico(
+ $font-ico-default,
+ $mico-size,
+ center,
+ $mico-color
+ );
+ content: 'smartphone';
+ display: block;
+ width: $mico-bg;
+ height: $mico-bg;
+ line-height: $mico-bg;
+ border-radius: 50%;
+ background-color: $white;
+ position: absolute;
+ bottom: 0;
+ right: 0;
+ }
+ }
+ @content;
+}
diff --git a/src/assets/scss/partials/_material-ui.scss b/src/assets/scss/partials/_material-ui.scss
index e69de29..59557c5 100644
--- a/src/assets/scss/partials/_material-ui.scss
+++ b/src/assets/scss/partials/_material-ui.scss
@@ -0,0 +1,3 @@
+.cdk-global-scrollblock {
+ overflow: hidden !important;
+}
diff --git a/src/assets/scss/setting/_variables.scss b/src/assets/scss/setting/_variables.scss
index c55bf4a..329aec9 100644
--- a/src/assets/scss/setting/_variables.scss
+++ b/src/assets/scss/setting/_variables.scss
@@ -1,9 +1,26 @@
+:root {
+ --ucap-screen-max-xs: 575;
+ --ucap-screen-min-sm: 576;
+ --ucap-screen-max-sm: 767;
+ --ucap-screen-min-md: 768;
+ --ucap-screen-max-md: 991;
+ --ucap-screen-min-lg: 992;
+ --ucap-screen-max-lg: 1199;
+ --ucap-screen-min-xl: 1200;
+
+ --ucap-organization-search-for-tenant-border-width: 1px;
+ --ucap-organization-search-for-tenant-border-color: #e42f66;
+}
+
// Fonts Family
$font-bold: 'LG Smart Bold';
$font-light: 'LG Smart Light';
$font-regular: 'LG Smart Regular';
$font-semibold: 'LG Smart SemiBold';
+$font-ico-default: 'material Icons';
+$font-ico-outline: 'material Icons Outlined';
+
// Font Info
$font-size-base: 14px !default;
$line-height-base: 1.5 !default;
@@ -11,6 +28,7 @@ $line-height-base: 1.5 !default;
// Color
$black: #000 !default;
$white: #ffffff !default;
+$grayf7-a: #f7f8fa !default;
$grayf3-5: #f3f4f5 !default;
$gray-ref0: #f0f0f0 !default;
$gray-re20: #202020 !default;
@@ -21,6 +39,7 @@ $gray-re4a: #4a4a4a !default;
$gray-re6: #666666 !default;
$gray-re70: #707070 !default;
$gray-re9: #999999 !default;
+$gray-rec: #cccccc !default;
$brown: #7f5360 !default;
@@ -29,30 +48,23 @@ $dirty-purple: #5c575a !default;
$rose-pink: #f0828c !default;
$warm-pink: #fd578a !default;
+$green: #20af9a;
+$red-absence: #f95050;
+$yellow-other: #fecd0d;
+
// text-color
$txt-color01: $gray-re6;
// Line Color
+$line-color-gray01: #d4d4d4;
// Bg Color
$body-bg: $white !default;
$bg-gray: $grayf3-5;
+$bg-linear-gradient: linear-gradient(57deg, #ffe1ba 13%, #f5878c 87%);
// Design size
-// Screen size
-$max-xs: 575;
-$min-sm: 576;
-$max-sm: 767;
-$min-md: 768;
-$max-md: 991;
-$min-lg: 992;
-$max-lg: 1199;
-$min-xl: 1200;
-
-$login-bg-w: 100/1920;
-$login-bg-h: 100/1080;
-
// Extend
%clearfix {
*zoom: 1;
@@ -71,88 +83,27 @@ $login-bg-h: 100/1080;
list-style: none;
box-sizing: border-box;
}
+%blind {
+ font: 0/0 '돋움', dotum;
+}
%guideline {
- //border: 1px dashed red;
+ border: 1px dashed red;
}
%guideline2 {
- //border: 1px dotted blue;
+ border: 1px dotted blue;
}
%height100vh {
height: 100vh;
}
-// font-family
-@mixin font-family($font, $sytle: normal) {
- font-family: $font, Malgun Gothic, '맑은고딕', Arial, Dotum, '돋움', Gulim,
- '굴림';
- @if ($font == $font-regular) or ($font == $font-bold) {
- @if $sytle == italic {
- font-style: italic;
- } @else if $sytle == normal {
- font-style: normal;
- }
- }
- else {
- font-style: normal;
- }
- font-weight: normal;
-}
-
-// Screen
-@mixin screen($size, $type: max, $pixels: false) {
- @if $size == 'xs' {
- //Default min size
- @media (max-width: $max-xs + px) {
- @content;
- }
- } @else if $size == 'sm' {
- @media (min-width: $min-sm + px) and (max-width: $max-sm + px) {
- @content;
- }
- } @else if $size == 'md' {
- @media (min-width: $min-md + px) and (max-width: $max-md + px) {
- @content;
- }
- } @else if $size == 'lg' {
- @media (min-width: $min-lg + px) and (max-width: $max-lg + px) {
- @content;
- }
- } @else if $size == 'mid' {
- //Default mid size
- @media (max-width: $max-md + px) {
- @content;
- }
- } @else if $size == 'xl' {
- //Default max size
- @media (min-width: $min-xl + px) {
- @content;
- }
- } @else if $size == 'landscape' {
- @media (orientation: landscape) {
- @content;
- }
- } @else if $size == 'portrait' {
- @media (orientation: portrait) {
- @content;
- }
- } @else if $size == 'custom' {
- @media ($type + -width: $pixels + px) {
- @content;
- }
- } @else if $size == 'print' {
- @media print {
- @content;
- }
- }
-}
-
$lg-red: (
50: #ffffff,
100: #fff9fc,
200: #f1e1e5,
300: #ef4c73,
- 400: #ec407a,
- 500: #ed097e,
+ 400: #ffbf2a,
+ //#ec407a
+ 500: #ed097e,
600: #d81b60,
700: #c2185b,
800: #ad1457,
@@ -185,503 +136,3 @@ $lg-red: (
G900: $light-primary-text
)
);
-
-@mixin ucap-material-theme($theme) {
- @include ucap-core-theme($theme);
-}
-
-@mixin ucap-core-theme($theme) {
- $accent: map-get($theme, accent);
- $warn: map-get($theme, warn);
- $primary: map-get($theme, primary);
- $background: map-get($theme, background);
- $foreground: map-get($theme, foreground);
-
- $gradient-darkest: mat-color($accent, G900);
- $gradient-light: mat-color($accent, G100);
-
- //basic
- .bg-primary-darkest {
- background: mat-color($primary, 900);
- color: mat-color($primary, default-contrast);
- }
- .bg-primary-dark {
- background: mat-color($primary, 900);
- color: mat-color($primary, default-contrast);
- }
- .bg-primary-light {
- background: mat-color($primary, 300);
- color: mat-color($primary, default-contrast);
- }
- .bg-primary-color {
- background: mat-color($primary);
- color: mat-color($primary, default-contrast);
- }
- .bg-accent-darkest {
- background: mat-color($accent, 800);
- color: mat-color($primary, default-contrast);
- }
- .bg-accent-dark {
- background: mat-color($accent, B200);
- color: mat-color($primary, default-contrast);
- }
- .bg-accent-light {
- background: mat-color($accent, 300);
- color: mat-color($primary, default-contrast);
- }
- .bg-accent-bright {
- background-color: mat-color($accent, 100);
- }
- .bg-accent-brightest {
- background: mat-color($accent, 50);
- color: mat-color($primary, $dark-primary-text);
- }
- .bg-accent-color {
- background: mat-color($accent);
- color: mat-color($accent, default-contrast);
- }
- .bg-warn-color {
- background-color: mat-color($warn, 300);
- }
- .bg-warn-darkest {
- background-color: mat-color($warn, 600);
- }
- .text-primary-light {
- color: mat-color($primary, 500);
- }
- .text-primary-color {
- color: mat-color($primary);
- }
- .text-accent-dark {
- color: mat-color($accent, 600);
- }
- .text-accent-darkest {
- color: mat-color($accent, 800);
- }
- .text-accent-color {
- color: mat-color($accent);
- }
- .text-warn-color {
- color: mat-color($warn, 800);
- }
- .border-primary-color {
- border-color: 1px solid mat-color($primary);
- }
- .border-accent-bright {
- border-color: mat-color($accent, 300);
- }
- .border-accent-color {
- border-color: 1px solid mat-color($accent);
- }
- .stroke-accent-darkest {
- stroke: mat-color($accent, 800);
- }
- .border-warn-color {
- border: mat-color($warn);
- }
- .stroke-warn-color {
- stroke: mat-color($warn, 900);
- }
- // sass 정의
- .mat-toolbar {
- background-color: mat-color($accent, B100);
- }
- ///// gnb
- .gnb .mat-toolbar {
- background-color: $gray-ref0;
- }
-
- .main-container {
- border-color: mat-color($accent, B100);
- }
- .global-menu {
- background-color: mat-color($accent, B100);
- }
-
- .ucap-clickable {
- cursor: pointer;
- }
-
- .policy {
- color: mat-color($accent, B100);
- }
-
- .mat-badge-accent .mat-badge-content,
- .weblink .mat-badge-content {
- background-color: mat-color($warn, 400);
- }
-
- .mat-tab-group.mat-primary .mat-ink-bar,
- body.theme-default .mat-tab-nav-bar.mat-primary .mat-ink-bar {
- background-color: mat-color($accent, 500);
- }
- .mat-chip.mat-standard-chip.mat-chip-selected.mat-primary {
- background-color: mat-color($accent, 700);
- cursor: pointer;
- }
-
- .load-container .loader:after {
- background-color: mat-color($accent, 300);
- }
-
- .global-menu {
- .mat-tab-label[aria-selected='true'] {
- .mat-tab-label-content {
- .icon-item {
- background: mat-color($accent, 300);
- }
- }
- }
- }
-
- .mat-form-field-appearance-legacy {
- .mat-form-field-label {
- color: mat-color($primary);
- }
-
- .mat-hint {
- color: mat-color($primary);
- }
-
- .mat-form-field-underline {
- background-color: mat-color($primary);
- }
- }
- $pixels-amount: 0;
- .mat-form-field-infix {
- padding: $pixels-amount;
- border-top: $pixels-amount;
- }
- ////login input
- .idpass-type {
- .mat-form-field-empty {
- &.mat-form-field-label {
- top: 11px;
- }
- }
- .mat-form-field-can-float {
- &.mat-form-field-should-float {
- .mat-form-field-label {
- top: 11px;
- width: 0;
- }
- }
- }
- .mat-form-field {
- &.mat-focused {
- .mat-form-field-label {
- opacity: 0;
- }
- }
- }
- }
- //////////
-
- .mat-button-toggle-appearance-standard .mat-button-toggle-label-content {
- height: 34px;
- line-height: 34px;
- padding: 0 12px;
- }
-
- .app-dialog-full .mat-dialog-container {
- overflow: hidden;
- padding: 0px;
- background-color: rgba($color: #000000, $alpha: 0.7);
- box-shadow: none;
- border-radius: 0px;
- }
-
- .btn-main-float .bg-accent-dark {
- background: mat-color($accent, 600);
- color: mat-color($primary, default-contrast);
- }
-
- .current-head {
- display: flex;
- justify-content: center;
- padding: 0 10px;
- background-color: #eeeeee;
- background: $gradient-light;
- background: -webkit-linear-gradient(
- to right,
- $gradient-darkest,
- $gradient-light
- );
- background: linear-gradient(to right, $gradient-darkest, $gradient-light);
- color: #ffffff;
- }
-
- //[S]탭개수에 의한 width 정의
- .mat-tab-group {
- &.tab_num2 {
- .mat-tab-label {
- width: 50%;
- min-width: 50%;
- }
- }
- &.tab_num3 {
- .mat-tab-label {
- width: 33%;
- min-width: 33%;
- }
- }
- .mat-ink-bar {
- opacity: 1;
- }
- .mat-tab-body-wrapper {
- height: 100%;
- border: none;
- }
- }
- //[E]탭개수에 의한 width 정의
-
- //[S]쪽지 라디오 버튼 정렬
- .message-box {
- .mat-radio-label {
- .mat-radio-label-content {
- padding-left: 4px;
- }
- }
- }
- //[E]쪽지 라디오 버튼 정렬
-
- //[S]mat-tab-group
- /*mat-tab-group {
- display: flex;
- flex-direction: row;
- .mat-tab-header {
- width: 100%;
- .mat-tab-label-container {
- width: 100%;
- }
- }
- }*/
-
- mat-tab-group[vertical] {
- display: flex;
- flex-direction: column;
- .mat-tab-header {
- border-bottom: none;
- .mat-tab-labels {
- display: flex;
- flex-direction: column;
- align-content: flex-start;
- .mat-tab-label {
- justify-content: flex-start;
- padding: 0;
- &[aria-selected='true'] {
- opacity: 1;
- }
- .mat-checkbox {
- margin-right: 8px;
- }
- }
- }
- }
- .mat-ink-bar {
- opacity: 0;
- }
- .mat-tab-body-wrapper {
- flex: 1 1 auto;
- border-left: 1px solid #dddddd;
- padding: 0;
- }
- }
-
- //설정 general-theme
- .theme-list {
- .mat-tab-header {
- width: 100%;
- border: none !important;
- .mat-tab-labels {
- position: relative;
- width: 100%;
- flex-direction: row !important;
- .mat-tab-label {
- width: 140px;
- height: 120px;
- &.mat-tab-label-active {
- opacity: 1;
- }
- .mat-tab-label-content {
- position: relative;
- flex-flow: column;
- width: 100%;
- }
- }
- }
- }
- }
- //[E]mat-tab-group
-
- nav[mat-tab-nav-bar][vertical] {
- display: flex;
- flex-direction: row !important;
-
- .mat-tab-links {
- display: flex;
- flex-direction: column !important;
- }
- }
-
- .mat-form-field-appearance-legacy[inlineEdit] .mat-form-field-underline {
- background-color: transparent;
- }
-
- app-page-messenger-main mat-drawer-content {
- overflow: hidden !important;
- }
-
- .mat-tab-labels .cdk-focused {
- background-color: transparent !important;
- }
-
- .messages .container {
- background: mat-color($accent, 50);
- }
- .profile-img {
- .responsive-chats-button:last-child {
- background-color: mat-color($accent, B100);
- }
- }
-
- //[S]대화 말풍선 global 적용
- .message-row {
- .message-main {
- .bubble {
- background-color: mat-color($accent, 100);
- border: 1px solid mat-color($accent, 200);
- overflow: hidden;
- .bubble-main {
- word-wrap: break-word;
- white-space: pre-wrap;
- word-break: break-word;
- a {
- color: #0367a6;
- text-decoration: underline;
- }
- }
- }
- }
- &.me {
- .message-main {
- .bubble {
- background-color: #ffffff;
- border: 1px solid mat-color($primary, 300);
- .event-info {
- border-radius: 0 0 0 6px;
- }
- }
- }
- }
- }
- //[E]대화 말풍선 global 적용
-
- //[S]스티커
- .sticker-selector {
- .mat-tab-header {
- }
- }
- //[E]스티커
-
- //[S]번역
- .translationForm {
- background-color: mat-color($accent, 200, 0.4);
- .select-language {
- .mat-form-field-wrapper {
- width: 100%;
- line-height: 0.8em;
- .mat-form-field-infix {
- padding: 0.2em 0 0;
- }
- }
- .mat-form-field-underline {
- bottom: 0.8em;
- }
- }
- }
- .translation-preview {
- background-color: mat-color($accent, 900, 0.8);
- }
- //[E] 번역
-
- .tree-has-child {
- li {
- .tree-node-body {
- background-color: mat-color($accent, 100);
- border: 1px solid mat-color($accent, 200);
- }
- }
- }
-
- .mat-calendar {
- mat-calendar-header {
- .mat-calendar-header {
- .mat-calendar-controls {
- margin: 0;
- }
- }
- }
- }
- .mat-calendar-body-selected {
- background-color: mat-color($accent);
- }
- .list-item {
- &.selected {
- background-color: mat-color($accent, 100);
- border-right: 2px solid mat-color($accent, 800);
- }
- }
- .mat-simple-snackbar-action {
- button {
- background-color: mat-color($accent, 500);
- }
- }
- .chatroom-search {
- .search-form {
- border: 2px solid mat-color($accent, B100);
- }
- .btns {
- background-color: mat-color($accent, B100);
- }
- }
-}
-///////////////////////////////////////////////////
-/////////////
-// Select
-@mixin selecCtrl($selec-height, $selec-width) {
- .mat-select-value {
- height: $selec-height + px;
- line-height: $selec-height + px;
- }
- .mat-form-field {
- height: $selec-height + px;
- }
- .mat-form-field-appearance-legacy .mat-form-field-wrapper {
- height: $selec-height + px;
- }
- .mat-form-field-appearance-legacy .mat-form-field-wrapper {
- padding-bottom: 0;
- }
- .mat-form-field-flex {
- height: $selec-height + px;
- }
-}
-
-//input
-@mixin matinputCtrl($selec-height, $selec-width) {
- .mat-form-field-appearance-legacy {
- .mat-form-field-infix {
- padding: 0;
- border-top: 0;
- width: $selec-width + px;
- }
- }
- .mat-form-field-appearance-legacy {
- .mat-form-field-underline {
- display: none;
- }
- }
-}
-/////////////
-////////////////////////////////////////////////////////
diff --git a/src/environments/environment.hmr.ts b/src/environments/environment.hmr.ts
index 7cc0e71..0057030 100644
--- a/src/environments/environment.hmr.ts
+++ b/src/environments/environment.hmr.ts
@@ -71,6 +71,9 @@ export const environment: Environment = {
profile: {
editableProfileImage: false
},
+ organization: {
+ displayRoot: false
+ },
group: {
useMyDeptGroup: false,
myDeptGroupSeq: -5,
@@ -94,7 +97,7 @@ export const environment: Environment = {
companyConfig: {
companyGroupCode: 'LG',
- fixedCompanyCode: undefined
+ fixedCompanyCode: null
},
commonApiModuleConfig: {
diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts
index 2956d7b..367509a 100644
--- a/src/environments/environment.prod.ts
+++ b/src/environments/environment.prod.ts
@@ -71,6 +71,9 @@ export const environment: Environment = {
profile: {
editableProfileImage: false
},
+ organization: {
+ displayRoot: false
+ },
group: {
useMyDeptGroup: false,
myDeptGroupSeq: -5,
@@ -94,7 +97,7 @@ export const environment: Environment = {
companyConfig: {
companyGroupCode: 'LG',
- fixedCompanyCode: undefined
+ fixedCompanyCode: null
},
commonApiModuleConfig: {
diff --git a/src/environments/environment.ts b/src/environments/environment.ts
index b6b2042..ce5ad5f 100644
--- a/src/environments/environment.ts
+++ b/src/environments/environment.ts
@@ -74,6 +74,9 @@ export const environment: Environment = {
profile: {
editableProfileImage: false
},
+ organization: {
+ displayRoot: false
+ },
group: {
useMyDeptGroup: false,
myDeptGroupSeq: -5,
@@ -97,7 +100,7 @@ export const environment: Environment = {
companyConfig: {
companyGroupCode: 'LG',
- fixedCompanyCode: undefined
+ fixedCompanyCode: null
},
commonApiModuleConfig: {
diff --git a/src/environments/environment.type.ts b/src/environments/environment.type.ts
index 99393f7..6d1e2e1 100644
--- a/src/environments/environment.type.ts
+++ b/src/environments/environment.type.ts
@@ -72,6 +72,10 @@ export interface Environment {
/** 내 프로필 이미지 수정 가능 여부 */
editableProfileImage: boolean;
};
+ organization: {
+ /** 최상위 노드 출력 여부 */
+ displayRoot: boolean;
+ };
group: {
/** 소속부서(내부서) 그룹핑 사용여부 */
useMyDeptGroup: boolean;
diff --git a/src/styles.scss b/src/styles.scss
index 6a13150..4775b20 100644
--- a/src/styles.scss
+++ b/src/styles.scss
@@ -5,9 +5,12 @@
@import 'assets/fonts/materialdesignicons/scss/materialdesignicons';
+@import 'assets/scss/ucap-theme';
+
@import '~@ucap/ng-ui-skin-default/skin';
-@import 'assets/scss/components';
+@import 'assets/scss/mixins';
+@import 'assets/scss/lg';
// Import app.theme.scss
@import 'app/app.theme';