diff --git a/project/package-lock.json b/project/package-lock.json
index 9fe2c47..741e6a4 100644
--- a/project/package-lock.json
+++ b/project/package-lock.json
@@ -18,7 +18,10 @@
"@angular/platform-server": "^18.1.0",
"@angular/router": "^18.1.0",
"@angular/ssr": "^18.1.2",
+ "@types/d3": "^7.4.3",
+ "d3": "^7.9.0",
"express": "^4.18.2",
+ "jalaali-js": "^1.2.7",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "~0.14.3"
@@ -5134,6 +5137,228 @@
"@types/node": "*"
}
},
+ "node_modules/@types/d3": {
+ "version": "7.4.3",
+ "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz",
+ "integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==",
+ "dependencies": {
+ "@types/d3-array": "*",
+ "@types/d3-axis": "*",
+ "@types/d3-brush": "*",
+ "@types/d3-chord": "*",
+ "@types/d3-color": "*",
+ "@types/d3-contour": "*",
+ "@types/d3-delaunay": "*",
+ "@types/d3-dispatch": "*",
+ "@types/d3-drag": "*",
+ "@types/d3-dsv": "*",
+ "@types/d3-ease": "*",
+ "@types/d3-fetch": "*",
+ "@types/d3-force": "*",
+ "@types/d3-format": "*",
+ "@types/d3-geo": "*",
+ "@types/d3-hierarchy": "*",
+ "@types/d3-interpolate": "*",
+ "@types/d3-path": "*",
+ "@types/d3-polygon": "*",
+ "@types/d3-quadtree": "*",
+ "@types/d3-random": "*",
+ "@types/d3-scale": "*",
+ "@types/d3-scale-chromatic": "*",
+ "@types/d3-selection": "*",
+ "@types/d3-shape": "*",
+ "@types/d3-time": "*",
+ "@types/d3-time-format": "*",
+ "@types/d3-timer": "*",
+ "@types/d3-transition": "*",
+ "@types/d3-zoom": "*"
+ }
+ },
+ "node_modules/@types/d3-array": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz",
+ "integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg=="
+ },
+ "node_modules/@types/d3-axis": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.6.tgz",
+ "integrity": "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==",
+ "dependencies": {
+ "@types/d3-selection": "*"
+ }
+ },
+ "node_modules/@types/d3-brush": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.6.tgz",
+ "integrity": "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==",
+ "dependencies": {
+ "@types/d3-selection": "*"
+ }
+ },
+ "node_modules/@types/d3-chord": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.6.tgz",
+ "integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg=="
+ },
+ "node_modules/@types/d3-color": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz",
+ "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A=="
+ },
+ "node_modules/@types/d3-contour": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.6.tgz",
+ "integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==",
+ "dependencies": {
+ "@types/d3-array": "*",
+ "@types/geojson": "*"
+ }
+ },
+ "node_modules/@types/d3-delaunay": {
+ "version": "6.0.4",
+ "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz",
+ "integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw=="
+ },
+ "node_modules/@types/d3-dispatch": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.6.tgz",
+ "integrity": "sha512-4fvZhzMeeuBJYZXRXrRIQnvUYfyXwYmLsdiN7XXmVNQKKw1cM8a5WdID0g1hVFZDqT9ZqZEY5pD44p24VS7iZQ=="
+ },
+ "node_modules/@types/d3-drag": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz",
+ "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==",
+ "dependencies": {
+ "@types/d3-selection": "*"
+ }
+ },
+ "node_modules/@types/d3-dsv": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz",
+ "integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g=="
+ },
+ "node_modules/@types/d3-ease": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz",
+ "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA=="
+ },
+ "node_modules/@types/d3-fetch": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz",
+ "integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==",
+ "dependencies": {
+ "@types/d3-dsv": "*"
+ }
+ },
+ "node_modules/@types/d3-force": {
+ "version": "3.0.10",
+ "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.10.tgz",
+ "integrity": "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw=="
+ },
+ "node_modules/@types/d3-format": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz",
+ "integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g=="
+ },
+ "node_modules/@types/d3-geo": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.0.tgz",
+ "integrity": "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==",
+ "dependencies": {
+ "@types/geojson": "*"
+ }
+ },
+ "node_modules/@types/d3-hierarchy": {
+ "version": "3.1.7",
+ "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz",
+ "integrity": "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg=="
+ },
+ "node_modules/@types/d3-interpolate": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz",
+ "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==",
+ "dependencies": {
+ "@types/d3-color": "*"
+ }
+ },
+ "node_modules/@types/d3-path": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.0.tgz",
+ "integrity": "sha512-P2dlU/q51fkOc/Gfl3Ul9kicV7l+ra934qBFXCFhrZMOL6du1TM0pm1ThYvENukyOn5h9v+yMJ9Fn5JK4QozrQ=="
+ },
+ "node_modules/@types/d3-polygon": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.2.tgz",
+ "integrity": "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA=="
+ },
+ "node_modules/@types/d3-quadtree": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz",
+ "integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg=="
+ },
+ "node_modules/@types/d3-random": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.3.tgz",
+ "integrity": "sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ=="
+ },
+ "node_modules/@types/d3-scale": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz",
+ "integrity": "sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==",
+ "dependencies": {
+ "@types/d3-time": "*"
+ }
+ },
+ "node_modules/@types/d3-scale-chromatic": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.0.3.tgz",
+ "integrity": "sha512-laXM4+1o5ImZv3RpFAsTRn3TEkzqkytiOY0Dz0sq5cnd1dtNlk6sHLon4OvqaiJb28T0S/TdsBI3Sjsy+keJrw=="
+ },
+ "node_modules/@types/d3-selection": {
+ "version": "3.0.10",
+ "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.10.tgz",
+ "integrity": "sha512-cuHoUgS/V3hLdjJOLTT691+G2QoqAjCVLmr4kJXR4ha56w1Zdu8UUQ5TxLRqudgNjwXeQxKMq4j+lyf9sWuslg=="
+ },
+ "node_modules/@types/d3-shape": {
+ "version": "3.1.6",
+ "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.6.tgz",
+ "integrity": "sha512-5KKk5aKGu2I+O6SONMYSNflgiP0WfZIQvVUMan50wHsLG1G94JlxEVnCpQARfTtzytuY0p/9PXXZb3I7giofIA==",
+ "dependencies": {
+ "@types/d3-path": "*"
+ }
+ },
+ "node_modules/@types/d3-time": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.3.tgz",
+ "integrity": "sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw=="
+ },
+ "node_modules/@types/d3-time-format": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz",
+ "integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg=="
+ },
+ "node_modules/@types/d3-timer": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz",
+ "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw=="
+ },
+ "node_modules/@types/d3-transition": {
+ "version": "3.0.8",
+ "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.8.tgz",
+ "integrity": "sha512-ew63aJfQ/ms7QQ4X7pk5NxQ9fZH/z+i24ZfJ6tJSfqxJMrYLiK01EAs2/Rtw/JreGUsS3pLPNV644qXFGnoZNQ==",
+ "dependencies": {
+ "@types/d3-selection": "*"
+ }
+ },
+ "node_modules/@types/d3-zoom": {
+ "version": "3.0.8",
+ "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz",
+ "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==",
+ "dependencies": {
+ "@types/d3-interpolate": "*",
+ "@types/d3-selection": "*"
+ }
+ },
"node_modules/@types/eslint": {
"version": "9.6.0",
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.0.tgz",
@@ -5184,6 +5409,11 @@
"@types/send": "*"
}
},
+ "node_modules/@types/geojson": {
+ "version": "7946.0.14",
+ "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.14.tgz",
+ "integrity": "sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg=="
+ },
"node_modules/@types/http-errors": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz",
@@ -6565,10 +6795,11 @@
}
},
"node_modules/axios": {
- "version": "1.7.3",
- "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.3.tgz",
- "integrity": "sha512-Ar7ND9pU99eJ9GpoGQKhKf58GpUOgnzuaB7ueNQ5BMi0p+LZ5oaEnfF999fAArcTIBwXTCHAmGcHOZJaWPq9Nw==",
+ "version": "1.7.5",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.5.tgz",
+ "integrity": "sha512-fZu86yCo+svH3uqJ/yTdQ0QHpQu5oL+/QE+QPSv6BZSkDAoky9vytxp7u5qk83OJFS3kEBcesWni9WTZAv3tSw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"follow-redirects": "^1.15.6",
"form-data": "^4.0.0",
@@ -7795,6 +8026,395 @@
"integrity": "sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==",
"dev": true
},
+ "node_modules/d3": {
+ "version": "7.9.0",
+ "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz",
+ "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==",
+ "dependencies": {
+ "d3-array": "3",
+ "d3-axis": "3",
+ "d3-brush": "3",
+ "d3-chord": "3",
+ "d3-color": "3",
+ "d3-contour": "4",
+ "d3-delaunay": "6",
+ "d3-dispatch": "3",
+ "d3-drag": "3",
+ "d3-dsv": "3",
+ "d3-ease": "3",
+ "d3-fetch": "3",
+ "d3-force": "3",
+ "d3-format": "3",
+ "d3-geo": "3",
+ "d3-hierarchy": "3",
+ "d3-interpolate": "3",
+ "d3-path": "3",
+ "d3-polygon": "3",
+ "d3-quadtree": "3",
+ "d3-random": "3",
+ "d3-scale": "4",
+ "d3-scale-chromatic": "3",
+ "d3-selection": "3",
+ "d3-shape": "3",
+ "d3-time": "3",
+ "d3-time-format": "4",
+ "d3-timer": "3",
+ "d3-transition": "3",
+ "d3-zoom": "3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-array": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz",
+ "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
+ "dependencies": {
+ "internmap": "1 - 2"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-axis": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz",
+ "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-brush": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz",
+ "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==",
+ "dependencies": {
+ "d3-dispatch": "1 - 3",
+ "d3-drag": "2 - 3",
+ "d3-interpolate": "1 - 3",
+ "d3-selection": "3",
+ "d3-transition": "3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-chord": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz",
+ "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==",
+ "dependencies": {
+ "d3-path": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-color": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
+ "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-contour": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz",
+ "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==",
+ "dependencies": {
+ "d3-array": "^3.2.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-delaunay": {
+ "version": "6.0.4",
+ "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz",
+ "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==",
+ "dependencies": {
+ "delaunator": "5"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-dispatch": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz",
+ "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-drag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz",
+ "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==",
+ "dependencies": {
+ "d3-dispatch": "1 - 3",
+ "d3-selection": "3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-dsv": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz",
+ "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==",
+ "dependencies": {
+ "commander": "7",
+ "iconv-lite": "0.6",
+ "rw": "1"
+ },
+ "bin": {
+ "csv2json": "bin/dsv2json.js",
+ "csv2tsv": "bin/dsv2dsv.js",
+ "dsv2dsv": "bin/dsv2dsv.js",
+ "dsv2json": "bin/dsv2json.js",
+ "json2csv": "bin/json2dsv.js",
+ "json2dsv": "bin/json2dsv.js",
+ "json2tsv": "bin/json2dsv.js",
+ "tsv2csv": "bin/dsv2dsv.js",
+ "tsv2json": "bin/dsv2json.js"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-dsv/node_modules/commander": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
+ "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/d3-dsv/node_modules/iconv-lite": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+ "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/d3-ease": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
+ "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-fetch": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz",
+ "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==",
+ "dependencies": {
+ "d3-dsv": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-force": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz",
+ "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==",
+ "dependencies": {
+ "d3-dispatch": "1 - 3",
+ "d3-quadtree": "1 - 3",
+ "d3-timer": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-format": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz",
+ "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-geo": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz",
+ "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==",
+ "dependencies": {
+ "d3-array": "2.5.0 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-hierarchy": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz",
+ "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-interpolate": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
+ "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
+ "dependencies": {
+ "d3-color": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-path": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz",
+ "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-polygon": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz",
+ "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-quadtree": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz",
+ "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-random": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz",
+ "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-scale": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
+ "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
+ "dependencies": {
+ "d3-array": "2.10.0 - 3",
+ "d3-format": "1 - 3",
+ "d3-interpolate": "1.2.0 - 3",
+ "d3-time": "2.1.1 - 3",
+ "d3-time-format": "2 - 4"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-scale-chromatic": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz",
+ "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==",
+ "dependencies": {
+ "d3-color": "1 - 3",
+ "d3-interpolate": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-selection": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
+ "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-shape": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz",
+ "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
+ "dependencies": {
+ "d3-path": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-time": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz",
+ "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",
+ "dependencies": {
+ "d3-array": "2 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-time-format": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
+ "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
+ "dependencies": {
+ "d3-time": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-timer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
+ "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-transition": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz",
+ "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==",
+ "dependencies": {
+ "d3-color": "1 - 3",
+ "d3-dispatch": "1 - 3",
+ "d3-ease": "1 - 3",
+ "d3-interpolate": "1 - 3",
+ "d3-timer": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "peerDependencies": {
+ "d3-selection": "2 - 3"
+ }
+ },
+ "node_modules/d3-zoom": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz",
+ "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==",
+ "dependencies": {
+ "d3-dispatch": "1 - 3",
+ "d3-drag": "2 - 3",
+ "d3-interpolate": "1 - 3",
+ "d3-selection": "2 - 3",
+ "d3-transition": "2 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/date-format": {
"version": "4.0.14",
"resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz",
@@ -7907,6 +8527,14 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/delaunator": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz",
+ "integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==",
+ "dependencies": {
+ "robust-predicates": "^3.0.2"
+ }
+ },
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
@@ -10083,6 +10711,14 @@
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
}
},
+ "node_modules/internmap": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
+ "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/ip-address": {
"version": "9.0.5",
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz",
@@ -10549,6 +11185,11 @@
"node": ">=8"
}
},
+ "node_modules/jalaali-js": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/jalaali-js/-/jalaali-js-1.2.7.tgz",
+ "integrity": "sha512-gE+YHWSbygYAoJa+Xg8LWxGILqFOxZSBQQw39ghel01fVFUxV7bjL0x1JFsHcLQ3uPjvn81HQMa+kxwyPWnxGQ=="
+ },
"node_modules/jasmine-core": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-5.1.2.tgz",
@@ -11762,10 +12403,11 @@
}
},
"node_modules/micromatch": {
- "version": "4.0.7",
- "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz",
- "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==",
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
+ "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"braces": "^3.0.3",
"picomatch": "^2.3.1"
@@ -14318,6 +14960,11 @@
"url": "https://github.com/sponsors/isaacs"
}
},
+ "node_modules/robust-predicates": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz",
+ "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg=="
+ },
"node_modules/rollup": {
"version": "4.18.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.0.tgz",
@@ -14388,6 +15035,11 @@
"queue-microtask": "^1.2.2"
}
},
+ "node_modules/rw": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",
+ "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ=="
+ },
"node_modules/rxjs": {
"version": "7.8.1",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
diff --git a/project/package.json b/project/package.json
index c77c2fc..ddd4a0b 100644
--- a/project/package.json
+++ b/project/package.json
@@ -23,7 +23,10 @@
"@angular/platform-server": "^18.1.0",
"@angular/router": "^18.1.0",
"@angular/ssr": "^18.1.2",
+ "@types/d3": "^7.4.3",
+ "d3": "^7.9.0",
"express": "^4.18.2",
+ "jalaali-js": "^1.2.7",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "~0.14.3"
diff --git a/project/src/app/app.routes.ts b/project/src/app/app.routes.ts
index 44270fa..c714e39 100644
--- a/project/src/app/app.routes.ts
+++ b/project/src/app/app.routes.ts
@@ -7,13 +7,17 @@ import {EditProfileComponent} from "./components/dashboard/edit-profile/edit-pro
import {ChangePasswordComponent} from "./components/dashboard/change-password/change-password.component";
import {ManageUsersComponent} from "./components/dashboard/manage-users/manage-users.component";
import {ShowDataComponent} from "./components/dashboard/show-data/show-data.component";
+import {isAdminGuard} from "./guards/admin/is-admin.guard";
+import { DashboardhomeComponent } from './components/dashboard/dashboardhome/dashboardhome.component';
export const routes: Routes = [
{path: 'dashboard', component: DashboardComponent, children: [
- {path: 'profile', component: ProfileComponent},
- {path: 'edit-profile', component: EditProfileComponent},
- {path: 'change-password', component: ChangePasswordComponent},
- {path: 'manage-users', component: ManageUsersComponent},
+ {path: 'home', component: DashboardhomeComponent},
+ {path: 'profile', component: ProfileComponent , children: [
+ {path: 'edit-profile', component: EditProfileComponent},
+ {path: 'change-password', component: ChangePasswordComponent},
+ ]},
+ {path: 'manage-users', component: ManageUsersComponent, canActivate: [isAdminGuard]},
{path: 'show-data', component: ShowDataComponent},
], canActivate: [loggedInGuard], data: { requiresAuth: true } },
{path: 'login', component: LoginComponent, canActivate: [loggedInGuard], data: { requiresAuth: false }},
diff --git a/project/src/app/components/bg-gif/bg-gif.component.html b/project/src/app/components/bg-gif/bg-gif.component.html
new file mode 100644
index 0000000..70fc429
--- /dev/null
+++ b/project/src/app/components/bg-gif/bg-gif.component.html
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/project/src/app/components/bg-gif/bg-gif.component.scss b/project/src/app/components/bg-gif/bg-gif.component.scss
new file mode 100644
index 0000000..a5eb9be
--- /dev/null
+++ b/project/src/app/components/bg-gif/bg-gif.component.scss
@@ -0,0 +1,205 @@
+
+
+@keyframes move {
+ 100% {
+ transform: translate3d(0, 0, 1px) rotate(360deg);
+ }
+}
+
+.background {
+ position: fixed;
+ width: 100vw;
+ height: 100vh;
+ top: 0;
+ left: 0;
+ background: #fdfdfd;
+ overflow: hidden;
+ z-index: -1;
+}
+
+.background span {
+ width: 1vmin;
+ height: 1vmin;
+ border-radius: 1vmin;
+ backface-visibility: hidden;
+ position: absolute;
+ animation: move;
+ animation-duration: 38;
+ animation-timing-function: linear;
+ animation-iteration-count: infinite;
+}
+
+
+.background span:nth-child(0) {
+ color: #e9b91b;
+ top: 44%;
+ left: 37%;
+ animation-duration: 62s;
+ animation-delay: -45s;
+ transform-origin: -4vw -4vh;
+ box-shadow: 2vmin 0 1.179326042713791vmin currentColor;
+}
+.background span:nth-child(1) {
+ color: #4f86c9;
+ top: 23%;
+ left: 97%;
+ animation-duration: 82s;
+ animation-delay: -25s;
+ transform-origin: 5vw 25vh;
+ box-shadow: -2vmin 0 0.7106015815700037vmin currentColor;
+}
+.background span:nth-child(2) {
+ color: #e9b91b;
+ top: 3%;
+ left: 64%;
+ animation-duration: 121s;
+ animation-delay: -58s;
+ transform-origin: 18vw -7vh;
+ box-shadow: -2vmin 0 0.2600782522780347vmin currentColor;
+}
+.background span:nth-child(3) {
+ color: #e9b91b;
+ top: 1%;
+ left: 65%;
+ animation-duration: 14s;
+ animation-delay: -34s;
+ transform-origin: -23vw 10vh;
+ box-shadow: -2vmin 0 0.8925616875372739vmin currentColor;
+}
+.background span:nth-child(4) {
+ color: #e9b91b;
+ top: 64%;
+ left: 96%;
+ animation-duration: 111s;
+ animation-delay: -36s;
+ transform-origin: -5vw 15vh;
+ box-shadow: -2vmin 0 0.3975511244379719vmin currentColor;
+}
+.background span:nth-child(5) {
+ color: #4f86c9;
+ top: 35%;
+ left: 88%;
+ animation-duration: 83s;
+ animation-delay: -19s;
+ transform-origin: 4vw 6vh;
+ box-shadow: -2vmin 0 0.4574674663634355vmin currentColor;
+}
+.background span:nth-child(6) {
+ color: #002b5b;
+ top: 17%;
+ left: 70%;
+ animation-duration: 116s;
+ animation-delay: -13s;
+ transform-origin: 10vw -22vh;
+ box-shadow: 2vmin 0 0.5994571422697834vmin currentColor;
+}
+.background span:nth-child(7) {
+ color: #e9b91b;
+ top: 8%;
+ left: 30%;
+ animation-duration: 12s;
+ animation-delay: -63s;
+ transform-origin: 20vw -20vh;
+ box-shadow: -2vmin 0 0.944790036995725vmin currentColor;
+}
+.background span:nth-child(8) {
+ color: #002b5b;
+ top: 58%;
+ left: 45%;
+ animation-duration: 23s;
+ animation-delay: -101s;
+ transform-origin: -20vw -2vh;
+ box-shadow: -2vmin 0 1.113637056359262vmin currentColor;
+}
+.background span:nth-child(9) {
+ color: #4f86c9;
+ top: 98%;
+ left: 80%;
+ animation-duration: 103s;
+ animation-delay: -9s;
+ transform-origin: -4vw 24vh;
+ box-shadow: -2vmin 0 0.8466328436131629vmin currentColor;
+}
+.background span:nth-child(10) {
+ color: #e9b91b;
+ top: 91%;
+ left: 35%;
+ animation-duration: 13s;
+ animation-delay: -85s;
+ transform-origin: -1vw -18vh;
+ box-shadow: -2vmin 0 0.6988589961678999vmin currentColor;
+}
+.background span:nth-child(11) {
+ color: #4f86c9;
+ top: 12%;
+ left: 38%;
+ animation-duration: 124s;
+ animation-delay: -80s;
+ transform-origin: -10vw -12vh;
+ box-shadow: -2vmin 0 1.0281140568796288vmin currentColor;
+}
+.background span:nth-child(12) {
+ color: #4f86c9;
+ top: 65%;
+ left: 47%;
+ animation-duration: 14s;
+ animation-delay: -20s;
+ transform-origin: 13vw 19vh;
+ box-shadow: 2vmin 0 0.4922053968944644vmin currentColor;
+}
+.background span:nth-child(13) {
+ color: #002b5b;
+ top: 1%;
+ left: 85%;
+ animation-duration: 119s;
+ animation-delay: -120s;
+ transform-origin: -5vw 1vh;
+ box-shadow: 2vmin 0 0.89657619610927vmin currentColor;
+}
+.background span:nth-child(14) {
+ color: #4f86c9;
+ top: 98%;
+ left: 67%;
+ animation-duration: 63s;
+ animation-delay: -98s;
+ transform-origin: 17vw -24vh;
+ box-shadow: -2vmin 0 0.2674072793462621vmin currentColor;
+}
+.background span:nth-child(15) {
+ color: #4f86c9;
+ top: 47%;
+ left: 22%;
+ animation-duration: 114s;
+ animation-delay: -21s;
+ transform-origin: 20vw 6vh;
+ box-shadow: 2vmin 0 1.1494681355574716vmin currentColor;
+}
+.background span:nth-child(16) {
+ color: #4f86c9;
+ top: 17%;
+ left: 3%;
+ animation-duration: 29s;
+ animation-delay: -82s;
+ transform-origin: 15vw 22vh;
+ box-shadow: -2vmin 0 0.7033084944562273vmin currentColor;
+}
+.background span:nth-child(17) {
+ color: #e9b91b;
+ top: 18%;
+ left: 31%;
+ animation-duration: 26s;
+ animation-delay: -97s;
+ transform-origin: 20vw -15vh;
+ box-shadow: 2vmin 0 0.3154577920055257vmin currentColor;
+}
+.background span:nth-child(18) {
+ color: #e9b91b;
+ top: 72%;
+ left: 46%;
+ animation-duration: 85s;
+ animation-delay: -42s;
+ transform-origin: 14vw 6vh;
+ box-shadow: 2vmin 0 0.5774089805359588vmin currentColor;
+}
+
+
diff --git a/project/src/app/components/bg-gif/bg-gif.component.spec.ts b/project/src/app/components/bg-gif/bg-gif.component.spec.ts
new file mode 100644
index 0000000..098313b
--- /dev/null
+++ b/project/src/app/components/bg-gif/bg-gif.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { BgGifComponent } from './bg-gif.component';
+
+describe('BgGifComponent', () => {
+ let component: BgGifComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [BgGifComponent]
+ })
+ .compileComponents();
+
+ fixture = TestBed.createComponent(BgGifComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/project/src/app/components/bg-gif/bg-gif.component.ts b/project/src/app/components/bg-gif/bg-gif.component.ts
new file mode 100644
index 0000000..9432fd2
--- /dev/null
+++ b/project/src/app/components/bg-gif/bg-gif.component.ts
@@ -0,0 +1,12 @@
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'app-bg-gif',
+ standalone: true,
+ imports: [],
+ templateUrl: './bg-gif.component.html',
+ styleUrl: './bg-gif.component.scss'
+})
+export class BgGifComponent {
+
+}
diff --git a/project/src/app/components/dashboard/change-password/change-password.component.ts b/project/src/app/components/dashboard/change-password/change-password.component.ts
index 8ad747c..c984948 100644
--- a/project/src/app/components/dashboard/change-password/change-password.component.ts
+++ b/project/src/app/components/dashboard/change-password/change-password.component.ts
@@ -32,7 +32,7 @@ export class ChangePasswordComponent {
newPassword: this.formGroup.value.newPassword
}
this.modifyService.changePassword(data);
- this.router.navigate(['dashboard/profile'])
+ this.router.navigate(['dashboard/home'])
} else {
alert("مشکلی در ورودی های شما وجود دارد.")
}
diff --git a/project/src/app/components/dashboard/dashboard.component.html b/project/src/app/components/dashboard/dashboard.component.html
index bb92da4..3ee561a 100644
--- a/project/src/app/components/dashboard/dashboard.component.html
+++ b/project/src/app/components/dashboard/dashboard.component.html
@@ -1,12 +1,16 @@
-
-
+
diff --git a/project/src/app/components/dashboard/dashboard.component.scss b/project/src/app/components/dashboard/dashboard.component.scss
index 8392e2e..8578725 100644
--- a/project/src/app/components/dashboard/dashboard.component.scss
+++ b/project/src/app/components/dashboard/dashboard.component.scss
@@ -1,25 +1,24 @@
@import "../../../variables";
@layer components {
- .dashboard-navbar-icon {
- @apply h-[3rem] w-[3rem] hover:cursor-pointer absolute top-1 right-0 rounded-2xl;
- background-color: $primary-color;
- color: $bg-color;
- }
- .dashboard-navbar-back-icon {
- @apply h-[3rem] w-[3rem] hover:cursor-pointer;
- }
- .dashboard-navbar {
- @apply self-stretch w-[100vw] lg:w-[20vw] p-4;
- background-color: $primary-color;
- color: $bg-color;
- }
- .dashboard-content {
- @apply self-stretch flex w-[99vw] flex-row justify-center items-center;
+ // .dashboard-navbar-icon {
+ // @apply h-[3rem] w-[3rem] hover:cursor-pointer absolute top-1 right-0 rounded-2xl;
+ // background-color: $primary-color;
+ // color: $bg-color;
+ // }
+ // .dashboard-navbar-back-icon {
+ // @apply h-[3rem] w-[3rem] hover:cursor-pointer;
+ // }
+ // .dashboard-navbar {
+ // @apply self-stretch w-[100vw] lg:w-[20vw] p-4;
+ // background-color: $primary-color;
+ // color: $bg-color;
+ // }
+ // .dashboard-content {
+ // @apply self-stretch flex w-[80vw] flex-row justify-center items-center;
+ // background-color: $bg-color;
+ // }
+ .fullscreen{
background-color: $bg-color;
}
}
-
-.dashboard-content {
- align-self: start;
-}
diff --git a/project/src/app/components/dashboard/dashboard.component.ts b/project/src/app/components/dashboard/dashboard.component.ts
index 65e2a0e..196963f 100644
--- a/project/src/app/components/dashboard/dashboard.component.ts
+++ b/project/src/app/components/dashboard/dashboard.component.ts
@@ -3,7 +3,8 @@ import {NavbarComponent} from "./navbar/navbar.component";
import {RouterOutlet} from "@angular/router";
import {NgIconComponent, provideIcons} from "@ng-icons/core";
import { heroBars3, heroArrowUturnRight } from '@ng-icons/heroicons/outline'
-import {isPlatformBrowser} from "@angular/common";
+import {isPlatformBrowser, NgClass} from "@angular/common";
+import { BgGifComponent } from "../bg-gif/bg-gif.component";
@Component({
selector: 'app-dashboard',
@@ -11,8 +12,10 @@ import {isPlatformBrowser} from "@angular/common";
imports: [
NavbarComponent,
RouterOutlet,
- NgIconComponent
- ],
+ NgIconComponent,
+ NgClass,
+ BgGifComponent
+],
providers: [provideIcons({heroBars3, heroArrowUturnRight})],
templateUrl: './dashboard.component.html',
styleUrl: './dashboard.component.scss'
@@ -72,4 +75,10 @@ constructor(private renderer: Renderer2, @Inject(PLATFORM_ID) private platform:
this.renderer.setStyle(this.contentRef.nativeElement, 'inline-size', '99vw');
this.renderer.setStyle(this.contentRef.nativeElement, 'display', 'flex');
}
+
+ isMenuOpen = false;
+
+ toggleMenu() {
+ this.isMenuOpen = !this.isMenuOpen;
+ }
}
diff --git a/project/src/app/components/dashboard/dashboardhome/dashboardhome.component.html b/project/src/app/components/dashboard/dashboardhome/dashboardhome.component.html
new file mode 100644
index 0000000..eee6284
--- /dev/null
+++ b/project/src/app/components/dashboard/dashboardhome/dashboardhome.component.html
@@ -0,0 +1,6 @@
+
+
+
+
به کد استار خوش امدید
+
+
\ No newline at end of file
diff --git a/project/src/app/components/dashboard/dashboardhome/dashboardhome.component.scss b/project/src/app/components/dashboard/dashboardhome/dashboardhome.component.scss
new file mode 100644
index 0000000..a591c63
--- /dev/null
+++ b/project/src/app/components/dashboard/dashboardhome/dashboardhome.component.scss
@@ -0,0 +1,3 @@
+.VazirmatnBold{
+ font-family: "VazirmatnBold" !important;
+}
\ No newline at end of file
diff --git a/project/src/app/components/dashboard/dashboardhome/dashboardhome.component.spec.ts b/project/src/app/components/dashboard/dashboardhome/dashboardhome.component.spec.ts
new file mode 100644
index 0000000..e32b785
--- /dev/null
+++ b/project/src/app/components/dashboard/dashboardhome/dashboardhome.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { DashboardhomeComponent } from './dashboardhome.component';
+
+describe('DashboardhomeComponent', () => {
+ let component: DashboardhomeComponent;
+ let fixture: ComponentFixture
;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [DashboardhomeComponent]
+ })
+ .compileComponents();
+
+ fixture = TestBed.createComponent(DashboardhomeComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/project/src/app/components/dashboard/dashboardhome/dashboardhome.component.ts b/project/src/app/components/dashboard/dashboardhome/dashboardhome.component.ts
new file mode 100644
index 0000000..918d651
--- /dev/null
+++ b/project/src/app/components/dashboard/dashboardhome/dashboardhome.component.ts
@@ -0,0 +1,12 @@
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'app-dashboardhome',
+ standalone: true,
+ imports: [],
+ templateUrl: './dashboardhome.component.html',
+ styleUrl: './dashboardhome.component.scss'
+})
+export class DashboardhomeComponent {
+
+}
diff --git a/project/src/app/components/dashboard/edit-profile/edit-profile.component.ts b/project/src/app/components/dashboard/edit-profile/edit-profile.component.ts
index 2faa6f3..ba2b8a6 100644
--- a/project/src/app/components/dashboard/edit-profile/edit-profile.component.ts
+++ b/project/src/app/components/dashboard/edit-profile/edit-profile.component.ts
@@ -36,7 +36,7 @@ export class EditProfileComponent {
userName: this.formGroup.value.userName || this.userService.getUser()?.userName
}
this.modifyService.modifyUser(data);
- this.router.navigate(['dashboard/profile']);
+ this.router.navigate(['dashboard/home']);
} else {
alert("اشکالی در ورودی های شما وجود دارد.")
}
diff --git a/project/src/app/components/dashboard/manage-users/manage-users.component.html b/project/src/app/components/dashboard/manage-users/manage-users.component.html
index 544c483..607218e 100644
--- a/project/src/app/components/dashboard/manage-users/manage-users.component.html
+++ b/project/src/app/components/dashboard/manage-users/manage-users.component.html
@@ -1 +1,89 @@
-manage-users works!
+
+
+
+
+
+
+ @for (user of finalUsers; track user.userName) {
+
+
{{ user.firstName }} {{ user.lastName }}
+ {{ user.userName }}
+ {{ user.email }}
+
+
+ }
+
+
+
+
diff --git a/project/src/app/components/dashboard/manage-users/manage-users.component.scss b/project/src/app/components/dashboard/manage-users/manage-users.component.scss
index e69de29..3d0351d 100644
--- a/project/src/app/components/dashboard/manage-users/manage-users.component.scss
+++ b/project/src/app/components/dashboard/manage-users/manage-users.component.scss
@@ -0,0 +1,87 @@
+@import '../../../../variables';
+
+@layer components {
+ .contain {
+ @apply flex flex-col items-center justify-start text-[2rem];
+
+ block-size: 100vh;
+ }
+ .search-form {
+ @apply p-4 flex flex-row items-center gap-8;
+
+ input {
+ @apply p-4 focus:outline-none rounded-2xl border-b transition-all duration-200 delay-75 ease-in-out;
+ border-color: black;
+
+ &:hover {
+ @apply transition-all duration-200 delay-75 ease-in-out;
+ border-color: $bg-color;
+ }
+ }
+
+ button[type="button"] {
+ @apply flex flex-row items-center gap-2;
+
+ .plusicon {
+ @apply w-[2.2rem] h-[2.2rem]
+ }
+ }
+ }
+ .line {
+ @apply border border-black w-full m-4;
+ }
+ .users-container {
+ @apply flex flex-row flex-wrap justify-center gap-6 p-8;
+ }
+ .user-container {
+ @apply flex flex-col items-center gap-4 p-4 border border-black rounded-2xl;
+
+ h3 {
+ @apply text-[1.8rem]
+ }
+
+ h4 {
+ @apply text-[1.6rem]
+ }
+
+ form {
+ @apply flex flex-row gap-4;
+
+ select {
+ @apply border border-black rounded-2xl text-[1.5rem];
+ }
+ }
+ }
+ .button {
+ @apply p-4 border transition-all duration-300 ease-in-out rounded-3xl text-[1.5rem];
+ background-color: $primary-color;
+ color: $bg-color;
+
+ &:hover {
+ @apply transition-all duration-300 ease-in-out;
+ background-color: $bg-color;
+ color: black;
+ }
+ }
+ .form-container {
+ @apply hidden absolute p-6 flex-col justify-center items-center text-[2rem] rounded-2xl;
+ background-color: $primary-color;
+ color: $bg-color;
+
+ inline-size: 50rem;
+ block-size: 85rem;
+
+ inset-block-start: calc(50vh - 42.5rem);
+ inset-inline-end: calc(50vw - 25rem);
+
+ .xicon {
+ @apply absolute w-[3.5rem] h-[3.5rem] top-0 right-0 hover:cursor-pointer;
+ color: $bg-color;
+ }
+
+ select {
+ @apply p-2 m-2 focus:outline-none rounded-2xl;
+ color: black
+ }
+ }
+}
diff --git a/project/src/app/components/dashboard/manage-users/manage-users.component.ts b/project/src/app/components/dashboard/manage-users/manage-users.component.ts
index 0b3fc93..c900cec 100644
--- a/project/src/app/components/dashboard/manage-users/manage-users.component.ts
+++ b/project/src/app/components/dashboard/manage-users/manage-users.component.ts
@@ -1,12 +1,109 @@
-import { Component } from '@angular/core';
+import {Component, ElementRef, ViewChild} from '@angular/core';
+import User from "../../../interfaces/user";
+import {HttpClient} from "@angular/common/http";
+import {API_BASE_URL} from "../../../app.config";
+import {ModifyUserService} from "../../../services/modify-user/modify-user.service";
+import {NgIconComponent, provideIcons} from "@ng-icons/core";
+import {heroUserPlus, heroXMark} from "@ng-icons/heroicons/outline";
+import {FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators} from "@angular/forms";
+import {UserService} from "../../../services/user/user.service";
+import {Router} from "@angular/router";
+import {BlurClickDirective} from "../../../directives/blur-click.directive";
@Component({
selector: 'app-manage-users',
standalone: true,
- imports: [],
+ imports: [NgIconComponent, FormsModule, ReactiveFormsModule, BlurClickDirective],
templateUrl: './manage-users.component.html',
- styleUrl: './manage-users.component.scss'
+ styleUrl: './manage-users.component.scss',
+ providers: [provideIcons({heroUserPlus, heroXMark})]
})
export class ManageUsersComponent {
+ users: User[] | undefined = undefined;
+ finalUsers: User[] | undefined = undefined;
+ formGroup = new FormGroup({
+ firstName: new FormControl('', Validators.required),
+ lastName: new FormControl('', Validators.required),
+ email: new FormControl('', [Validators.required, Validators.email]),
+ userName: new FormControl('', Validators.required),
+ password: new FormControl('', Validators.required),
+ role: new FormControl('Admin', Validators.required),
+ })
+
+ @ViewChild('formElement') formElement!: ElementRef;
+ @ViewChild('searchElement') searchElement!: ElementRef;
+
+ constructor(private modifyService: ModifyUserService, private http: HttpClient, private userService: UserService,
+ private router: Router) {}
+
+ ngOnInit(): void {
+ const token = this.getToken();
+ this.http.get(API_BASE_URL + 'identity', {headers: {'Authorization': "bearer " + token}})
+ .subscribe((response) => {
+ this.users = response;
+ this.finalUsers = response;
+ });
+ }
+
+ handleChangeRole(user: User): void {
+ const index = this?.finalUsers?.indexOf(user);
+ if (index !== -1 && index !== undefined && this.finalUsers !== undefined) {
+ const selected = (document.getElementById(this.finalUsers[index].userName!) as HTMLSelectElement).value;
+ if (selected === this.finalUsers[index].role!.toLowerCase()) {
+ return;
+ }
+ this.finalUsers[index].role = selected;
+ this.modifyService.alterRole(this.finalUsers[index].userName!, selected);
+ const user = this.userService.getUser();
+ if (user?.userName === this.finalUsers[index].userName!) {
+ user.role = selected;
+ this.userService.setUser(user);
+ this.router.navigateByUrl('dashboard/home');
+ }
+ }
+ }
+
+ getToken(): string | null {
+ let token = localStorage.getItem("token");
+ if (token) {
+ token = token.substring(1, token.length - 1);
+ }
+ return token;
+ }
+
+ handleAdd(): void {
+ if (this.formGroup.valid) {
+ const data = {
+ firstName: this.formGroup.value.firstName,
+ lastName: this.formGroup.value.lastName,
+ userName: this.formGroup.value.userName,
+ email: this.formGroup.value.email,
+ password: this.formGroup.value.password,
+ role: this.formGroup.value.role,
+ }
+ const token = this.getToken();
+ this.http.post(API_BASE_URL + 'identity/signup', data, {headers: {'Authorization': "Bearer " + token}})
+ .subscribe((res) => {
+ this.finalUsers?.push(res);
+ this.handleClose();
+ });
+ } else {
+ console.log(this.formGroup.value);
+ }
+ }
+
+ handleShowForm(): void {
+ this.formElement.nativeElement.style.display = 'flex';
+ }
+
+ handleClose(): void {
+ this.formElement.nativeElement.style.display = 'none';
+ }
+
+ handleSearch() {
+ const value = this.searchElement.nativeElement.value;
+ this.finalUsers = this.users?.filter(user => user.userName?.includes(value) ||
+ user.firstName?.includes(value) || user.lastName?.includes(value) || user.email?.includes(value));
+ }
}
diff --git a/project/src/app/components/dashboard/navbar/navbar.component.html b/project/src/app/components/dashboard/navbar/navbar.component.html
index 10f8dd4..ac23a68 100644
--- a/project/src/app/components/dashboard/navbar/navbar.component.html
+++ b/project/src/app/components/dashboard/navbar/navbar.component.html
@@ -1,10 +1,38 @@
- - پروفایل
- - عوض کردن گذرواژه
- - ادیت کردن پروفایل
+
+ -
+
+
+
+ خانه
+
+
+
+
+ -
+
+
+ پروفایل
+
+
@if (user?.role?.toLowerCase() === "admin") {
- - مدیریت کاربران
+ -
+
+
+ مدیریت کاربران
+
+
}
- - مشاهده و آنالیز فایل ها
- - خروج از حساب
+ -
+
+
+ مشاهده و آنالیز فایل ها
+
+
diff --git a/project/src/app/components/dashboard/navbar/navbar.component.scss b/project/src/app/components/dashboard/navbar/navbar.component.scss
index 8fdb2ba..6b1558c 100644
--- a/project/src/app/components/dashboard/navbar/navbar.component.scss
+++ b/project/src/app/components/dashboard/navbar/navbar.component.scss
@@ -1,21 +1,75 @@
@import '../../../../variables';
@layer components {
+
.navbar-list {
- @apply flex flex-col justify-start p-4 gap-2;
+ @apply flex flex-col justify-start gap-[20px] p-[1.5rem] w-full self-center ;
+ background-color: $primary-color;
+ height: 90%;
+ border-radius: 20px 0 0px 20px;
+
+ .logo{
+ height: 210px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-direction: column;
+ gap: 25px;
+ p{
+ color: $bg-color;
+ font-size: 32px;
+ }
+ svg{
+ height: 50px;
+ fill: #fdfdfd;
+ width: 50px;
+ padding: 5px;
+ }
}
+}
.navbar-item {
- @apply text-[2rem] hover:cursor-pointer border-b-2 p-4 transition-all duration-300 delay-75 ease-in-out;
- border-bottom-color: $secondary-color;
+ @apply text-[2rem] hover:cursor-pointer transition-all delay-75 ease-in-out rounded-2xl;
+ color: $secondary-color;
- &:hover {
- @apply transition-all duration-300 delay-75 ease-in-out;
+ background-color: $primary-color;
+ border: 3px solid $primary-color;
+ height: 65px;
+ display: grid;
+ grid-template-columns: 1fr 5fr 1fr;
+ align-items: center;
+ gap: 10px;
+ transition-duration: 0.3s;
- color: $accent-color;
- border-bottom-color: $accent-color;
+ padding-right: 10px;
+ &:hover {
+ // @apply transition-all duration-300 delay-75 ease-in-out;
+ color: $secondary-color;
+ // background-color: $secondary-color;
+ border: 3px solid $secondary-color;
+ .heroArrowLeft{
+ color: $secondary-color;
+ display: block;
+ transition-duration: 0.3s;
+ }
+ }
+ ng-icon{
+ --ng-icon__size: 2.3rem !important;
+ }
+ .heroArrowLeft{
+ display: none;
}
}
.active {
background: $secondary-color;
+ color: $bg-color;
+
+ &:hover {
+ color: $bg-color
+ }
+
+ .heroArrowLeft {
+ display: none;
+ }
}
+
}
diff --git a/project/src/app/components/dashboard/navbar/navbar.component.ts b/project/src/app/components/dashboard/navbar/navbar.component.ts
index c069e76..6c25544 100644
--- a/project/src/app/components/dashboard/navbar/navbar.component.ts
+++ b/project/src/app/components/dashboard/navbar/navbar.component.ts
@@ -2,25 +2,35 @@ import { Component } from '@angular/core';
import {RouterLink, RouterLinkActive} from "@angular/router";
import {UserService} from "../../../services/user/user.service";
import User from "../../../interfaces/user";
+import { NgIconComponent, provideIcons } from '@ng-icons/core';
+import { heroArrowLeft, heroChartBar, heroHome, heroUserCircle, heroUserGroup } from '@ng-icons/heroicons/outline';
+import { heroChartBarSolid, heroHomeSolid, heroUserCircleSolid, heroUserGroupSolid } from '@ng-icons/heroicons/solid';
+import { Location } from '@angular/common';
@Component({
selector: 'app-navbar',
standalone: true,
imports: [
RouterLink,
- RouterLinkActive
+ RouterLinkActive,
+ NgIconComponent
],
templateUrl: './navbar.component.html',
- styleUrl: './navbar.component.scss'
+ styleUrl: './navbar.component.scss',
+ providers: [provideIcons({heroHome,heroHomeSolid,heroArrowLeft,heroUserGroup,heroUserGroupSolid,heroUserCircle,heroUserCircleSolid,heroChartBar,heroChartBarSolid})]
+
})
export class NavbarComponent {
user!: User | undefined;
- constructor(private userService: UserService) {
+ constructor(private userService: UserService ,private location: Location) {
this.user = this.userService.getUser();
}
handleLogout() {
this.userService.logout();
}
+ isActive(route: string): boolean {
+ return this.location.path() === '/dashboard/' + route;
+ }
}
diff --git a/project/src/app/components/dashboard/profile/profile.component.html b/project/src/app/components/dashboard/profile/profile.component.html
index 7f01448..ed6668f 100644
--- a/project/src/app/components/dashboard/profile/profile.component.html
+++ b/project/src/app/components/dashboard/profile/profile.component.html
@@ -1,8 +1,25 @@
-
{{ user?.firstName }} {{ user?.lastName }}
-
{{ user?.userName }}
-
{{ user?.email }}
+
+ نام و نام خانوادگی:
+ {{ user?.firstName }}
+ {{ user?.lastName }}
+
+
+
+ نام کاربری:
+
+
+ {{ user?.userName }}
+
+
+
+
+ ایمیل:
+
+ {{ user?.email }}
+
+
وضعیت:
@if (user?.role?.toLowerCase() === "admin") {
ادمین سیستم
@@ -14,4 +31,15 @@
{{ user?.userName }}
آنالیزگر
}
+
+ -
+ عوض کردن گذرواژه
+
+ -
+ ادیت کردن پروفایل
+
+ -
+ خروج از حساب
+
+
diff --git a/project/src/app/components/dashboard/profile/profile.component.scss b/project/src/app/components/dashboard/profile/profile.component.scss
index 029cee2..731be3a 100644
--- a/project/src/app/components/dashboard/profile/profile.component.scss
+++ b/project/src/app/components/dashboard/profile/profile.component.scss
@@ -1,8 +1,12 @@
@import '../../../../variables';
@layer components {
.profile {
- @apply border-y-2 p-12 rounded-3xl flex flex-col items-center gap-12;
- border-color: $primary-color;
+ @apply p-12 rounded-3xl flex flex-col items-center gap-12;
+ background: $secondary-color;
+ color: #fdfdfd;
+ border-radius: 20px;
+ margin-inline: 50px;
+ height: 90vh;
}
.profile-name {
@apply text-[8rem];
diff --git a/project/src/app/components/dashboard/profile/profile.component.ts b/project/src/app/components/dashboard/profile/profile.component.ts
index 6dbb615..ee3477c 100644
--- a/project/src/app/components/dashboard/profile/profile.component.ts
+++ b/project/src/app/components/dashboard/profile/profile.component.ts
@@ -1,5 +1,6 @@
import { Component } from '@angular/core';
import User from "../../../interfaces/user";
+import {RouterLink, RouterLinkActive} from "@angular/router";
import {UserService} from "../../../services/user/user.service";
import {NgIconComponent, provideIcons} from "@ng-icons/core";
import { heroMagnifyingGlassSolid, heroUserCircleSolid, heroCircleStackSolid } from '@ng-icons/heroicons/solid';
@@ -7,7 +8,9 @@ import { heroMagnifyingGlassSolid, heroUserCircleSolid, heroCircleStackSolid } f
@Component({
selector: 'app-profile',
standalone: true,
- imports: [NgIconComponent],
+ imports: [RouterLink,
+ RouterLinkActive,
+ NgIconComponent ],
templateUrl: './profile.component.html',
styleUrl: './profile.component.scss',
providers: [provideIcons({heroUserCircleSolid, heroMagnifyingGlassSolid, heroCircleStackSolid})]
@@ -20,4 +23,8 @@ export class ProfileComponent {
ngOnInit() {
this.user = this.userService.getUser();
}
+
+ handleLogout() {
+ this.userService.logout();
+ }
}
diff --git a/project/src/app/components/dashboard/show-data/pipes/persian-date.pipe.spec.ts b/project/src/app/components/dashboard/show-data/pipes/persian-date.pipe.spec.ts
new file mode 100644
index 0000000..51860f8
--- /dev/null
+++ b/project/src/app/components/dashboard/show-data/pipes/persian-date.pipe.spec.ts
@@ -0,0 +1,8 @@
+import { PersianDatePipe } from './persian-date.pipe';
+
+describe('PersianDatePipe', () => {
+ it('create an instance', () => {
+ const pipe = new PersianDatePipe();
+ expect(pipe).toBeTruthy();
+ });
+});
diff --git a/project/src/app/components/dashboard/show-data/pipes/persian-date.pipe.ts b/project/src/app/components/dashboard/show-data/pipes/persian-date.pipe.ts
new file mode 100644
index 0000000..d41edd0
--- /dev/null
+++ b/project/src/app/components/dashboard/show-data/pipes/persian-date.pipe.ts
@@ -0,0 +1,24 @@
+import { Pipe, PipeTransform } from '@angular/core';
+import * as jalaali from 'jalaali-js';
+
+@Pipe({
+ name: 'persianDate',
+ standalone: true
+})
+export class PersianDatePipe implements PipeTransform {
+
+ transform(value: string): string {
+ if (!value) return '';
+
+ const date = new Date(value);
+ const jDate = jalaali.toJalaali(date.getFullYear(), date.getMonth() + 1, date.getDate());
+
+ // Format the Persian date as desired (YYYY/MM/DD)
+ return `${jDate.jy}/${this.padZero(jDate.jm)}/${this.padZero(jDate.jd)}`;
+ }
+
+ // Helper method to pad single digits with a leading zero
+ private padZero(value: number): string {
+ return value < 10 ? '0' + value : value.toString();
+ }
+}
diff --git a/project/src/app/components/dashboard/show-data/pipes/rial-pipe.pipe.spec.ts b/project/src/app/components/dashboard/show-data/pipes/rial-pipe.pipe.spec.ts
new file mode 100644
index 0000000..b48d723
--- /dev/null
+++ b/project/src/app/components/dashboard/show-data/pipes/rial-pipe.pipe.spec.ts
@@ -0,0 +1,8 @@
+import { RialPipePipe } from './rial-pipe.pipe';
+
+describe('RialPipePipe', () => {
+ it('create an instance', () => {
+ const pipe = new RialPipePipe();
+ expect(pipe).toBeTruthy();
+ });
+});
diff --git a/project/src/app/components/dashboard/show-data/pipes/rial-pipe.pipe.ts b/project/src/app/components/dashboard/show-data/pipes/rial-pipe.pipe.ts
new file mode 100644
index 0000000..2a64b2f
--- /dev/null
+++ b/project/src/app/components/dashboard/show-data/pipes/rial-pipe.pipe.ts
@@ -0,0 +1,20 @@
+import { Pipe, PipeTransform } from '@angular/core';
+
+@Pipe({
+ name: 'rialPipe',
+ standalone: true
+})
+export class RialPipePipe implements PipeTransform {
+
+ transform(value: number): string {
+ value /= 10;
+ let result = String(value);
+ let cntr = result.length - 3;
+ while (cntr > 0) {
+ result = result.substring(0, cntr) + ',' + result.substring(cntr, result.length);
+ cntr -= 3;
+ }
+ return result + ' تومان';
+ }
+
+}
diff --git a/project/src/app/components/dashboard/show-data/show-data.component.html b/project/src/app/components/dashboard/show-data/show-data.component.html
index 3606b4d..e7a003d 100644
--- a/project/src/app/components/dashboard/show-data/show-data.component.html
+++ b/project/src/app/components/dashboard/show-data/show-data.component.html
@@ -1,7 +1,55 @@
-@if (user?.role?.toLowerCase() === "admin" || user?.role?.toLowerCase() === "dataadmin") {
-
-}
+
+ @if (user?.role?.toLowerCase() === "admin" || user?.role?.toLowerCase() === "dataadmin") {
+
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+ حساب مبدا |
+ حساب مقصد |
+ مبلغ تراکنش |
+ تاریخ |
+ نوع تراکنش |
+
+
+
+ @for (trans of data; track trans.TransactionId) {
+
+ {{ trans.sourceAccountId }} |
+ {{ trans.destinationAccountId }} |
+ {{ trans.amount | rialPipe }} |
+ {{ trans.date | persianDate }} |
+ {{ trans.type }} |
+
+ } @empty {
+ هیچ داده ای برای نمایش یافت نشد.
+ }
+
+
+
+
+
+
diff --git a/project/src/app/components/dashboard/show-data/show-data.component.scss b/project/src/app/components/dashboard/show-data/show-data.component.scss
index c28cec1..2b0fc75 100644
--- a/project/src/app/components/dashboard/show-data/show-data.component.scss
+++ b/project/src/app/components/dashboard/show-data/show-data.component.scss
@@ -1,30 +1,134 @@
@import "../../../../variables";
@layer utilities {
+ .data-show-container {
+ @apply flex flex-col justify-start items-center gap-4 h-[100vh];
+ }
.upload-form {
- @apply border-2 border-t-0 rounded-2xl p-4;
- border-color: $neutral-color;
+ @apply border-2 border-t-0 rounded-2xl p-4 text-[2rem] flex flex-row items-center justify-start gap-6;
+ border-color: black;
}
.upload-input {
@apply hidden;
}
.input-label {
- @apply cursor-pointer text-[2rem] m-6 p-2;
+ @apply cursor-pointer p-2;
&:hover {
@apply border-b border-b-black
}
}
.form-button {
- @apply text-[2rem];
color: $primary-color;
-
border-color: $primary-color;
&:hover {
color: $bg-color;
+ background-color: $primary-color;
+ }
+
+ &:disabled {
+ opacity: 0.5;
+
+ &:hover {
+ color: $primary-color;
+ background-color: $bg-color;
+ }
+ }
+ }
+ .select-group {
+ @apply focus:outline-none p-2 rounded-2xl;
+ background-color: $bg-color;
+
+ border: 1px solid black;
+ }
+ .show-button {
+ @apply text-[2rem] self-center border rounded-3xl p-3 transition-all duration-300 ease-in-out;
+ color: $primary-color;
+ border-color: $primary-color;
+ &:hover {
+ @apply transition-all duration-300 ease-in-out;
background-color: $primary-color;
+ color: $bg-color;
+ }
+ }
+ .table-container {
+ @apply absolute p-6 justify-center items-center rounded-2xl hidden text-[1.5rem] overflow-auto;
+ background-color: $primary-color;
+ color: $bg-color;
+
+ inline-size: 70rem;
+ block-size: 50rem;
+
+ inset-block-start: calc(50vh - 25rem);
+ inset-inline-start: calc(50vw - 35rem);
+
+ scroll-behavior: smooth;
+ scrollbar-width: thin;
+ scrollbar-gutter: stable;
+ scrollbar-color: $bg-color $primary-color;
+ }
+ .table {
+ @apply border-separate border-spacing-4 table-auto mt-10;
+
+ thead {
+ @apply top-0 sticky;
+ background-color: $bg-color;
+ color: black;
+ }
+
+ td, th {
+ @apply border-b p-1;
+ border-color: $bg-color;
+ }
+ }
+ .xicon {
+ @apply absolute w-[4rem] h-[4rem] top-0 right-0;
+ color: $bg-color;
+ }
+ .form-search-user {
+ @apply text-[2rem] self-center border rounded-2xl transition-all duration-300 ease-in-out p-4 flex flex-row
+ gap-4 items-center;
+ inline-size: 70rem;
+
+ input {
+ @apply flex-grow p-4 rounded-2xl;
+ }
+ }
+ #graph-container {
+ @apply self-stretch flex-grow m-8 rounded-xl flex flex-row justify-center items-center;
+ background-color: $secondary-color;
+ }
+}
+.context-menu-container {
+ position: absolute;
+
+ display: none;
+
+ padding: 1rem;
+
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+
+ background-color: $primary-color;
+ color: $bg-color;
+
+ font-size: 1.6rem;
+
+ ul {
+ list-style: none;
+
+ padding: 0.1rem;
+
+ li {
+ padding: 0.5rem;
+ user-select: none;
+
+ &:hover {
+ cursor: pointer;
+ }
}
}
}
diff --git a/project/src/app/components/dashboard/show-data/show-data.component.ts b/project/src/app/components/dashboard/show-data/show-data.component.ts
index ee1a88f..0c60892 100644
--- a/project/src/app/components/dashboard/show-data/show-data.component.ts
+++ b/project/src/app/components/dashboard/show-data/show-data.component.ts
@@ -1,42 +1,374 @@
-import {Component, ElementRef, ViewChild} from '@angular/core';
+import {Component, ElementRef, EventEmitter, HostListener, Output, ViewChild} from '@angular/core';
import {UserService} from "../../../services/user/user.service";
import User from "../../../interfaces/user";
import {FormsModule} from "@angular/forms";
import {HttpClient} from "@angular/common/http";
import {API_BASE_URL} from "../../../app.config";
+import {RialPipePipe} from "./pipes/rial-pipe.pipe";
+import {PersianDatePipe} from "./pipes/persian-date.pipe";
+import {heroXMark} from "@ng-icons/heroicons/outline";
+import {NgIconComponent, provideIcons} from "@ng-icons/core";
+import {BlurClickDirective} from "../../../directives/blur-click.directive";
+import * as d3 from 'd3';
+import {FetchDataService} from "../../../services/fetchData/fetch-data.service";
+
+interface Transaction {
+ TransactionId: number,
+ sourceAccountId: number,
+ destinationAccountId: number,
+ amount: number,
+ date: string,
+ type: string
+}
+
+interface Node {
+ x: number;
+ y: number;
+ vx: number;
+ vy: number;
+ fx?: number | null;
+ fy?: number | null;
+ label: string | number;
+}
+
+interface Link {
+ source: Node;
+ target: Node;
+ date: string;
+ amount: string;
+ type: string;
+}
@Component({
selector: 'app-show-data',
standalone: true,
imports: [
- FormsModule
+ FormsModule,
+ RialPipePipe,
+ PersianDatePipe,
+ NgIconComponent,
+ BlurClickDirective
],
templateUrl: './show-data.component.html',
- styleUrl: './show-data.component.scss'
+ styleUrl: './show-data.component.scss',
+ providers: [provideIcons({heroXMark})]
})
export class ShowDataComponent {
user!: User | undefined;
+ data: Transaction[] | undefined = undefined;
+ nodes: Node[] = [];
+ links: Link[] = [];
+
+ element!: d3.Selection;
+ svgGroup!: d3.Selection;
+ simulation!: d3.Simulation;
+ link!: d3.Selection;
+ linkLabelsAmount!: d3.Selection;
+ linkLabelsDate!: d3.Selection;
+ linkLabelsType!: d3.Selection;
+ node!: d3.Selection;
+ nodeLabels!: d3.Selection;
+
+
+ @Output() dataGotEvent = new EventEmitter();
+
@ViewChild('labelElement') labelElement!: ElementRef;
@ViewChild('inputElement') inputElement!: ElementRef;
+ @ViewChild('selectElement') selectElement!: ElementRef;
+ @ViewChild('dataElement') dataElement!: ElementRef;
+ @ViewChild('graphElement') graphElement!: ElementRef;
+ @ViewChild('contextElement') contextElement!: ElementRef;
+ @ViewChild('searchIdElement') searchIdElement!: ElementRef;
- constructor(private userService: UserService, private http: HttpClient) {
+ constructor(private userService: UserService, private http: HttpClient, private fetchDataService: FetchDataService) {
this.user = this.userService.getUser();
}
- handleChange() {
+ handleChange(): void {
if (this?.inputElement?.nativeElement?.files && this?.inputElement?.nativeElement?.files?.length > 0) {
this.labelElement.nativeElement.textContent = this?.inputElement?.nativeElement?.files[0].name;
}
}
+ handleDisabled(): boolean {
+ return !(this?.inputElement?.nativeElement?.files && this?.inputElement?.nativeElement?.files?.length > 0);
+ }
+
+ clearGraphTable(): void {
+ d3.select(this.graphElement.nativeElement).selectAll('*').remove();
+ }
+
+ async handleGetUser() {
+ const response = await this.fetchDataService.fetchDataById(this.searchIdElement.nativeElement.value);
+ if (response.length === 0) {
+ this.graphElement.nativeElement.textContent = "داده ای یافت نشد!";
+ return;
+ }
+ this.nodes = [];
+ this.links = [];
+ this.clearGraphTable();
+ this.nodes.push({
+ x: 1,
+ y: 1,
+ vx: 1,
+ vy: 1,
+ label: Number(this.searchIdElement.nativeElement.value)
+ });
+ for (const item of response) {
+ if (!this.nodes.find(node => node.label === item.accountId)) {
+ this.nodes.push({
+ x: this.nodes[this.nodes.length - 1] ? this.nodes[this.nodes.length - 1].x + 1 : 1,
+ y: this.nodes[this.nodes.length - 1] ? this.nodes[this.nodes.length - 1].y + 1 : 1,
+ vx: 1,
+ vy: 1,
+ label: item.accountId,
+ });
+ }
+ this.links.push({
+ source: item.transactionWithSources[0].sourceAcount === item.accountId ?
+ this.nodes.find(node => node.label === item.accountId)!
+ : this.nodes.find(node => node.label === Number(this.searchIdElement.nativeElement.value))!,
+ target: item.transactionWithSources[0].sourceAcount === item.accountId ?
+ this.nodes.find(node => node.label === Number(this.searchIdElement.nativeElement.value))!
+ : this.nodes.find(node => node.label === item.accountId)!,
+ type: item.transactionWithSources[0].type,
+ amount: (new RialPipePipe()).transform(item.transactionWithSources[0].amount),
+ date: (new PersianDatePipe()).transform(item.transactionWithSources[0].date),
+ })
+ }
+ this.dataGotEvent.emit();
+ }
+
onSubmit(): void {
const formData: FormData = new FormData();
+ const token: string | null = this.getToken();
if (this.inputElement.nativeElement.files) {
const file = this.inputElement.nativeElement.files[0];
formData.append('file', file);
- this.http.post(API_BASE_URL + 'Transaction/ImportTransactions', formData).subscribe((response) => {
- console.log(response);
- })
+ if (this.selectElement.nativeElement.value === "transaction") {
+ this.http.post(API_BASE_URL + 'transactions/upload', formData, {headers: {"Authorization": "Bearer " + token}}).subscribe((response) => {
+ console.log(response);
+ })
+ } else if (this.selectElement.nativeElement.value === "account") {
+ this.http.post(API_BASE_URL + 'accounts/upload', formData, {headers: {"Authorization": "Bearer " + token}}).subscribe((response) => {
+ console.log(response);
+ })
+ }
+ }
+ }
+
+ showData(): void {
+ this.dataElement.nativeElement.style.display = 'flex';
+ }
+
+ handleClose(): void {
+ this.dataElement.nativeElement.style.display = 'none';
+ }
+
+ getToken(): string | null {
+ let token = localStorage.getItem("token");
+ if (token) {
+ token = token.substring(1, token.length - 1);
+ }
+
+ return token;
+ }
+
+ async ngOnInit() {
+ const response = await this.fetchDataService.fetchData();
+ this.data = response;
+ for (const trans of response) {
+ if (!this.nodes.find(node => node.label === trans.sourceAccountId)) {
+ this.nodes.push({
+ x: this.nodes[this.nodes.length - 1] ? this.nodes[this.nodes.length - 1].x + 1 : 1,
+ y: this.nodes[this.nodes.length - 1] ? this.nodes[this.nodes.length - 1].y + 1 : 1,
+ vx: 1,
+ vy: 1,
+ label: trans.sourceAccountId,
+ });
+ }
+ if (!this.nodes.find(node => node.label === trans.destinationAccountId)) {
+ this.nodes.push({
+ x: this.nodes[this.nodes.length - 1] ? this.nodes[this.nodes.length - 1].x + 1 : 1,
+ y: this.nodes[this.nodes.length - 1] ? this.nodes[this.nodes.length - 1].y + 1 : 1,
+ vx: 1,
+ vy: 1,
+ label: trans.destinationAccountId,
+ });
+ }
+ this.links.push({
+ source: this.nodes.find(node => node.label === trans.sourceAccountId)!,
+ target: this.nodes.find(node => node.label === trans.destinationAccountId)!,
+ date: (new PersianDatePipe()).transform(trans.date),
+ type: trans.type,
+ amount: (new RialPipePipe()).transform(trans.amount),
+ });
}
+ this.dataGotEvent.emit();
+ }
+
+ @HostListener('dataGotEvent')
+ handleGraph(): void {
+ this.element = d3.select(this.graphElement.nativeElement)
+ .append('svg')
+ .attr('width', this.graphElement.nativeElement.clientWidth)
+ .attr('height', this.graphElement.nativeElement.clientHeight);
+
+
+ this.svgGroup = this.element.append('g');
+
+ const zoom = d3.zoom()
+ .scaleExtent([0.5, 4])
+ .on('zoom', (event) => {
+ this.svgGroup.attr('transform', event.transform);
+ });
+
+ this.element.call(zoom);
+
+ this.simulation = d3.forceSimulation(this.nodes)
+ .force("link", d3.forceLink(this.links));
+
+ this.svgGroup.append('defs').append('marker')
+ .attr('id', 'arrowhead')
+ .attr('viewBox', '-0 -5 10 10')
+ .attr('refX', 13)
+ .attr('refY', 0)
+ .attr('orient', 'auto')
+ .attr('markerWidth', 12)
+ .attr('markerHeight', 12)
+ .attr('xoverflow', 'visible')
+ .append('svg:path')
+ .attr('d', 'M 0,-5 L 10 ,0 L 0,5')
+ .attr('fill', '#FDFDFD')
+ .style('stroke', 'none');
+
+ this.link = this.svgGroup.append('g')
+ .attr('class', 'links')
+ .selectAll('line')
+ .data(this.links)
+ .enter()
+ .append('line')
+ .attr('stroke-width', 2)
+ .attr('stroke', '#FDFDFD')
+ .attr('marker-end', 'url(#arrowhead)');
+
+
+ this.linkLabelsAmount = this.svgGroup.append('g')
+ .attr('class', 'link-labels')
+ .selectAll('text')
+ .data(this.links)
+ .enter()
+ .append('text')
+ .attr('text-anchor', 'middle')
+ .attr('fill', '#172535')
+ .attr('style', 'user-select: none;font-weight:bold;font-size:1.5rem;')
+ .text((d: Link) => d.amount ? d.amount : "");
+
+ this.linkLabelsType = this.svgGroup.append('g')
+ .attr('class', 'link-labels')
+ .selectAll('text')
+ .data(this.links)
+ .enter()
+ .append('text')
+ .attr('text-anchor', 'middle')
+ .attr('fill', '#172535')
+ .attr('style', 'user-select: none;font-weight:bold;font-size:1.5rem;')
+ .text((d: Link) => d.type ? d.type : "");
+
+ this.linkLabelsDate = this.svgGroup.append('g')
+ .attr('class', 'link-labels')
+ .selectAll('text')
+ .data(this.links)
+ .enter()
+ .append('text')
+ .attr('text-anchor', 'middle')
+ .attr('fill', '#172535')
+ .attr('style', 'user-select: none;font-weight:bold;font-size:1.5rem;')
+ .text((d: Link) => d.date ? d.date : "");
+
+ this.node = this.svgGroup.append('g')
+ .attr('class', 'nodes')
+ .selectAll('circle')
+ .data(this.nodes)
+ .enter()
+ .append('circle')
+ .attr('r', 10)
+ .attr('fill', '#002B5B')
+ .call(d3.drag()
+ .on('start', (event: d3.D3DragEvent, d: Node) => {
+ if (!event.active) this.simulation.alphaTarget(0.3).restart();
+ d.fx = d.x;
+ d.fy = d.y;
+ })
+ .on('drag', (event: d3.D3DragEvent, d: Node) => {
+ d.fx = event.x;
+ d.fy = event.y;
+ })
+ .on('end', (event: d3.D3DragEvent, d: Node) => {
+ if (!event.active) this.simulation.alphaTarget(0);
+ d.fx = null;
+ d.fy = null;
+ })
+ );
+
+ this.node.on('contextmenu', (event: MouseEvent, d: Node) => {
+ event.preventDefault();
+
+ this.contextElement.nativeElement.style.display = 'flex';
+ this.contextElement.nativeElement.style.top = event.clientY + 'px';
+ this.contextElement.nativeElement.style.left = event.clientX + 'px';
+ });
+
+ this.nodeLabels = this.svgGroup.append('g')
+ .attr('class', 'node-labels')
+ .selectAll('text')
+ .data(this.nodes)
+ .enter()
+ .append('text')
+ .attr('text-anchor', 'middle')
+ .attr('dy', -10) // Position above the node
+ .attr('fill', '#172535')
+ .attr('style', 'user-select: none;font-weight:bold;font-size:1.5rem;')
+ .text((d: Node) => d.label ? d.label : "");
+
+ this.simulation.on('tick', () => {
+ this.link
+ .attr('x1', d => d.source.x)
+ .attr('y1', d => d.source.y)
+ .attr('x2', d => d.target.x)
+ .attr('y2', d => d.target.y);
+
+ this.node
+ .attr('cx', d => d.x)
+ .attr('cy', d => d.y);
+
+ // Update positions of node labels
+ this.nodeLabels
+ .attr('x', d => d.x)
+ .attr('y', d => d.y);
+
+ // Update positions of link labels
+ this.linkLabelsAmount
+ .attr('x', d => ((d.source as Node).x + (d.target as Node).x) / 2)
+ .attr('y', d => ((d.source as Node).y + (d.target as Node).y) / 2);
+
+ this.linkLabelsDate
+ .attr('x', d => ((d.source as Node).x + (d.target as Node).x) / 2)
+ .attr('y', d => ((d.source as Node).y + (d.target as Node).y) / 2 + 20);
+
+ this.linkLabelsType
+ .attr('x', d => ((d.source as Node).x + (d.target as Node).x) / 2)
+ .attr('y', d => ((d.source as Node).y + (d.target as Node).y) / 2 + 40);
+ });
+
+ this.simulation
+ .force('link', d3.forceLink(this.links).id((d, i) => i).distance(250))
+ .force('charge', d3.forceManyBody().strength(-350))
+ .force('center', d3.forceCenter(this.graphElement.nativeElement.clientWidth / 2, this.graphElement.nativeElement.clientHeight / 2));
+
+ }
+
+ handleCloseContext() {
+ this.contextElement.nativeElement.style.display = 'none';
}
}
diff --git a/project/src/app/components/login/login.component.html b/project/src/app/components/login/login.component.html
index 8fbbd13..82bd398 100644
--- a/project/src/app/components/login/login.component.html
+++ b/project/src/app/components/login/login.component.html
@@ -1,4 +1,4 @@
-