Migrierte Dienste und Komponenten. Machen Sie Komponenten „standalone“.
This commit is contained in:
parent
3962b202b9
commit
20d1fee79d
@ -1,336 +1,6 @@
|
|||||||
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
|
<body>
|
||||||
<!-- * * * * * * * * * * * The content below * * * * * * * * * * * -->
|
<app-nav-menu></app-nav-menu>
|
||||||
<!-- * * * * * * * * * * is only a placeholder * * * * * * * * * * -->
|
<main class="container-fluid">
|
||||||
<!-- * * * * * * * * * * and can be replaced. * * * * * * * * * * -->
|
<router-outlet></router-outlet>
|
||||||
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
|
</main>
|
||||||
<!-- * * * * * * * * * Delete the template below * * * * * * * * * -->
|
</body>
|
||||||
<!-- * * * * * * * to get started with your project! * * * * * * * -->
|
|
||||||
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
|
|
||||||
|
|
||||||
<style>
|
|
||||||
:host {
|
|
||||||
--bright-blue: oklch(51.01% 0.274 263.83);
|
|
||||||
--electric-violet: oklch(53.18% 0.28 296.97);
|
|
||||||
--french-violet: oklch(47.66% 0.246 305.88);
|
|
||||||
--vivid-pink: oklch(69.02% 0.277 332.77);
|
|
||||||
--hot-red: oklch(61.42% 0.238 15.34);
|
|
||||||
--orange-red: oklch(63.32% 0.24 31.68);
|
|
||||||
|
|
||||||
--gray-900: oklch(19.37% 0.006 300.98);
|
|
||||||
--gray-700: oklch(36.98% 0.014 302.71);
|
|
||||||
--gray-400: oklch(70.9% 0.015 304.04);
|
|
||||||
|
|
||||||
--red-to-pink-to-purple-vertical-gradient: linear-gradient(
|
|
||||||
180deg,
|
|
||||||
var(--orange-red) 0%,
|
|
||||||
var(--vivid-pink) 50%,
|
|
||||||
var(--electric-violet) 100%
|
|
||||||
);
|
|
||||||
|
|
||||||
--red-to-pink-to-purple-horizontal-gradient: linear-gradient(
|
|
||||||
90deg,
|
|
||||||
var(--orange-red) 0%,
|
|
||||||
var(--vivid-pink) 50%,
|
|
||||||
var(--electric-violet) 100%
|
|
||||||
);
|
|
||||||
|
|
||||||
--pill-accent: var(--bright-blue);
|
|
||||||
|
|
||||||
font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
|
|
||||||
Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
|
|
||||||
"Segoe UI Symbol";
|
|
||||||
box-sizing: border-box;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
-moz-osx-font-smoothing: grayscale;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 3.125rem;
|
|
||||||
color: var(--gray-900);
|
|
||||||
font-weight: 500;
|
|
||||||
line-height: 100%;
|
|
||||||
letter-spacing: -0.125rem;
|
|
||||||
margin: 0;
|
|
||||||
font-family: "Inter Tight", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
|
|
||||||
Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
|
|
||||||
"Segoe UI Symbol";
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin: 0;
|
|
||||||
color: var(--gray-700);
|
|
||||||
}
|
|
||||||
|
|
||||||
main {
|
|
||||||
width: 100%;
|
|
||||||
min-height: 100%;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
padding: 1rem;
|
|
||||||
box-sizing: inherit;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.angular-logo {
|
|
||||||
max-width: 9.2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-around;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 700px;
|
|
||||||
margin-bottom: 3rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content h1 {
|
|
||||||
margin-top: 1.75rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content p {
|
|
||||||
margin-top: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.divider {
|
|
||||||
width: 1px;
|
|
||||||
background: var(--red-to-pink-to-purple-vertical-gradient);
|
|
||||||
margin-inline: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pill-group {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: start;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 1.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pill {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
--pill-accent: var(--bright-blue);
|
|
||||||
background: color-mix(in srgb, var(--pill-accent) 5%, transparent);
|
|
||||||
color: var(--pill-accent);
|
|
||||||
padding-inline: 0.75rem;
|
|
||||||
padding-block: 0.375rem;
|
|
||||||
border-radius: 2.75rem;
|
|
||||||
border: 0;
|
|
||||||
transition: background 0.3s ease;
|
|
||||||
font-family: var(--inter-font);
|
|
||||||
font-size: 0.875rem;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 500;
|
|
||||||
line-height: 1.4rem;
|
|
||||||
letter-spacing: -0.00875rem;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pill:hover {
|
|
||||||
background: color-mix(in srgb, var(--pill-accent) 15%, transparent);
|
|
||||||
}
|
|
||||||
|
|
||||||
.pill-group .pill:nth-child(6n + 1) {
|
|
||||||
--pill-accent: var(--bright-blue);
|
|
||||||
}
|
|
||||||
.pill-group .pill:nth-child(6n + 2) {
|
|
||||||
--pill-accent: var(--french-violet);
|
|
||||||
}
|
|
||||||
.pill-group .pill:nth-child(6n + 3),
|
|
||||||
.pill-group .pill:nth-child(6n + 4),
|
|
||||||
.pill-group .pill:nth-child(6n + 5) {
|
|
||||||
--pill-accent: var(--hot-red);
|
|
||||||
}
|
|
||||||
|
|
||||||
.pill-group svg {
|
|
||||||
margin-inline-start: 0.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.social-links {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.73rem;
|
|
||||||
margin-top: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.social-links path {
|
|
||||||
transition: fill 0.3s ease;
|
|
||||||
fill: var(--gray-400);
|
|
||||||
}
|
|
||||||
|
|
||||||
.social-links a:hover svg path {
|
|
||||||
fill: var(--gray-900);
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 650px) {
|
|
||||||
.content {
|
|
||||||
flex-direction: column;
|
|
||||||
width: max-content;
|
|
||||||
}
|
|
||||||
|
|
||||||
.divider {
|
|
||||||
height: 1px;
|
|
||||||
width: 100%;
|
|
||||||
background: var(--red-to-pink-to-purple-horizontal-gradient);
|
|
||||||
margin-block: 1.5rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<main class="main">
|
|
||||||
<div class="content">
|
|
||||||
<div class="left-side">
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 982 239"
|
|
||||||
fill="none"
|
|
||||||
class="angular-logo"
|
|
||||||
>
|
|
||||||
<g clip-path="url(#a)">
|
|
||||||
<path
|
|
||||||
fill="url(#b)"
|
|
||||||
d="M388.676 191.625h30.849L363.31 31.828h-35.758l-56.215 159.797h30.848l13.174-39.356h60.061l13.256 39.356Zm-65.461-62.675 21.602-64.311h1.227l21.602 64.311h-44.431Zm126.831-7.527v70.202h-28.23V71.839h27.002v20.374h1.392c2.782-6.71 7.2-12.028 13.255-15.956 6.056-3.927 13.584-5.89 22.503-5.89 8.264 0 15.465 1.8 21.684 5.318 6.137 3.518 10.964 8.673 14.319 15.382 3.437 6.71 5.074 14.81 4.992 24.383v76.175h-28.23v-71.92c0-8.019-2.046-14.237-6.219-18.819-4.173-4.5-9.819-6.791-17.102-6.791-4.91 0-9.328 1.063-13.174 3.272-3.846 2.128-6.792 5.237-9.001 9.328-2.046 4.009-3.191 8.918-3.191 14.728ZM589.233 239c-10.147 0-18.82-1.391-26.103-4.091-7.282-2.7-13.092-6.382-17.511-10.964-4.418-4.582-7.528-9.655-9.164-15.219l25.448-6.136c1.145 2.372 2.782 4.663 4.991 6.954 2.209 2.291 5.155 4.255 8.837 5.81 3.683 1.554 8.428 2.291 14.074 2.291 8.019 0 14.647-1.964 19.884-5.81 5.237-3.845 7.856-10.227 7.856-19.064v-22.665h-1.391c-1.473 2.946-3.601 5.892-6.383 9.001-2.782 3.109-6.464 5.645-10.965 7.691-4.582 2.046-10.228 3.109-17.101 3.109-9.165 0-17.511-2.209-25.039-6.545-7.446-4.337-13.42-10.883-17.757-19.474-4.418-8.673-6.628-19.473-6.628-32.565 0-13.091 2.21-24.301 6.628-33.383 4.419-9.082 10.311-15.955 17.839-20.7 7.528-4.746 15.874-7.037 25.039-7.037 7.037 0 12.846 1.145 17.347 3.518 4.582 2.373 8.182 5.236 10.883 8.51 2.7 3.272 4.746 6.382 6.137 9.327h1.554v-19.8h27.821v121.749c0 10.228-2.454 18.737-7.364 25.447-4.91 6.709-11.538 11.7-20.048 15.055-8.509 3.355-18.165 4.991-28.884 4.991Zm.245-71.266c5.974 0 11.047-1.473 15.302-4.337 4.173-2.945 7.446-7.118 9.573-12.519 2.21-5.482 3.274-12.027 3.274-19.637 0-7.609-1.064-14.155-3.274-19.8-2.127-5.646-5.318-10.064-9.491-13.255-4.174-3.11-9.329-4.746-15.384-4.746s-11.537 1.636-15.792 4.91c-4.173 3.272-7.365 7.772-9.492 13.418-2.128 5.727-3.191 12.191-3.191 19.392 0 7.2 1.063 13.745 3.273 19.228 2.127 5.482 5.318 9.736 9.573 12.764 4.174 3.027 9.41 4.582 15.629 4.582Zm141.56-26.51V71.839h28.23v119.786h-27.412v-21.273h-1.227c-2.7 6.709-7.119 12.191-13.338 16.446-6.137 4.255-13.747 6.382-22.748 6.382-7.855 0-14.81-1.718-20.783-5.237-5.974-3.518-10.72-8.591-14.075-15.382-3.355-6.709-5.073-14.891-5.073-24.464V71.839h28.312v71.921c0 7.609 2.046 13.664 6.219 18.083 4.173 4.5 9.655 6.709 16.365 6.709 4.173 0 8.183-.982 12.111-3.028 3.927-2.045 7.118-5.072 9.655-9.082 2.537-4.091 3.764-9.164 3.764-15.218Zm65.707-109.395v159.796h-28.23V31.828h28.23Zm44.841 162.169c-7.61 0-14.402-1.391-20.457-4.091-6.055-2.7-10.883-6.791-14.32-12.109-3.518-5.319-5.237-11.946-5.237-19.801 0-6.791 1.228-12.355 3.765-16.773 2.536-4.419 5.891-7.937 10.228-10.637 4.337-2.618 9.164-4.664 14.647-6.055 5.4-1.391 11.046-2.373 16.856-3.027 7.037-.737 12.683-1.391 17.102-1.964 4.337-.573 7.528-1.555 9.574-2.782 1.963-1.309 3.027-3.273 3.027-5.973v-.491c0-5.891-1.718-10.391-5.237-13.664-3.518-3.191-8.51-4.828-15.056-4.828-6.955 0-12.356 1.473-16.447 4.5-4.009 3.028-6.71 6.546-8.183 10.719l-26.348-3.764c2.046-7.282 5.483-13.336 10.31-18.328 4.746-4.909 10.638-8.59 17.511-11.045 6.955-2.455 14.565-3.682 22.912-3.682 5.809 0 11.537.654 17.265 2.045s10.965 3.6 15.711 6.71c4.746 3.109 8.51 7.282 11.455 12.6 2.864 5.318 4.337 11.946 4.337 19.883v80.184h-27.166v-16.446h-.9c-1.719 3.355-4.092 6.464-7.201 9.328-3.109 2.864-6.955 5.237-11.619 6.955-4.828 1.718-10.229 2.536-16.529 2.536Zm7.364-20.701c5.646 0 10.556-1.145 14.729-3.354 4.173-2.291 7.364-5.237 9.655-9.001 2.292-3.763 3.355-7.854 3.355-12.273v-14.155c-.9.737-2.373 1.391-4.5 2.046-2.128.654-4.419 1.145-7.037 1.636-2.619.491-5.155.9-7.692 1.227-2.537.328-4.746.655-6.628.901-4.173.572-8.019 1.472-11.292 2.781-3.355 1.31-5.973 3.11-7.855 5.401-1.964 2.291-2.864 5.318-2.864 8.918 0 5.237 1.882 9.164 5.728 11.782 3.682 2.782 8.51 4.091 14.401 4.091Zm64.643 18.328V71.839h27.412v19.965h1.227c2.21-6.955 5.974-12.274 11.292-16.038 5.319-3.763 11.456-5.645 18.329-5.645 1.555 0 3.355.082 5.237.163 1.964.164 3.601.328 4.91.573v25.938c-1.227-.41-3.109-.819-5.646-1.146a58.814 58.814 0 0 0-7.446-.49c-5.155 0-9.738 1.145-13.829 3.354-4.091 2.209-7.282 5.236-9.655 9.164-2.373 3.927-3.519 8.427-3.519 13.5v70.448h-28.312ZM222.077 39.192l-8.019 125.923L137.387 0l84.69 39.192Zm-53.105 162.825-57.933 33.056-57.934-33.056 11.783-28.556h92.301l11.783 28.556ZM111.039 62.675l30.357 73.803H80.681l30.358-73.803ZM7.937 165.115 0 39.192 84.69 0 7.937 165.115Z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
fill="url(#c)"
|
|
||||||
d="M388.676 191.625h30.849L363.31 31.828h-35.758l-56.215 159.797h30.848l13.174-39.356h60.061l13.256 39.356Zm-65.461-62.675 21.602-64.311h1.227l21.602 64.311h-44.431Zm126.831-7.527v70.202h-28.23V71.839h27.002v20.374h1.392c2.782-6.71 7.2-12.028 13.255-15.956 6.056-3.927 13.584-5.89 22.503-5.89 8.264 0 15.465 1.8 21.684 5.318 6.137 3.518 10.964 8.673 14.319 15.382 3.437 6.71 5.074 14.81 4.992 24.383v76.175h-28.23v-71.92c0-8.019-2.046-14.237-6.219-18.819-4.173-4.5-9.819-6.791-17.102-6.791-4.91 0-9.328 1.063-13.174 3.272-3.846 2.128-6.792 5.237-9.001 9.328-2.046 4.009-3.191 8.918-3.191 14.728ZM589.233 239c-10.147 0-18.82-1.391-26.103-4.091-7.282-2.7-13.092-6.382-17.511-10.964-4.418-4.582-7.528-9.655-9.164-15.219l25.448-6.136c1.145 2.372 2.782 4.663 4.991 6.954 2.209 2.291 5.155 4.255 8.837 5.81 3.683 1.554 8.428 2.291 14.074 2.291 8.019 0 14.647-1.964 19.884-5.81 5.237-3.845 7.856-10.227 7.856-19.064v-22.665h-1.391c-1.473 2.946-3.601 5.892-6.383 9.001-2.782 3.109-6.464 5.645-10.965 7.691-4.582 2.046-10.228 3.109-17.101 3.109-9.165 0-17.511-2.209-25.039-6.545-7.446-4.337-13.42-10.883-17.757-19.474-4.418-8.673-6.628-19.473-6.628-32.565 0-13.091 2.21-24.301 6.628-33.383 4.419-9.082 10.311-15.955 17.839-20.7 7.528-4.746 15.874-7.037 25.039-7.037 7.037 0 12.846 1.145 17.347 3.518 4.582 2.373 8.182 5.236 10.883 8.51 2.7 3.272 4.746 6.382 6.137 9.327h1.554v-19.8h27.821v121.749c0 10.228-2.454 18.737-7.364 25.447-4.91 6.709-11.538 11.7-20.048 15.055-8.509 3.355-18.165 4.991-28.884 4.991Zm.245-71.266c5.974 0 11.047-1.473 15.302-4.337 4.173-2.945 7.446-7.118 9.573-12.519 2.21-5.482 3.274-12.027 3.274-19.637 0-7.609-1.064-14.155-3.274-19.8-2.127-5.646-5.318-10.064-9.491-13.255-4.174-3.11-9.329-4.746-15.384-4.746s-11.537 1.636-15.792 4.91c-4.173 3.272-7.365 7.772-9.492 13.418-2.128 5.727-3.191 12.191-3.191 19.392 0 7.2 1.063 13.745 3.273 19.228 2.127 5.482 5.318 9.736 9.573 12.764 4.174 3.027 9.41 4.582 15.629 4.582Zm141.56-26.51V71.839h28.23v119.786h-27.412v-21.273h-1.227c-2.7 6.709-7.119 12.191-13.338 16.446-6.137 4.255-13.747 6.382-22.748 6.382-7.855 0-14.81-1.718-20.783-5.237-5.974-3.518-10.72-8.591-14.075-15.382-3.355-6.709-5.073-14.891-5.073-24.464V71.839h28.312v71.921c0 7.609 2.046 13.664 6.219 18.083 4.173 4.5 9.655 6.709 16.365 6.709 4.173 0 8.183-.982 12.111-3.028 3.927-2.045 7.118-5.072 9.655-9.082 2.537-4.091 3.764-9.164 3.764-15.218Zm65.707-109.395v159.796h-28.23V31.828h28.23Zm44.841 162.169c-7.61 0-14.402-1.391-20.457-4.091-6.055-2.7-10.883-6.791-14.32-12.109-3.518-5.319-5.237-11.946-5.237-19.801 0-6.791 1.228-12.355 3.765-16.773 2.536-4.419 5.891-7.937 10.228-10.637 4.337-2.618 9.164-4.664 14.647-6.055 5.4-1.391 11.046-2.373 16.856-3.027 7.037-.737 12.683-1.391 17.102-1.964 4.337-.573 7.528-1.555 9.574-2.782 1.963-1.309 3.027-3.273 3.027-5.973v-.491c0-5.891-1.718-10.391-5.237-13.664-3.518-3.191-8.51-4.828-15.056-4.828-6.955 0-12.356 1.473-16.447 4.5-4.009 3.028-6.71 6.546-8.183 10.719l-26.348-3.764c2.046-7.282 5.483-13.336 10.31-18.328 4.746-4.909 10.638-8.59 17.511-11.045 6.955-2.455 14.565-3.682 22.912-3.682 5.809 0 11.537.654 17.265 2.045s10.965 3.6 15.711 6.71c4.746 3.109 8.51 7.282 11.455 12.6 2.864 5.318 4.337 11.946 4.337 19.883v80.184h-27.166v-16.446h-.9c-1.719 3.355-4.092 6.464-7.201 9.328-3.109 2.864-6.955 5.237-11.619 6.955-4.828 1.718-10.229 2.536-16.529 2.536Zm7.364-20.701c5.646 0 10.556-1.145 14.729-3.354 4.173-2.291 7.364-5.237 9.655-9.001 2.292-3.763 3.355-7.854 3.355-12.273v-14.155c-.9.737-2.373 1.391-4.5 2.046-2.128.654-4.419 1.145-7.037 1.636-2.619.491-5.155.9-7.692 1.227-2.537.328-4.746.655-6.628.901-4.173.572-8.019 1.472-11.292 2.781-3.355 1.31-5.973 3.11-7.855 5.401-1.964 2.291-2.864 5.318-2.864 8.918 0 5.237 1.882 9.164 5.728 11.782 3.682 2.782 8.51 4.091 14.401 4.091Zm64.643 18.328V71.839h27.412v19.965h1.227c2.21-6.955 5.974-12.274 11.292-16.038 5.319-3.763 11.456-5.645 18.329-5.645 1.555 0 3.355.082 5.237.163 1.964.164 3.601.328 4.91.573v25.938c-1.227-.41-3.109-.819-5.646-1.146a58.814 58.814 0 0 0-7.446-.49c-5.155 0-9.738 1.145-13.829 3.354-4.091 2.209-7.282 5.236-9.655 9.164-2.373 3.927-3.519 8.427-3.519 13.5v70.448h-28.312ZM222.077 39.192l-8.019 125.923L137.387 0l84.69 39.192Zm-53.105 162.825-57.933 33.056-57.934-33.056 11.783-28.556h92.301l11.783 28.556ZM111.039 62.675l30.357 73.803H80.681l30.358-73.803ZM7.937 165.115 0 39.192 84.69 0 7.937 165.115Z"
|
|
||||||
/>
|
|
||||||
</g>
|
|
||||||
<defs>
|
|
||||||
<radialGradient
|
|
||||||
id="c"
|
|
||||||
cx="0"
|
|
||||||
cy="0"
|
|
||||||
r="1"
|
|
||||||
gradientTransform="rotate(118.122 171.182 60.81) scale(205.794)"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
>
|
|
||||||
<stop stop-color="#FF41F8" />
|
|
||||||
<stop offset=".707" stop-color="#FF41F8" stop-opacity=".5" />
|
|
||||||
<stop offset="1" stop-color="#FF41F8" stop-opacity="0" />
|
|
||||||
</radialGradient>
|
|
||||||
<linearGradient
|
|
||||||
id="b"
|
|
||||||
x1="0"
|
|
||||||
x2="982"
|
|
||||||
y1="192"
|
|
||||||
y2="192"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
>
|
|
||||||
<stop stop-color="#F0060B" />
|
|
||||||
<stop offset="0" stop-color="#F0070C" />
|
|
||||||
<stop offset=".526" stop-color="#CC26D5" />
|
|
||||||
<stop offset="1" stop-color="#7702FF" />
|
|
||||||
</linearGradient>
|
|
||||||
<clipPath id="a"><path fill="#fff" d="M0 0h982v239H0z" /></clipPath>
|
|
||||||
</defs>
|
|
||||||
</svg>
|
|
||||||
<h1>Hello, {{ title }}</h1>
|
|
||||||
<p>Congratulations! Your app is running. 🎉</p>
|
|
||||||
</div>
|
|
||||||
<div class="divider" role="separator" aria-label="Divider"></div>
|
|
||||||
<div class="right-side">
|
|
||||||
<div class="pill-group">
|
|
||||||
@for (item of [
|
|
||||||
{ title: 'Explore the Docs', link: 'https://angular.dev' },
|
|
||||||
{ title: 'Learn with Tutorials', link: 'https://angular.dev/tutorials' },
|
|
||||||
{ title: 'CLI Docs', link: 'https://angular.dev/tools/cli' },
|
|
||||||
{ title: 'Angular Language Service', link: 'https://angular.dev/tools/language-service' },
|
|
||||||
{ title: 'Angular DevTools', link: 'https://angular.dev/tools/devtools' },
|
|
||||||
]; track item.title) {
|
|
||||||
<a
|
|
||||||
class="pill"
|
|
||||||
[href]="item.link"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener"
|
|
||||||
>
|
|
||||||
<span>{{ item.title }}</span>
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
height="14"
|
|
||||||
viewBox="0 -960 960 960"
|
|
||||||
width="14"
|
|
||||||
fill="currentColor"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h280v80H200v560h560v-280h80v280q0 33-23.5 56.5T760-120H200Zm188-212-56-56 372-372H560v-80h280v280h-80v-144L388-332Z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
<div class="social-links">
|
|
||||||
<a
|
|
||||||
href="https://github.com/angular/angular"
|
|
||||||
aria-label="Github"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
width="25"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 25 24"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
alt="Github"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M12.3047 0C5.50634 0 0 5.50942 0 12.3047C0 17.7423 3.52529 22.3535 8.41332 23.9787C9.02856 24.0946 9.25414 23.7142 9.25414 23.3871C9.25414 23.0949 9.24389 22.3207 9.23876 21.2953C5.81601 22.0377 5.09414 19.6444 5.09414 19.6444C4.53427 18.2243 3.72524 17.8449 3.72524 17.8449C2.61064 17.082 3.81137 17.0973 3.81137 17.0973C5.04697 17.1835 5.69604 18.3647 5.69604 18.3647C6.79321 20.2463 8.57636 19.7029 9.27978 19.3881C9.39052 18.5924 9.70736 18.0499 10.0591 17.7423C7.32641 17.4347 4.45429 16.3765 4.45429 11.6618C4.45429 10.3185 4.9311 9.22133 5.72065 8.36C5.58222 8.04931 5.16694 6.79833 5.82831 5.10337C5.82831 5.10337 6.85883 4.77319 9.2121 6.36459C10.1965 6.09082 11.2424 5.95546 12.2883 5.94931C13.3342 5.95546 14.3801 6.09082 15.3644 6.36459C17.7023 4.77319 18.7328 5.10337 18.7328 5.10337C19.3942 6.79833 18.9789 8.04931 18.8559 8.36C19.6403 9.22133 20.1171 10.3185 20.1171 11.6618C20.1171 16.3888 17.2409 17.4296 14.5031 17.7321C14.9338 18.1012 15.3337 18.8559 15.3337 20.0084C15.3337 21.6552 15.3183 22.978 15.3183 23.3779C15.3183 23.7009 15.5336 24.0854 16.1642 23.9623C21.0871 22.3484 24.6094 17.7341 24.6094 12.3047C24.6094 5.50942 19.0999 0 12.3047 0Z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
href="https://twitter.com/angular"
|
|
||||||
aria-label="Twitter"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
width="24"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
alt="Twitter"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
href="https://www.youtube.com/channel/UCbn1OgGei-DV7aSRo_HaAiw"
|
|
||||||
aria-label="Youtube"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
width="29"
|
|
||||||
height="20"
|
|
||||||
viewBox="0 0 29 20"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
alt="Youtube"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fill-rule="evenodd"
|
|
||||||
clip-rule="evenodd"
|
|
||||||
d="M27.4896 1.52422C27.9301 1.96749 28.2463 2.51866 28.4068 3.12258C29.0004 5.35161 29.0004 10 29.0004 10C29.0004 10 29.0004 14.6484 28.4068 16.8774C28.2463 17.4813 27.9301 18.0325 27.4896 18.4758C27.0492 18.9191 26.5 19.2389 25.8972 19.4032C23.6778 20 14.8068 20 14.8068 20C14.8068 20 5.93586 20 3.71651 19.4032C3.11363 19.2389 2.56449 18.9191 2.12405 18.4758C1.68361 18.0325 1.36732 17.4813 1.20683 16.8774C0.613281 14.6484 0.613281 10 0.613281 10C0.613281 10 0.613281 5.35161 1.20683 3.12258C1.36732 2.51866 1.68361 1.96749 2.12405 1.52422C2.56449 1.08095 3.11363 0.76113 3.71651 0.596774C5.93586 0 14.8068 0 14.8068 0C14.8068 0 23.6778 0 25.8972 0.596774C26.5 0.76113 27.0492 1.08095 27.4896 1.52422ZM19.3229 10L11.9036 5.77905V14.221L19.3229 10Z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
|
|
||||||
<!-- * * * * * * * * * * * The content above * * * * * * * * * * * * -->
|
|
||||||
<!-- * * * * * * * * * * is only a placeholder * * * * * * * * * * * -->
|
|
||||||
<!-- * * * * * * * * * * and can be replaced. * * * * * * * * * * * -->
|
|
||||||
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
|
|
||||||
<!-- * * * * * * * * * * End of Placeholder * * * * * * * * * * * * -->
|
|
||||||
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
|
|
||||||
|
|
||||||
|
|
||||||
<router-outlet />
|
|
||||||
@ -1,13 +1,14 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { RouterOutlet } from '@angular/router';
|
import { RouterOutlet } from '@angular/router';
|
||||||
|
import {NavMenuComponent} from './nav-menu/nav-menu.component'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [RouterOutlet],
|
imports: [RouterOutlet, NavMenuComponent],
|
||||||
templateUrl: './app.component.html',
|
templateUrl: './app.component.html',
|
||||||
styleUrl: './app.component.scss'
|
styleUrl: './app.component.scss'
|
||||||
})
|
})
|
||||||
export class AppComponent {
|
export class AppComponent {
|
||||||
title = 'user_manager_ui';
|
title = 'app';
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,17 @@
|
|||||||
import { Routes } from '@angular/router';
|
import { Routes } from '@angular/router';
|
||||||
|
import { HomeComponent } from './home/home.component';
|
||||||
|
import { AuthGuard } from './auth/auth.guard';
|
||||||
|
import { UserComponent } from './components/user/user.component';
|
||||||
|
import { GroupComponent } from './components/group/group.component';
|
||||||
|
import { ModuleComponent } from './components/module/module.component';
|
||||||
|
import { UserAssignmentComponent } from './components/user-assignment/user-assignment.component';
|
||||||
|
import { UserRepresentationComponent } from './components/user-representation/user-representation.component';
|
||||||
|
|
||||||
export const routes: Routes = [];
|
export const routes: Routes = [
|
||||||
|
{ path: '', component: HomeComponent },
|
||||||
|
{ path: 'user-table', component: UserComponent, canActivate: [AuthGuard] },
|
||||||
|
{ path: 'group-table', component: GroupComponent, canActivate: [AuthGuard] },
|
||||||
|
{ path: 'module-table', component: ModuleComponent, canActivate: [AuthGuard] },
|
||||||
|
{ path: 'user-assignment', component: UserAssignmentComponent, canActivate: [AuthGuard] },
|
||||||
|
{ path: 'user-representation', component: UserRepresentationComponent, canActivate: [AuthGuard] }
|
||||||
|
];
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
import { CanActivateFn } from '@angular/router';
|
||||||
|
|
||||||
|
import { authGuard } from './auth.guard';
|
||||||
|
|
||||||
|
describe('authGuard', () => {
|
||||||
|
const executeGuard: CanActivateFn = (...guardParameters) =>
|
||||||
|
TestBed.runInInjectionContext(() => authGuard(...guardParameters));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
expect(executeGuard).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,49 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router';
|
||||||
|
import { Observable, of } from 'rxjs';
|
||||||
|
import { AuthenticationService } from '../services/authentication.service'; // Adjust the path as necessary
|
||||||
|
import { MatDialog, MatDialogRef } from '@angular/material/dialog';
|
||||||
|
import { LoginComponent } from '../login/login.component';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class AuthGuard implements CanActivate {
|
||||||
|
constructor(
|
||||||
|
public dialog: MatDialog,
|
||||||
|
public authService: AuthenticationService,
|
||||||
|
private router: Router
|
||||||
|
) {}
|
||||||
|
|
||||||
|
canActivate(
|
||||||
|
next: ActivatedRouteSnapshot,
|
||||||
|
state: RouterStateSnapshot
|
||||||
|
): Observable<boolean> | Promise<boolean> | boolean {
|
||||||
|
return new Observable(observer => {
|
||||||
|
this.authService.isAuthenticated().subscribe({
|
||||||
|
next: (res) => {
|
||||||
|
if(!res)
|
||||||
|
this.openLogin();
|
||||||
|
|
||||||
|
observer.next(res)
|
||||||
|
},
|
||||||
|
error: (error) => {
|
||||||
|
observer.next(false)
|
||||||
|
},
|
||||||
|
complete: () => observer.complete()
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
openLogin(): MatDialogRef<LoginComponent, any> {
|
||||||
|
const dialogRef = this.dialog.open(LoginComponent, {
|
||||||
|
width: "35vw",
|
||||||
|
data: {
|
||||||
|
afterLogin: () => {
|
||||||
|
dialogRef.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return dialogRef;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
<div class="card">
|
||||||
|
<div *ngIf="!hideTitle" class="card-header">
|
||||||
|
<span *ngIf="title">{{ title }}</span>
|
||||||
|
<ng-content select=".header-content"></ng-content>
|
||||||
|
</div>
|
||||||
|
<div [ngClass]="'card-body p-' + padding">
|
||||||
|
<ng-content select=".body-content"></ng-content>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { CardComponent } from './card.component';
|
||||||
|
|
||||||
|
describe('CardComponent', () => {
|
||||||
|
let component: CardComponent;
|
||||||
|
let fixture: ComponentFixture<CardComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [CardComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(CardComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
import { Component, Input } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'card',
|
||||||
|
standalone: true,
|
||||||
|
imports: [CommonModule],
|
||||||
|
templateUrl: './card.component.html',
|
||||||
|
styleUrls: ['./card.component.css']
|
||||||
|
})
|
||||||
|
export class CardComponent {
|
||||||
|
@Input() title: string | null = null;
|
||||||
|
@Input() col: number | null = null;
|
||||||
|
@Input() padding: number = 0;
|
||||||
|
@Input() hideTitle: boolean = false;
|
||||||
|
}
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
.bi {
|
||||||
|
vertical-align: -.125em;
|
||||||
|
fill: currentColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hide {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
@ -0,0 +1,72 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="d-none">
|
||||||
|
<symbol id="check2" viewBox="0 0 16 16">
|
||||||
|
<path
|
||||||
|
d="M13.854 3.646a.5.5 0 0 1 0 .708l-7 7a.5.5 0 0 1-.708 0l-3.5-3.5a.5.5 0 1 1 .708-.708L6.5 10.293l6.646-6.647a.5.5 0 0 1 .708 0z" />
|
||||||
|
</symbol>
|
||||||
|
<symbol id="circle-half" viewBox="0 0 16 16">
|
||||||
|
<path d="M8 15A7 7 0 1 0 8 1v14zm0 1A8 8 0 1 1 8 0a8 8 0 0 1 0 16z" />
|
||||||
|
</symbol>
|
||||||
|
<symbol id="moon-stars-fill" viewBox="0 0 16 16">
|
||||||
|
<path
|
||||||
|
d="M6 .278a.768.768 0 0 1 .08.858 7.208 7.208 0 0 0-.878 3.46c0 4.021 3.278 7.277 7.318 7.277.527 0 1.04-.055 1.533-.16a.787.787 0 0 1 .81.316.733.733 0 0 1-.031.893A8.349 8.349 0 0 1 8.344 16C3.734 16 0 12.286 0 7.71 0 4.266 2.114 1.312 5.124.06A.752.752 0 0 1 6 .278z" />
|
||||||
|
<path
|
||||||
|
d="M10.794 3.148a.217.217 0 0 1 .412 0l.387 1.162c.173.518.579.924 1.097 1.097l1.162.387a.217.217 0 0 1 0 .412l-1.162.387a1.734 1.734 0 0 0-1.097 1.097l-.387 1.162a.217.217 0 0 1-.412 0l-.387-1.162A1.734 1.734 0 0 0 9.31 6.593l-1.162-.387a.217.217 0 0 1 0-.412l1.162-.387a1.734 1.734 0 0 0 1.097-1.097l.387-1.162zM13.863.099a.145.145 0 0 1 .274 0l.258.774c.115.346.386.617.732.732l.774.258a.145.145 0 0 1 0 .274l-.774.258a1.156 1.156 0 0 0-.732.732l-.258.774a.145.145 0 0 1-.274 0l-.258-.774a1.156 1.156 0 0 0-.732-.732l-.774-.258a.145.145 0 0 1 0-.274l.774-.258c.346-.115.617-.386.732-.732L13.863.1z" />
|
||||||
|
</symbol>
|
||||||
|
<symbol id="sun-fill" viewBox="0 0 16 16">
|
||||||
|
<path
|
||||||
|
d="M8 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM8 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 0zm0 13a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 13zm8-5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2a.5.5 0 0 1 .5.5zM3 8a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2A.5.5 0 0 1 3 8zm10.657-5.657a.5.5 0 0 1 0 .707l-1.414 1.415a.5.5 0 1 1-.707-.708l1.414-1.414a.5.5 0 0 1 .707 0zm-9.193 9.193a.5.5 0 0 1 0 .707L3.05 13.657a.5.5 0 0 1-.707-.707l1.414-1.414a.5.5 0 0 1 .707 0zm9.193 2.121a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .707zM4.464 4.465a.5.5 0 0 1-.707 0L2.343 3.05a.5.5 0 1 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .708z" />
|
||||||
|
</symbol>
|
||||||
|
</svg>
|
||||||
|
<div class="dropdown bd-mode-toggle">
|
||||||
|
<button class="btn py-2 dropdown-toggle d-flex align-items-center" id="bd-theme" type="button" aria-expanded="false"
|
||||||
|
data-bs-toggle="dropdown" aria-label="Toggle theme (auto)">
|
||||||
|
<svg class="bi my-1 theme-icon-active" width="1em" height="1em" viewBox="0 0 16 16">
|
||||||
|
<use href="#circle-half" [class.hide]="theme !== Themes.Auto"></use>
|
||||||
|
<use href="#sun-fill" [class.hide]="theme !== Themes.Light"></use>
|
||||||
|
<use href="#moon-stars-fill" [class.hide]="theme !== Themes.Dark
|
||||||
|
"></use>
|
||||||
|
</svg>
|
||||||
|
<span class="visually-hidden" id="bd-theme-text">Toggle theme</span>
|
||||||
|
</button>
|
||||||
|
<ul class="dropdown-menu dropdown-menu-end shadow" aria-labelledby="bd-theme-text">
|
||||||
|
<li>
|
||||||
|
<button type="button" (click)="onClick(Themes.Light)"
|
||||||
|
[ngClass]="{'active': theme == Themes.Light, 'dropdown-item d-flex align-items-center': true}"
|
||||||
|
data-bs-theme-value="light" aria-pressed="false">
|
||||||
|
<svg class="bi me-2 opacity-50" width="1em" height="1em">
|
||||||
|
<use href="#sun-fill"></use>
|
||||||
|
</svg>
|
||||||
|
Light
|
||||||
|
<svg class="bi ms-auto d-none" width="1em" height="1em">
|
||||||
|
<use href="#check2"></use>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<button type="button" (click)="onClick(Themes.Dark)"
|
||||||
|
[ngClass]="{'active': theme == Themes.Dark, 'dropdown-item d-flex align-items-center': true}"
|
||||||
|
data-bs-theme-value="dark" aria-pressed="false">
|
||||||
|
<svg class="bi me-2 opacity-50" width="1em" height="1em">
|
||||||
|
<use href="#moon-stars-fill"></use>
|
||||||
|
</svg>
|
||||||
|
Dark
|
||||||
|
<svg class="bi ms-auto d-none" width="1em" height="1em">
|
||||||
|
<use href="#check2"></use>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<button type="button" (click)="onClick(Themes.Auto)"
|
||||||
|
[ngClass]="{'active': theme == Themes.Auto, 'dropdown-item d-flex align-items-center': true}"
|
||||||
|
data-bs-theme-value="auto" aria-pressed="true">
|
||||||
|
<svg class="bi me-2 opacity-50" width="1em" height="1em">
|
||||||
|
<use href="#circle-half"></use>
|
||||||
|
</svg>
|
||||||
|
Auto
|
||||||
|
<svg class="bi ms-auto d-none" width="1em" height="1em">
|
||||||
|
<use href="#check2"></use>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
After Width: | Height: | Size: 4.3 KiB |
@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ColorModeBttnComponent } from './color-mode-bttn.component';
|
||||||
|
|
||||||
|
describe('ColorModeBttnComponent', () => {
|
||||||
|
let component: ColorModeBttnComponent;
|
||||||
|
let fixture: ComponentFixture<ColorModeBttnComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [ColorModeBttnComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(ColorModeBttnComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { ColorModeService, GetLocalTheme, Theme } from 'src/app/services/color-mode.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-color-mode-bttn',
|
||||||
|
templateUrl: './color-mode-bttn.component.html',
|
||||||
|
styleUrl: './color-mode-bttn.component.css'
|
||||||
|
})
|
||||||
|
export class ColorModeBttnComponent implements OnInit {
|
||||||
|
constructor(private cModeService: ColorModeService) {
|
||||||
|
this.theme = GetLocalTheme();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.cModeService.updateTheme();
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly Themes = Theme;
|
||||||
|
|
||||||
|
theme : Theme;
|
||||||
|
onClick(theme: Theme) {
|
||||||
|
this.theme = theme;
|
||||||
|
let theTheme:Theme = theme;
|
||||||
|
this.cModeService.setTheme(theTheme);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,50 @@
|
|||||||
|
<table cdkDrag mat-table [dataSource]="dataSource" class="mat-elevation-z8">
|
||||||
|
|
||||||
|
<!-- Checkbox Column -->
|
||||||
|
<ng-container matColumnDef="select">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>
|
||||||
|
<mat-checkbox (change)="$event ? toggleAllRows() : null"
|
||||||
|
[checked]="selection.hasValue() && isAllSelected()"
|
||||||
|
[indeterminate]="selection.hasValue() && !isAllSelected()"
|
||||||
|
[aria-label]="checkboxLabel()">
|
||||||
|
</mat-checkbox>
|
||||||
|
</th>
|
||||||
|
<td mat-cell *matCellDef="let row" [draggable]="true">
|
||||||
|
<mat-checkbox cdkDrag (click)="$event.stopPropagation()"
|
||||||
|
(change)="$event ? selection.toggle(row) : null"
|
||||||
|
[checked]="selection.isSelected(row)"
|
||||||
|
[aria-label]="checkboxLabel(row)">
|
||||||
|
</mat-checkbox>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Position Column -->
|
||||||
|
<ng-container matColumnDef="position">
|
||||||
|
<th mat-header-cell *matHeaderCellDef [draggable]="true"> No. </th>
|
||||||
|
<td mat-cell *matCellDef="let element"> {{element.position}} </td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Name Column -->
|
||||||
|
<ng-container matColumnDef="name">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Name </th>
|
||||||
|
<td mat-cell *matCellDef="let element"> {{element.name}} </td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Weight Column -->
|
||||||
|
<ng-container matColumnDef="weight">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Weight </th>
|
||||||
|
<td mat-cell *matCellDef="let element"> {{element.weight}} </td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Symbol Column -->
|
||||||
|
<ng-container matColumnDef="symbol">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Symbol </th>
|
||||||
|
<td mat-cell *matCellDef="let element"> {{element.symbol}} </td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||||
|
<tr mat-row *matRowDef="let row; columns: displayedColumns;"
|
||||||
|
(click)="selection.toggle(row)">
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { MatTableComponent } from './mat-table.component';
|
||||||
|
|
||||||
|
describe('MatTableComponent', () => {
|
||||||
|
let component: MatTableComponent;
|
||||||
|
let fixture: ComponentFixture<MatTableComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [MatTableComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(MatTableComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,66 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import {SelectionModel} from '@angular/cdk/collections';
|
||||||
|
import {MatTableDataSource, MatTableModule} from '@angular/material/table';
|
||||||
|
import {MatCheckboxModule} from '@angular/material/checkbox';
|
||||||
|
|
||||||
|
export interface PeriodicElement {
|
||||||
|
name: string;
|
||||||
|
position: number;
|
||||||
|
weight: number;
|
||||||
|
symbol: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ELEMENT_DATA: PeriodicElement[] = [
|
||||||
|
{position: 1, name: 'Hydrogen', weight: 1.0079, symbol: 'H'},
|
||||||
|
{position: 2, name: 'Helium', weight: 4.0026, symbol: 'He'},
|
||||||
|
{position: 3, name: 'Lithium', weight: 6.941, symbol: 'Li'},
|
||||||
|
{position: 4, name: 'Beryllium', weight: 9.0122, symbol: 'Be'},
|
||||||
|
{position: 5, name: 'Boron', weight: 10.811, symbol: 'B'},
|
||||||
|
{position: 6, name: 'Carbon', weight: 12.0107, symbol: 'C'},
|
||||||
|
{position: 7, name: 'Nitrogen', weight: 14.0067, symbol: 'N'},
|
||||||
|
{position: 8, name: 'Oxygen', weight: 15.9994, symbol: 'O'},
|
||||||
|
{position: 9, name: 'Fluorine', weight: 18.9984, symbol: 'F'},
|
||||||
|
{position: 10, name: 'Neon', weight: 20.1797, symbol: 'Ne'},
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @title Table with selection
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'mt-table',
|
||||||
|
standalone: true,
|
||||||
|
imports: [CommonModule, MatTableModule, MatCheckboxModule],
|
||||||
|
templateUrl: './mat-table.component.html',
|
||||||
|
styleUrl: './mat-table.component.css'
|
||||||
|
})
|
||||||
|
export class MatTableComponent {
|
||||||
|
displayedColumns: string[] = ['select', 'position', 'name', 'weight', 'symbol'];
|
||||||
|
dataSource = new MatTableDataSource<PeriodicElement>(ELEMENT_DATA);
|
||||||
|
selection = new SelectionModel<PeriodicElement>(true, []);
|
||||||
|
|
||||||
|
/** Whether the number of selected elements matches the total number of rows. */
|
||||||
|
isAllSelected() {
|
||||||
|
const numSelected = this.selection.selected.length;
|
||||||
|
const numRows = this.dataSource.data.length;
|
||||||
|
return numSelected === numRows;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Selects all rows if they are not all selected; otherwise clear selection. */
|
||||||
|
toggleAllRows() {
|
||||||
|
if (this.isAllSelected()) {
|
||||||
|
this.selection.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.selection.select(...this.dataSource.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The label for the checkbox on the passed row */
|
||||||
|
checkboxLabel(row?: PeriodicElement): string {
|
||||||
|
if (!row) {
|
||||||
|
return `${this.isAllSelected() ? 'deselect' : 'select'} all`;
|
||||||
|
}
|
||||||
|
return `${this.selection.isSelected(row) ? 'deselect' : 'select'} row ${row.position + 1}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<ul class="nav nav-tabs card-header-tabs"
|
||||||
|
[routerLinkActive]="['link-active']">
|
||||||
|
<li *ngFor="let item of tabItems" class="nav-item">
|
||||||
|
<a [ngClass]="item.title == activeTabTitle ? 'nav-link active' : 'nav-link'" aria-current="true"
|
||||||
|
[routerLink]="[item.routerLink]">{{ item.title }}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div [ngClass]="'card-body p-' + padding">
|
||||||
|
<ng-content select=".body-content"></ng-content>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { TabCardComponent } from './tab-card.component';
|
||||||
|
|
||||||
|
describe('TabCardComponent', () => {
|
||||||
|
let component: TabCardComponent;
|
||||||
|
let fixture: ComponentFixture<TabCardComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [TabCardComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(TabCardComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
import { Component, Input, input } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { CardComponent } from '../card/card.component';
|
||||||
|
import { RouterModule } from '@angular/router';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'tab-card',
|
||||||
|
standalone: true,
|
||||||
|
imports: [CommonModule, CardComponent, RouterModule],
|
||||||
|
templateUrl: './tab-card.component.html',
|
||||||
|
styleUrl: './tab-card.component.css'
|
||||||
|
})
|
||||||
|
export class TabCardComponent extends CardComponent {
|
||||||
|
@Input() tabItems: TabItem[] = []
|
||||||
|
@Input() activeTabTitle: string | null = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TabItem {
|
||||||
|
title: string
|
||||||
|
routerLink: string
|
||||||
|
}
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
<div class="row p-0 m-0">
|
||||||
|
<div class="col p-0 m-0">
|
||||||
|
<nav class="navbar bg-body-tertiary">
|
||||||
|
<form class="container-fluid justify-content-start">
|
||||||
|
<button class="btn btn-outline-success me-2" type="button"
|
||||||
|
(click)="addSelectedGroups()">Gruppen<br>Hinzufügen</button>
|
||||||
|
</form>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row align-items-start p-0 m-0">
|
||||||
|
<div class="col p-0 m-0">
|
||||||
|
<app-dir-group-table #dirGroups [rowSelection]="dirGroupsRowSelection"></app-dir-group-table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { GroupDirImportComponent } from './group-dir-import.component';
|
||||||
|
|
||||||
|
describe('GroupDirImportComponent', () => {
|
||||||
|
let component: GroupDirImportComponent;
|
||||||
|
let fixture: ComponentFixture<GroupDirImportComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [GroupDirImportComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(GroupDirImportComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,77 @@
|
|||||||
|
import { AfterViewInit, Component, Inject, OnInit, ViewChild } from '@angular/core';
|
||||||
|
import { GuiRowSelection, GuiRowSelectionMode, GuiRowSelectionType } from '@generic-ui/ngx-grid';
|
||||||
|
import Swal from 'sweetalert2';
|
||||||
|
import { GroupService } from '../../services/group.service';
|
||||||
|
import { Observable, forkJoin, of } from 'rxjs';
|
||||||
|
import { catchError, finalize } from 'rxjs/operators';
|
||||||
|
import { DirGroupTableComponent } from '../tables/dir-group-table/dir-group-table.component';
|
||||||
|
import { DirGroup } from '../../models/user-management.api.models';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
standalone: true,
|
||||||
|
selector: 'app-group-dir-import',
|
||||||
|
templateUrl: './group-dir-import.component.html',
|
||||||
|
styleUrl: './group-dir-import.component.css'
|
||||||
|
})
|
||||||
|
export class GroupDirImportComponent implements OnInit {
|
||||||
|
|
||||||
|
initWithoutData = () => { }
|
||||||
|
|
||||||
|
constructor(private gService: GroupService) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ViewChild('dirGroups') dirGroups!: DirGroupTableComponent;
|
||||||
|
|
||||||
|
dirGroupsRowSelection: GuiRowSelection = {
|
||||||
|
enabled: true,
|
||||||
|
type: GuiRowSelectionType.CHECKBOX,
|
||||||
|
mode: GuiRowSelectionMode.MULTIPLE
|
||||||
|
}
|
||||||
|
|
||||||
|
dirUsersRowSelection: GuiRowSelection = {
|
||||||
|
enabled: true,
|
||||||
|
type: GuiRowSelectionType.CHECKBOX,
|
||||||
|
mode: GuiRowSelectionMode.MULTIPLE
|
||||||
|
}
|
||||||
|
|
||||||
|
addSelectedGroups() {
|
||||||
|
let requests = new Array<Observable<DirGroup | null>>();
|
||||||
|
let numAdded: number = 0;
|
||||||
|
for (let row of this.dirGroups.selectedRows) {
|
||||||
|
requests.push(
|
||||||
|
this.gService.createByDir({ samaccountname: row?.source?.samaccountname }).pipe(
|
||||||
|
catchError((err) => {
|
||||||
|
return of(null);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
forkJoin(requests).pipe(
|
||||||
|
// finalize is executed after all requests are completed or when an error occurs
|
||||||
|
finalize(() => {
|
||||||
|
// Show Swal notification after all requests are completed
|
||||||
|
Swal.fire({
|
||||||
|
icon: "success",
|
||||||
|
title: "Completed",
|
||||||
|
text: `${numAdded} new groups added`,
|
||||||
|
position: "center",
|
||||||
|
showConfirmButton: false,
|
||||||
|
timer: 3000
|
||||||
|
});
|
||||||
|
|
||||||
|
this.dirGroups.safelyUnselectAll();
|
||||||
|
})
|
||||||
|
).subscribe({
|
||||||
|
next: (results) => {
|
||||||
|
numAdded += results.filter(result => result !== null).length;
|
||||||
|
},
|
||||||
|
error: (err) => {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1 @@
|
|||||||
|
<app-group-table></app-group-table>
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { GroupComponent } from './group.component';
|
||||||
|
|
||||||
|
describe('GroupComponent', () => {
|
||||||
|
let component: GroupComponent;
|
||||||
|
let fixture: ComponentFixture<GroupComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [GroupComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(GroupComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
standalone: true,
|
||||||
|
selector: 'app-group',
|
||||||
|
templateUrl: './group.component.html',
|
||||||
|
styleUrl: './group.component.css'
|
||||||
|
})
|
||||||
|
export class GroupComponent {
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1 @@
|
|||||||
|
<app-module-table></app-module-table>
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ModuleComponent } from './module.component';
|
||||||
|
|
||||||
|
describe('ModuleComponent', () => {
|
||||||
|
let component: ModuleComponent;
|
||||||
|
let fixture: ComponentFixture<ModuleComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [ModuleComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(ModuleComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
standalone: true,
|
||||||
|
selector: 'app-module',
|
||||||
|
templateUrl: './module.component.html',
|
||||||
|
styleUrl: './module.component.css'
|
||||||
|
})
|
||||||
|
export class ModuleComponent {
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
<gui-grid #grid
|
||||||
|
[columns]="columns"
|
||||||
|
[columnMenu]="columnMenu"
|
||||||
|
[sorting]="sorting"
|
||||||
|
[loading]="loading"
|
||||||
|
[rowSelection] = "rowSelection"
|
||||||
|
[rowDetail]="rowDetail"
|
||||||
|
[autoResizeWidth]="autoResizeWidth"
|
||||||
|
[paging]="paging"
|
||||||
|
[searching]="searching"
|
||||||
|
[cellEditing]="cellEditing"
|
||||||
|
[virtualScroll]="true"
|
||||||
|
[infoPanel]="infoPanel"
|
||||||
|
[titlePanel]="titlePanel"
|
||||||
|
[theme]="theme"
|
||||||
|
(selectedRows)="onSelectedRows($event)">
|
||||||
|
</gui-grid>
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { BaseTableComponent } from './base-table.component';
|
||||||
|
import { ApiService } from '../../../services/user-management.api.service';
|
||||||
|
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
|
|
||||||
|
describe('BaseTableComponent', () => {
|
||||||
|
let component: BaseTableComponent<any, any>;
|
||||||
|
let fixture: ComponentFixture<BaseTableComponent<any, any>>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ BaseTableComponent ],
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: ApiService,
|
||||||
|
useValue: jasmine.createSpyObj('ApiService', ['getAll', 'update', 'delete'])
|
||||||
|
}
|
||||||
|
],
|
||||||
|
// Ignore any unknown elements and attributes
|
||||||
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(BaseTableComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add more tests here to cover other functionalities
|
||||||
|
});
|
||||||
@ -0,0 +1,158 @@
|
|||||||
|
import { AfterViewInit, Component, Inject, Input, OnDestroy, OnInit, ViewChild, input } from '@angular/core';
|
||||||
|
import { ApiService } from '../../../services/user-management.api.service';
|
||||||
|
import { GuiColumn, GuiColumnMenu, GuiSorting, GuiRowDetail, GuiPaging, GuiPagingDisplay, GuiSearching, GuiCellEdit, GuiInfoPanel, GuiTitlePanel, GuiRowSelection, GuiSelectedRow, GuiGridComponent, GuiGridApi, GuiTheme } from '@generic-ui/ngx-grid';
|
||||||
|
import { Subscription } from 'rxjs/internal/Subscription';
|
||||||
|
import { ColorModeService, Theme } from '../../../services/color-mode.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
standalone: true,
|
||||||
|
selector: 'app-base-table',
|
||||||
|
templateUrl: './base-table.component.html',
|
||||||
|
styleUrl: './base-table.component.css'
|
||||||
|
})
|
||||||
|
export class BaseTableComponent<TModel, TApiService extends ApiService<TModel>> implements OnInit, OnDestroy {
|
||||||
|
|
||||||
|
service: TApiService;
|
||||||
|
|
||||||
|
columnMenu: GuiColumnMenu = {
|
||||||
|
enabled: true,
|
||||||
|
sort: true,
|
||||||
|
columnsManager: false,
|
||||||
|
filter: false
|
||||||
|
};
|
||||||
|
sorting: GuiSorting = {
|
||||||
|
enabled: true,
|
||||||
|
multiSorting: true
|
||||||
|
};
|
||||||
|
loading: boolean = false;
|
||||||
|
autoResizeWidth: boolean = true;
|
||||||
|
rowDetail: GuiRowDetail = {
|
||||||
|
enabled: true,
|
||||||
|
template: (item: TModel) => {
|
||||||
|
return `
|
||||||
|
<div></div>`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
paging: GuiPaging = {
|
||||||
|
enabled: true,
|
||||||
|
page: 1,
|
||||||
|
pageSize: 15,
|
||||||
|
pageSizes: [5, 10, 15, 20, 25, 30, 35, 40, 45, 50],
|
||||||
|
pagerTop: true,
|
||||||
|
pagerBottom: false,
|
||||||
|
display: GuiPagingDisplay.ADVANCED
|
||||||
|
};
|
||||||
|
searching: GuiSearching = {
|
||||||
|
enabled: true,
|
||||||
|
placeholder: 'Suche'
|
||||||
|
};
|
||||||
|
maxHeight: any = 400;
|
||||||
|
infoPanel: GuiInfoPanel = {
|
||||||
|
enabled: true,
|
||||||
|
infoDialog: false,
|
||||||
|
columnsManager: true,
|
||||||
|
schemaManager: true
|
||||||
|
};
|
||||||
|
titlePanel: GuiTitlePanel = {
|
||||||
|
enabled: false,
|
||||||
|
template: () => {
|
||||||
|
return `
|
||||||
|
<div class='title-panel-example' >List of contract workers</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
theme: GuiTheme = localStorage.getItem('theme') === 'dark' ? GuiTheme.DARK : GuiTheme.FABRIC;
|
||||||
|
|
||||||
|
private themeSubscription: Subscription = new Subscription();
|
||||||
|
|
||||||
|
constructor(@Inject(ApiService<TModel>) service: TApiService, columns: Array<GuiColumn>, private cModeService: ColorModeService) {
|
||||||
|
this.service = service;
|
||||||
|
if (this.columns.length == 0)
|
||||||
|
this.columns = columns;
|
||||||
|
|
||||||
|
//assign row details
|
||||||
|
if (this.rowDetailTemplate === null || this.rowDetailTemplate === undefined)
|
||||||
|
this.rowDetail = {
|
||||||
|
enabled: false,
|
||||||
|
};
|
||||||
|
else
|
||||||
|
this.rowDetail = {
|
||||||
|
enabled: true,
|
||||||
|
template: (this.rowDetailTemplate)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Input() rowDetailTemplate: null | ((item: TModel, index: number) => string) = null;
|
||||||
|
|
||||||
|
@Input() isCellEditable = false;
|
||||||
|
|
||||||
|
@Input() cellEditing: GuiCellEdit = {
|
||||||
|
|
||||||
|
enabled: this.isCellEditable,
|
||||||
|
|
||||||
|
rowEdit: (value: any, item: any, index: number) => {
|
||||||
|
return Boolean(index % 2);
|
||||||
|
},
|
||||||
|
|
||||||
|
cellEdit: (value: any, item: any, index: number) => {
|
||||||
|
return Boolean(index % 5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Input() rowSelection: boolean | GuiRowSelection = true;
|
||||||
|
|
||||||
|
@Input() onSelectedRows: (rows: Array<GuiSelectedRow>) => void = (rows) => { };
|
||||||
|
|
||||||
|
@Input() initData: () => void = this.fetchData;
|
||||||
|
|
||||||
|
@Input() columns: Array<GuiColumn> = [];
|
||||||
|
|
||||||
|
selected: boolean = false;
|
||||||
|
safelyUnselectAll() {
|
||||||
|
this.selected = true
|
||||||
|
if (this.api?.getSelectedRows() != undefined)
|
||||||
|
if ((this.api?.getSelectedRows().length ?? 0 > 0) && this.selected) {
|
||||||
|
this.api?.unselectAll()
|
||||||
|
this.selected = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewChild('grid', { static: true }) mainGrid!: GuiGridComponent;
|
||||||
|
|
||||||
|
private get api(): GuiGridApi {
|
||||||
|
return this.mainGrid.api;
|
||||||
|
}
|
||||||
|
|
||||||
|
set source(data: TModel[]) {
|
||||||
|
this.api.setSource(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
get selectedRows(): Array<GuiSelectedRow> {
|
||||||
|
return this.api.getSelectedRows();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
|
||||||
|
const subscription = this.cModeService.themeChanges$.subscribe((theme: Theme) => {
|
||||||
|
this.theme = theme === 'dark' ? GuiTheme.DARK : GuiTheme.FABRIC;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.themeSubscription.add(subscription);
|
||||||
|
this.initData()
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.themeSubscription.unsubscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchData(): void {
|
||||||
|
this.service.getAll().subscribe({
|
||||||
|
next: (response) => {
|
||||||
|
this.source = response;
|
||||||
|
this.loading = false;
|
||||||
|
},
|
||||||
|
error: (error) => { }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { DirGroupTableComponent } from './dir-group-table.component';
|
||||||
|
|
||||||
|
describe('AdGroupUserTableComponent', () => {
|
||||||
|
let component: DirGroupTableComponent;
|
||||||
|
let fixture: ComponentFixture<DirGroupTableComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [DirGroupTableComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(DirGroupTableComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
import { Component, Inject } from '@angular/core';
|
||||||
|
import { BaseTableComponent } from '../base-table/base-table.component';
|
||||||
|
import { DirGroupService } from '../../../services/dir-group.service';
|
||||||
|
import { DirGroup } from '../../../models/user-management.api.models';
|
||||||
|
import { GuiColumn } from '@generic-ui/ngx-grid';
|
||||||
|
import { ColorModeService } from '../../../services/color-mode.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
standalone: true,
|
||||||
|
selector: 'app-dir-group-table',
|
||||||
|
templateUrl: '../base-table/base-table.component.html',
|
||||||
|
styleUrl: './dir-group-table.component.css'
|
||||||
|
})
|
||||||
|
export class DirGroupTableComponent extends BaseTableComponent<DirGroup, DirGroupService> {
|
||||||
|
constructor(service: DirGroupService, @Inject('DIR_GROUP_TABLE_COLUMNS') columns: Array<GuiColumn>, cModeService: ColorModeService) {
|
||||||
|
super(service, columns, cModeService)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { DirUserTableComponent } from './dir-user-table.component';
|
||||||
|
|
||||||
|
describe('DirUserTableComponent', () => {
|
||||||
|
let component: DirUserTableComponent;
|
||||||
|
let fixture: ComponentFixture<DirUserTableComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [DirUserTableComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(DirUserTableComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
import { Component, Inject } from '@angular/core';
|
||||||
|
import { DirUser } from '../../../models/user-management.api.models';
|
||||||
|
import { DirUserService } from '../../../services/dir-user.service';
|
||||||
|
import { BaseTableComponent } from '../base-table/base-table.component';
|
||||||
|
import { GuiColumn } from '@generic-ui/ngx-grid/gui/grid/src/core/api/gui.grid.public-api';
|
||||||
|
import { ColorModeService } from '../../../services/color-mode.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
standalone: true,
|
||||||
|
selector: 'app-dir-user-table',
|
||||||
|
templateUrl: '../base-table/base-table.component.html',
|
||||||
|
styleUrl: './dir-user-table.component.css'
|
||||||
|
})
|
||||||
|
export class DirUserTableComponent extends BaseTableComponent<DirUser, DirUserService> {
|
||||||
|
constructor(service: DirUserService, @Inject('DIR_USER_TABLE_COLUMNS') columns: Array<GuiColumn>, cModeService: ColorModeService) {
|
||||||
|
super(service, columns, cModeService)
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchDataByGroupName(groupName: string): void {
|
||||||
|
this.service.getAll(groupName).subscribe({
|
||||||
|
next: (response: any) => {
|
||||||
|
this.source = response;
|
||||||
|
this.loading = false;
|
||||||
|
},
|
||||||
|
error: (error: any) => {}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { GroupTableComponent } from './group-table.component';
|
||||||
|
|
||||||
|
describe('GroupTableComponent', () => {
|
||||||
|
let component: GroupTableComponent;
|
||||||
|
let fixture: ComponentFixture<GroupTableComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [GroupTableComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(GroupTableComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
import { Component, Inject } from '@angular/core';
|
||||||
|
import { GroupService } from '../../../services/group.service';
|
||||||
|
import { Group } from '../../../models/user-management.api.models';
|
||||||
|
import { GuiColumn } from '@generic-ui/ngx-grid';
|
||||||
|
import { BaseTableComponent } from '../base-table/base-table.component';
|
||||||
|
import { ColorModeService } from '../../../services/color-mode.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
standalone: true,
|
||||||
|
selector: 'app-group-table',
|
||||||
|
templateUrl: '../base-table/base-table.component.html',
|
||||||
|
styleUrl: './group-table.component.css'
|
||||||
|
})
|
||||||
|
export class GroupTableComponent extends BaseTableComponent<Group, GroupService> {
|
||||||
|
constructor(
|
||||||
|
service: GroupService, @Inject('GROUP_COMPLETE_TABLE_COLUMNS') columns: Array<GuiColumn>, cModeService: ColorModeService) {
|
||||||
|
super(service, columns, cModeService)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { GroupUserTableComponent } from './group-user-table.component';
|
||||||
|
|
||||||
|
describe('GroupUserTableComponent', () => {
|
||||||
|
let component: GroupUserTableComponent;
|
||||||
|
let fixture: ComponentFixture<GroupUserTableComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [GroupUserTableComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(GroupUserTableComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
import { Component, Inject } from '@angular/core';
|
||||||
|
import { GroupOfUserService } from '../../../services/group-of-user.service';
|
||||||
|
import { GroupOfUser } from '../../../models/user-management.api.models';
|
||||||
|
import { GuiColumn } from '@generic-ui/ngx-grid';
|
||||||
|
import { BaseTableComponent } from '../base-table/base-table.component';
|
||||||
|
import { ColorModeService } from '../../../services/color-mode.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
standalone: true,
|
||||||
|
selector: 'app-group-user-table',
|
||||||
|
templateUrl: '../base-table/base-table.component.html',
|
||||||
|
styleUrl: './group-user-table.component.css'
|
||||||
|
})
|
||||||
|
export class GroupUserTableComponent extends BaseTableComponent<GroupOfUser, GroupOfUserService> {
|
||||||
|
constructor(service: GroupOfUserService, @Inject('GROUP_USER_TABLE_COLUMNS') columns: Array<GuiColumn>, cModeService: ColorModeService) {
|
||||||
|
super(service, columns, cModeService)
|
||||||
|
this.initData = () => this.fetchDataWith(true,true);
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchDataWith(withUser: boolean, withGroup: boolean){
|
||||||
|
this.service.getAll(withUser, withGroup).subscribe ({
|
||||||
|
next: (response) => {
|
||||||
|
this.source = response;
|
||||||
|
},
|
||||||
|
error: (error) => {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ModuleTableComponent } from './module-table.component';
|
||||||
|
|
||||||
|
describe('ModuleTableComponent', () => {
|
||||||
|
let component: ModuleTableComponent;
|
||||||
|
let fixture: ComponentFixture<ModuleTableComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [ModuleTableComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(ModuleTableComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
import { Component, Inject } from '@angular/core';
|
||||||
|
import { Module } from '../../../models/user-management.api.models';
|
||||||
|
import { GuiColumn } from '@generic-ui/ngx-grid';
|
||||||
|
import { BaseTableComponent } from '../base-table/base-table.component';
|
||||||
|
import { ModuleService } from '../../../services/module.service'
|
||||||
|
import { ColorModeService } from '../../../services/color-mode.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
standalone: true,
|
||||||
|
selector: 'app-module-table',
|
||||||
|
templateUrl: '../base-table/base-table.component.html',
|
||||||
|
styleUrl: './module-table.component.css'
|
||||||
|
})
|
||||||
|
export class ModuleTableComponent extends BaseTableComponent<Module, ModuleService> {
|
||||||
|
constructor(
|
||||||
|
service: ModuleService, @Inject('MODULE_TABLE_COLUMNS') columns: Array<GuiColumn>, cModeService: ColorModeService) {
|
||||||
|
super(service, columns, cModeService)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { UserRepTableComponent } from './user-rep-table.component';
|
||||||
|
|
||||||
|
describe('UserRepTableComponent', () => {
|
||||||
|
let component: UserRepTableComponent;
|
||||||
|
let fixture: ComponentFixture<UserRepTableComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [UserRepTableComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(UserRepTableComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
import { Component, Inject } from '@angular/core';
|
||||||
|
import { UserRep } from '../../../models/user-management.api.models';
|
||||||
|
import { UserRepService } from '../../../services/user-representation.service';
|
||||||
|
import { BaseTableComponent } from '../base-table/base-table.component';
|
||||||
|
import { GuiColumn } from '@generic-ui/ngx-grid';
|
||||||
|
import { ColorModeService } from '../../../services/color-mode.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
standalone: true,
|
||||||
|
selector: 'app-user-rep-table',
|
||||||
|
templateUrl: '../base-table/base-table.component.html',
|
||||||
|
styleUrl: './user-rep-table.component.css'
|
||||||
|
})
|
||||||
|
export class UserRepTableComponent extends BaseTableComponent<UserRep, UserRepService> {
|
||||||
|
|
||||||
|
constructor(service: UserRepService, @Inject('USER_REP_TABLE_COLUMNS') columns: Array<GuiColumn>, cModeService: ColorModeService) {
|
||||||
|
super(service, columns, cModeService)
|
||||||
|
this.loading = false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fetchData(userId?: number): void {
|
||||||
|
this.service.getAll(false, true, true, true, userId).subscribe({
|
||||||
|
next: (response: UserRep[]) => {
|
||||||
|
this.source = response;
|
||||||
|
this.loading = false;
|
||||||
|
},
|
||||||
|
error: (error: any) => {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { UserTableComponent } from './user-table.component';
|
||||||
|
|
||||||
|
describe('UserTableComponent', () => {
|
||||||
|
let component: UserTableComponent;
|
||||||
|
let fixture: ComponentFixture<UserTableComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [UserTableComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(UserTableComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,86 @@
|
|||||||
|
import { Component, Inject, Input } from '@angular/core';
|
||||||
|
import { UserService } from '../../../services/user.service';
|
||||||
|
import { ModuleOfUserService } from '../../../services/module-of-user.service';
|
||||||
|
import { GroupOfUserService } from '../../../services/group-of-user.service';
|
||||||
|
import { User } from '../../../models/user-management.api.models';
|
||||||
|
import { GuiColumn } from '@generic-ui/ngx-grid';
|
||||||
|
import { BaseTableComponent } from '../base-table/base-table.component';
|
||||||
|
import { ColorModeService } from '../../../services/color-mode.service'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
standalone: true,
|
||||||
|
selector: 'app-user-table',
|
||||||
|
templateUrl: '../base-table/base-table.component.html',
|
||||||
|
styleUrl: './user-table.component.css'
|
||||||
|
})
|
||||||
|
export class UserTableComponent extends BaseTableComponent<User, UserService> {
|
||||||
|
|
||||||
|
mosService: ModuleOfUserService;
|
||||||
|
gosService: GroupOfUserService;
|
||||||
|
|
||||||
|
constructor(mosService: ModuleOfUserService,
|
||||||
|
gosService: GroupOfUserService,
|
||||||
|
service: UserService,
|
||||||
|
@Inject('USER_TABLE_COLUMNS') columns: Array<GuiColumn>,
|
||||||
|
cModeService: ColorModeService) {
|
||||||
|
super(service, columns, cModeService)
|
||||||
|
this.mosService = mosService;
|
||||||
|
this.gosService = gosService;
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchDataByModuleId(moduleId: number, assigned: boolean = true): void {
|
||||||
|
this.service.getByModuleId(moduleId, assigned).subscribe({
|
||||||
|
next: (users) => {
|
||||||
|
this.source = users;
|
||||||
|
},
|
||||||
|
error: (error) => {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchDataByGroupId(groupId: number, assigned: boolean = true): void {
|
||||||
|
this.service.getByGroupId(groupId, assigned).subscribe({
|
||||||
|
next: (users) => {
|
||||||
|
this.source = users;
|
||||||
|
},
|
||||||
|
error: (error) => {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async createModuleOfUsers(moduleId: number, users: User[]): Promise<any[]> {
|
||||||
|
const creationPromises = users
|
||||||
|
.filter(user => user.id && user.id != null)
|
||||||
|
.map(user => this.mosService.create({ moduleId: moduleId, userId: user.id ?? -1, addedWho: "DEFAULT" }).toPromise());
|
||||||
|
|
||||||
|
return Promise.all(creationPromises);
|
||||||
|
}
|
||||||
|
|
||||||
|
async createGroupOfUsers(groupId: number, users: User[]): Promise<any[]> {
|
||||||
|
const creationPromises = users
|
||||||
|
.filter(user => user.id && user.id != null)
|
||||||
|
.map(user => this.gosService.create({ groupId: groupId, userId: user.id ?? -1, addedWho: "DEFAULT" }).toPromise());
|
||||||
|
|
||||||
|
return Promise.all(creationPromises);
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteModuleOfUsers(moduleId: number, users: User[]): Promise<void> {
|
||||||
|
const deletionPromises = users
|
||||||
|
.filter(user => user.id)
|
||||||
|
.map(user => this.mosService.deleteByModuleGroupId(moduleId, user.id ?? -1).toPromise());
|
||||||
|
|
||||||
|
try {
|
||||||
|
const responses = await Promise.all(deletionPromises);
|
||||||
|
} catch (error) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteGroupOfUsers(groupId: number, users: User[]): Promise<void> {
|
||||||
|
const deletionPromises = users
|
||||||
|
.filter(user => user.id)
|
||||||
|
.map(user => this.gosService.deleteByGroupUserId(groupId, user.id ?? -1).toPromise());
|
||||||
|
|
||||||
|
try {
|
||||||
|
const responses = await Promise.all(deletionPromises);
|
||||||
|
} catch (error) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
/* Tab label'ları için daha spesifik */
|
||||||
|
.mat-tab-group.mat-tab-group .mat-tab-labels .mat-tab-label {
|
||||||
|
color: white !important; /* font rengi */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Seçili tab'ın altındaki indicator için daha spesifik */
|
||||||
|
.mat-tab-group.mat-tab-group .mat-tab-labels .mat-tab-label.mat-tab-label-active .mat-tab-indicator {
|
||||||
|
background-color: white !important; /* indicator rengi */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tab'ların arka planı için daha spesifik */
|
||||||
|
.mat-tab-group.mat-tab-group .mat-tab-body .mat-tab-body-content {
|
||||||
|
background-color: #673ab7 !important; /* arka plan rengi */
|
||||||
|
}
|
||||||
@ -0,0 +1,39 @@
|
|||||||
|
<div class="container-fluid text-center">
|
||||||
|
<!-- First row: Assignment -->
|
||||||
|
<div class="row m-0 p-0">
|
||||||
|
<!-- (1, 1): modules -->
|
||||||
|
<div class="col-2">
|
||||||
|
<mat-tab-group>
|
||||||
|
<mat-tab label="Modul">
|
||||||
|
<app-module-table #modules class="table-content" [onSelectedRows]="modulesOnSelectedRows"></app-module-table>
|
||||||
|
</mat-tab>
|
||||||
|
<mat-tab label="Gruppen">
|
||||||
|
<app-group-table #groups class="table-content" [onSelectedRows]="groupsOnSelectedRows"></app-group-table>
|
||||||
|
</mat-tab>
|
||||||
|
</mat-tab-group>
|
||||||
|
</div>
|
||||||
|
<!-- (1, 2): unassigned users -->
|
||||||
|
<div class="col-5 mt-0 pt-0">
|
||||||
|
<mat-tab-group>
|
||||||
|
<mat-tab label="Nicht zugeordnete benutzer">
|
||||||
|
<app-user-table #unassignedUsers class="body-content" [onSelectedRows]="unassignedUsersOnSelectedRows"
|
||||||
|
[rowSelection]="userRowSelection" (drop)="dropToUnassigned($event)"
|
||||||
|
(dragover)="allowDropOnUnassigned($event)" (dragstart)="dragUnassigned($event)"
|
||||||
|
[draggable]="true" [initData]="initWithoutData"></app-user-table>
|
||||||
|
<!-- <mt-table></mt-table> -->
|
||||||
|
</mat-tab>
|
||||||
|
</mat-tab-group>
|
||||||
|
</div>
|
||||||
|
<!-- (1, 3): assigned users -->
|
||||||
|
<div class="col-5">
|
||||||
|
<mat-tab-group>
|
||||||
|
<mat-tab label="{{userInLabel}}">
|
||||||
|
<app-user-table #assignedUsers class="body-content" [onSelectedRows]="assignedUsersOnSelectedRows"
|
||||||
|
[rowSelection]="userRowSelection" (drop)="dropToAssigned($event)" (dragover)="allowDropOnAssigned($event)"
|
||||||
|
(dragstart)="dragAssigned($event)" [draggable]="true" [initData]="initWithoutData"></app-user-table>
|
||||||
|
</mat-tab>
|
||||||
|
</mat-tab-group>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { UserAssignmentComponent } from './user-assignment.component';
|
||||||
|
|
||||||
|
describe('UserAssignmentComponent', () => {
|
||||||
|
let component: UserAssignmentComponent;
|
||||||
|
let fixture: ComponentFixture<UserAssignmentComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [UserAssignmentComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(UserAssignmentComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,176 @@
|
|||||||
|
import { AfterViewInit, Component, Input, OnInit, ViewChild } from '@angular/core';
|
||||||
|
import { TabItem } from '../common/tab-card/tab-card.component';
|
||||||
|
import { GuiRowSelection, GuiRowSelectionMode, GuiRowSelectionType, GuiSelectedRow } from '@generic-ui/ngx-grid';
|
||||||
|
import { UserTableComponent } from '../tables/user-table/user-table.component';
|
||||||
|
import { ModuleTableComponent } from '../tables/module-table/module-table.component';
|
||||||
|
import { GroupTableComponent } from '../tables/group-table/group-table.component';
|
||||||
|
import { User } from '../../models/user-management.api.models';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
standalone: true,
|
||||||
|
standalone: true,
|
||||||
|
selector: 'app-user-assignment',
|
||||||
|
templateUrl: './user-assignment.component.html',
|
||||||
|
styleUrl: './user-assignment.component.scss'
|
||||||
|
})
|
||||||
|
export class UserAssignmentComponent implements OnInit, AfterViewInit {
|
||||||
|
|
||||||
|
initWithoutData = () => {}
|
||||||
|
|
||||||
|
userInLabel: string = "Benutzer in"
|
||||||
|
|
||||||
|
tabItems: TabItem[] = [
|
||||||
|
{
|
||||||
|
routerLink: '/module-user',
|
||||||
|
title: 'Modules'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
routerLink: '/group-user',
|
||||||
|
title: 'Groups'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
@Input() activeTabTitle: string | null = null;
|
||||||
|
@Input() activeIndex: number = 0;
|
||||||
|
@ViewChild("unassignedUsers") unassignedUsers!: UserTableComponent;
|
||||||
|
@ViewChild("assignedUsers") assignedUsers!: UserTableComponent;
|
||||||
|
@ViewChild("modules") modules!: ModuleTableComponent;
|
||||||
|
@ViewChild("groups") groups!: GroupTableComponent;
|
||||||
|
|
||||||
|
userRowSelection: GuiRowSelection = {
|
||||||
|
enabled: true,
|
||||||
|
type: GuiRowSelectionType.CHECKBOX,
|
||||||
|
mode: GuiRowSelectionMode.MULTIPLE
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {}
|
||||||
|
|
||||||
|
ngAfterViewInit(): void {
|
||||||
|
//this.unassignedUsers.loading = false;
|
||||||
|
//this.assignedUsers.loading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
createAssignDragMethod(target: string): (event: DragEvent) => void {
|
||||||
|
return (event: DragEvent) => {
|
||||||
|
this.dragging = target;
|
||||||
|
event.dataTransfer?.setData("text", target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
target: Target = Target.Module;
|
||||||
|
targetId = 0;
|
||||||
|
|
||||||
|
dragging: string = "";
|
||||||
|
|
||||||
|
dragAssigned = this.createAssignDragMethod("assigned")
|
||||||
|
dragUnassigned = this.createAssignDragMethod("unassigned")
|
||||||
|
|
||||||
|
allowDropOnAssigned(event: DragEvent) {
|
||||||
|
if (this.dragging == "unassigned")
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
allowDropOnUnassigned(event: DragEvent) {
|
||||||
|
if (this.dragging == "assigned")
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
dropToAssigned(event: DragEvent) {
|
||||||
|
if (event.dataTransfer?.getData("text") == "unassigned") {
|
||||||
|
var rows = this.unassignedUsers.selectedRows;
|
||||||
|
var users: User[] = new Array<User>();
|
||||||
|
if (!rows)
|
||||||
|
return
|
||||||
|
|
||||||
|
for (var row of rows)
|
||||||
|
users.push(row.source);
|
||||||
|
|
||||||
|
this.unselectUserTables()
|
||||||
|
switch (this.target) {
|
||||||
|
case Target.Module:
|
||||||
|
this.unassignedUsers.createModuleOfUsers(this.targetId, users)
|
||||||
|
.then(() => this.updateUserTables())
|
||||||
|
break;
|
||||||
|
case Target.Group:
|
||||||
|
this.unassignedUsers.createGroupOfUsers(this.targetId, users)
|
||||||
|
.then(() => this.updateUserTables())
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dropToUnassigned(event: DragEvent) {
|
||||||
|
if (event.dataTransfer?.getData("text") == "assigned") {
|
||||||
|
var rows = this.assignedUsers.selectedRows;
|
||||||
|
var users: User[] = new Array<User>();
|
||||||
|
if (!rows)
|
||||||
|
return
|
||||||
|
|
||||||
|
for (var row of rows)
|
||||||
|
users.push(row.source);
|
||||||
|
|
||||||
|
this.unselectUserTables()
|
||||||
|
switch (this.target) {
|
||||||
|
case Target.Module:
|
||||||
|
this.unassignedUsers.deleteModuleOfUsers(this.targetId, users)
|
||||||
|
.then(() => this.updateUserTables())
|
||||||
|
break;
|
||||||
|
case Target.Group:
|
||||||
|
this.unassignedUsers.deleteGroupOfUsers(this.targetId, users)
|
||||||
|
.then(() => this.updateUserTables())
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unselectUserTables() {
|
||||||
|
this.assignedUsers.safelyUnselectAll()
|
||||||
|
this.unassignedUsers.safelyUnselectAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
updateUserTables() {
|
||||||
|
this.unselectUserTables()
|
||||||
|
|
||||||
|
switch (this.target) {
|
||||||
|
case Target.Module:
|
||||||
|
this.assignedUsers.fetchDataByModuleId(this.targetId);
|
||||||
|
this.unassignedUsers.fetchDataByModuleId(this.targetId, false);
|
||||||
|
break;
|
||||||
|
case Target.Group:
|
||||||
|
this.assignedUsers.fetchDataByGroupId(this.targetId);
|
||||||
|
this.unassignedUsers.fetchDataByGroupId(this.targetId, false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
modulesOnSelectedRows = (rows: GuiSelectedRow[]) => {
|
||||||
|
if (rows.length > 0) {
|
||||||
|
this.modules.safelyUnselectAll();
|
||||||
|
this.userInLabel = `Benutzer in Module ${rows[0].source?.name}`
|
||||||
|
this.target = Target.Module;
|
||||||
|
this.targetId = rows[0].source.id;
|
||||||
|
this.updateUserTables();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
groupsOnSelectedRows = (rows: GuiSelectedRow[]) => {
|
||||||
|
if (rows.length > 0) {
|
||||||
|
this.groups.safelyUnselectAll();
|
||||||
|
this.userInLabel = `Benutzer in Gruppe ${rows[0].source?.name}`
|
||||||
|
this.target = Target.Group;
|
||||||
|
this.targetId = rows[0].source.id;
|
||||||
|
this.updateUserTables();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unassignedUsersOnSelectedRows = (rows: GuiSelectedRow[]) => {
|
||||||
|
}
|
||||||
|
|
||||||
|
assignedUsersOnSelectedRows = (rows: GuiSelectedRow[]) => {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Target {
|
||||||
|
Module,
|
||||||
|
Group
|
||||||
|
}
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
<div class="row p-0 m-0">
|
||||||
|
<div class="col p-0 m-0">
|
||||||
|
<nav class="navbar bg-body-tertiary">
|
||||||
|
<form class="container-fluid justify-content-start">
|
||||||
|
<button class="btn btn-outline-success me-2" type="button"
|
||||||
|
(click)="addSelectedUsers()">Benutzer<br>Hinzufügen</button>
|
||||||
|
</form>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row align-items-start p-0 m-0">
|
||||||
|
<div class="col-6 p-0 m-0">
|
||||||
|
<app-dir-group-table #dirGroups [rowSelection]="dirGroupsRowSelection" [onSelectedRows]="dirGroupOnSelectedRows"></app-dir-group-table>
|
||||||
|
</div>
|
||||||
|
<div class="col-6 p-0 m-0">
|
||||||
|
<app-dir-user-table #dirUsers [initData]="initWithoutData" [rowSelection]="dirUsersRowSelection"></app-dir-user-table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { UserGroupDirImportComponent } from './user-group-dir-import.component';
|
||||||
|
|
||||||
|
describe('UserGroupDirImportComponent', () => {
|
||||||
|
let component: UserGroupDirImportComponent;
|
||||||
|
let fixture: ComponentFixture<UserGroupDirImportComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [UserGroupDirImportComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(UserGroupDirImportComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,96 @@
|
|||||||
|
import { AfterViewInit, Component, Inject, OnInit, ViewChild } from '@angular/core';
|
||||||
|
import { GuiRowSelection, GuiRowSelectionMode, GuiRowSelectionType, GuiSelectedRow } from '@generic-ui/ngx-grid';
|
||||||
|
import Swal from 'sweetalert2';
|
||||||
|
import { GroupService } from '../../services/group.service';
|
||||||
|
import { Observable, forkJoin, of } from 'rxjs';
|
||||||
|
import { catchError, finalize } from 'rxjs/operators';
|
||||||
|
import { DirGroupTableComponent } from '../tables/dir-group-table/dir-group-table.component';
|
||||||
|
import { DirUserTableComponent } from '../tables/dir-user-table/dir-user-table.component';
|
||||||
|
import { UserService } from '../../services/user.service';
|
||||||
|
import {User} from '../../models/user-management.api.models'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-user-group-dir-import',
|
||||||
|
templateUrl: './user-group-dir-import.component.html',
|
||||||
|
styleUrl: './user-group-dir-import.component.css'
|
||||||
|
})
|
||||||
|
export class UserGroupDirImportComponent implements OnInit, AfterViewInit {
|
||||||
|
|
||||||
|
initWithoutData = () => { }
|
||||||
|
|
||||||
|
constructor(private gService: GroupService, private uService: UserService) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewChild('dirGroups') dirGroups!: DirGroupTableComponent;
|
||||||
|
@ViewChild('dirUsers') dirUsers!: DirUserTableComponent;
|
||||||
|
|
||||||
|
ngAfterViewInit(): void {
|
||||||
|
//this.dirUsers.loading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
dirGroupsRowSelection: GuiRowSelection = {
|
||||||
|
enabled: true,
|
||||||
|
type: GuiRowSelectionType.ROW,
|
||||||
|
mode: GuiRowSelectionMode.SINGLE
|
||||||
|
}
|
||||||
|
|
||||||
|
dirUsersRowSelection: GuiRowSelection = {
|
||||||
|
enabled: true,
|
||||||
|
type: GuiRowSelectionType.CHECKBOX,
|
||||||
|
mode: GuiRowSelectionMode.MULTIPLE
|
||||||
|
}
|
||||||
|
|
||||||
|
addSelectedUsers() {
|
||||||
|
let requests = new Array<Observable<User | null>>();
|
||||||
|
let numAdded: number = 0;
|
||||||
|
for (let row of this.dirUsers.selectedRows) {
|
||||||
|
// Create an Observable for each request and add it to the requests array
|
||||||
|
requests.push(
|
||||||
|
this.uService.create({
|
||||||
|
email: row?.source?.mail?.[0],
|
||||||
|
prename: row.source?.givenname?.[0],
|
||||||
|
username: row.source?.samaccountname?.[0],
|
||||||
|
name: row.source?.sn?.[0],
|
||||||
|
}).pipe(
|
||||||
|
catchError((err) => {
|
||||||
|
return of(null);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
forkJoin(requests).pipe(
|
||||||
|
// finalize is executed after all requests are completed or when an error occurs
|
||||||
|
finalize(() => {
|
||||||
|
// Show Swal notification after all requests are completed
|
||||||
|
Swal.fire({
|
||||||
|
icon: "success",
|
||||||
|
title: "Completed",
|
||||||
|
text: `${numAdded} new users added`,
|
||||||
|
position: "center",
|
||||||
|
showConfirmButton: false,
|
||||||
|
timer: 3000
|
||||||
|
});
|
||||||
|
|
||||||
|
this.dirUsers.safelyUnselectAll();
|
||||||
|
})
|
||||||
|
).subscribe({
|
||||||
|
next: (results) => {
|
||||||
|
numAdded += results.filter(result => result !== null).length;
|
||||||
|
},
|
||||||
|
error: (err) => {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
dirGroupOnSelectedRows = (rows: Array<GuiSelectedRow>) => {
|
||||||
|
if (rows.length > 0) {
|
||||||
|
this.dirGroups.safelyUnselectAll()
|
||||||
|
let groupName: string = rows[rows.length - 1].source.samaccountname;
|
||||||
|
this.dirUsers.fetchDataByGroupName(groupName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
<div class="container-fluid text-center">
|
||||||
|
<!-- First row: Assignment -->
|
||||||
|
<div class="row">
|
||||||
|
<!-- (1, 1): users -->
|
||||||
|
<div class="col-5">
|
||||||
|
<mat-tab-group>
|
||||||
|
<mat-tab label="Benutzer">
|
||||||
|
<app-user-table #users [onSelectedRows]="userOnSelectedRows"></app-user-table>
|
||||||
|
</mat-tab>
|
||||||
|
</mat-tab-group>
|
||||||
|
</div>
|
||||||
|
<!-- (1, 2): right groups -->
|
||||||
|
<div class="col-2">
|
||||||
|
<mat-tab-group>
|
||||||
|
<mat-tab label="Rich. Gruppen">
|
||||||
|
<app-group-table #rightGroups] [columns]="groupRightColumns" [onSelectedRows]="rightGroupOnSelectedRows"></app-group-table>
|
||||||
|
</mat-tab>
|
||||||
|
</mat-tab-group>
|
||||||
|
</div>
|
||||||
|
<!-- (1, 3): representations -->
|
||||||
|
<div class="col-2">
|
||||||
|
<mat-tab-group>
|
||||||
|
<mat-tab label="Rep. Benutzer">
|
||||||
|
<app-user-table #repUsers [onSelectedRows]="repUserOnSelectedRows"></app-user-table>
|
||||||
|
</mat-tab>
|
||||||
|
<mat-tab label="Rep. Gruppen">
|
||||||
|
<app-group-table #repGroups [columns]="groupRepCols" [onSelectedRows]="repGroupOnSelectedRows"></app-group-table>
|
||||||
|
</mat-tab>
|
||||||
|
</mat-tab-group>
|
||||||
|
</div>
|
||||||
|
<!-- (1, 4): assigned users -->
|
||||||
|
<div class="col-3">
|
||||||
|
<mat-tab-group>
|
||||||
|
<mat-tab label="{{useRepLabel}}">
|
||||||
|
<app-user-rep-table #userReps [initData]="initWithoutData" [onSelectedRows]="userRepOnSelectedRows"></app-user-rep-table>
|
||||||
|
</mat-tab>
|
||||||
|
</mat-tab-group>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { UserRepresentationComponent } from './user-representation.component';
|
||||||
|
|
||||||
|
describe('UserRepresentationComponent', () => {
|
||||||
|
let component: UserRepresentationComponent;
|
||||||
|
let fixture: ComponentFixture<UserRepresentationComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [UserRepresentationComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(UserRepresentationComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,193 @@
|
|||||||
|
import { Component, Inject, ViewChild } from '@angular/core';
|
||||||
|
import { GuiColumn, GuiSelectedRow } from '@generic-ui/ngx-grid/gui/grid/src/core/api/gui.grid.public-api';
|
||||||
|
import { UserTableComponent } from '../tables/user-table/user-table.component';
|
||||||
|
import { UserRepTableComponent } from '../tables/user-rep-table/user-rep-table.component';
|
||||||
|
import { GroupTableComponent } from '../tables/group-table/group-table.component';
|
||||||
|
import { UserRepService } from '../../services/user-representation.service';
|
||||||
|
import Swal from 'sweetalert2';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
standalone: true,
|
||||||
|
selector: 'app-user-representation',
|
||||||
|
templateUrl: './user-representation.component.html',
|
||||||
|
styleUrl: './user-representation.component.css'
|
||||||
|
})
|
||||||
|
export class UserRepresentationComponent {
|
||||||
|
|
||||||
|
useRepLabel: string = "";
|
||||||
|
groupRepCols: Array<GuiColumn>;
|
||||||
|
groupRightColumns: Array<GuiColumn>;
|
||||||
|
slUserId: null | number = null;
|
||||||
|
slRepUserId: null | number = null;
|
||||||
|
slRepGroupId: null | number = null;
|
||||||
|
slRightGroupId: null | number = null;
|
||||||
|
slUserRepId: null | number = null;
|
||||||
|
userRepService: UserRepService
|
||||||
|
|
||||||
|
initWithoutData = () => { }
|
||||||
|
|
||||||
|
constructor(userRepService: UserRepService, @Inject('GROUP_REP_TABLE_COLUMNS') groupRepCols: Array<GuiColumn>, @Inject('GROUP_RIGHT_TABLE_COLUMNS') groupRightColumns: Array<GuiColumn>) {
|
||||||
|
this.groupRepCols = groupRepCols;
|
||||||
|
this.groupRightColumns = groupRightColumns;
|
||||||
|
this.userRepService = userRepService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewChild("users") users!: UserTableComponent;
|
||||||
|
@ViewChild("repUsers") repUsers!: UserTableComponent;
|
||||||
|
@ViewChild("repGroups") repGroups!: GroupTableComponent;
|
||||||
|
@ViewChild("rightGroups") rightGroups!: GroupTableComponent;
|
||||||
|
@ViewChild("userReps") userReps!: UserRepTableComponent;
|
||||||
|
|
||||||
|
userOnSelectedRows = (rows: GuiSelectedRow[]) => {
|
||||||
|
if (rows.length > 0) {
|
||||||
|
this.users.safelyUnselectAll();
|
||||||
|
this.useRepLabel = `Repräsentationen von ${rows[0].source?.username}`
|
||||||
|
this.userReps.fetchData(rows[0].source?.id)
|
||||||
|
this.slUserId = rows[0].source?.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rightGroupOnSelectedRows = (rows: GuiSelectedRow[]) => {
|
||||||
|
if (rows.length > 0) {
|
||||||
|
this.slRightGroupId = rows[0].source?.id
|
||||||
|
} else {
|
||||||
|
this.slRightGroupId = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
repUserOnSelectedRows = (rows: GuiSelectedRow[]) => {
|
||||||
|
if (rows.length == 0 && this.slRepUserId) {
|
||||||
|
if(!this.slUserId){
|
||||||
|
Swal.fire({
|
||||||
|
icon: "error",
|
||||||
|
title: "Oops...",
|
||||||
|
text: "Bitte wählen Sie den Benutzer!",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if(!this.slRepUserId){
|
||||||
|
Swal.fire({
|
||||||
|
icon: "error",
|
||||||
|
title: "Oops...",
|
||||||
|
text: "Bitte wählen Sie die repräsentative Benutzer!",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if(!this.slRightGroupId){
|
||||||
|
Swal.fire({
|
||||||
|
icon: "error",
|
||||||
|
title: "Oops...",
|
||||||
|
text: "Bitte wählen Sie die richtige Gruppe!",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var newUserRep = {
|
||||||
|
userId: this.slUserId,
|
||||||
|
rightGroupId: this.slRightGroupId,
|
||||||
|
repUserId: this.slRepUserId,
|
||||||
|
addedWho: 'DEFAULT'
|
||||||
|
}
|
||||||
|
this.userRepService.create(newUserRep).subscribe({
|
||||||
|
next: (response) => {
|
||||||
|
this.slRepUserId = null;
|
||||||
|
this.repUsers.safelyUnselectAll()
|
||||||
|
if(this.slUserId != null)
|
||||||
|
this.userReps.fetchData(this.slUserId)
|
||||||
|
},
|
||||||
|
error: (error) => {
|
||||||
|
const errorMessage = error?.error || "Es ist ein unerwarteter Fehler aufgetreten.";
|
||||||
|
Swal.fire({
|
||||||
|
icon: "error",
|
||||||
|
title: "Oops...",
|
||||||
|
text: `${errorMessage}\nBitte versuchen Sie es später noch einmal.`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.slRepUserId = null;
|
||||||
|
}
|
||||||
|
else if(rows.length > 0) {
|
||||||
|
this.slRepUserId = rows[0].source?.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
repGroupOnSelectedRows = (rows: GuiSelectedRow[]) => {
|
||||||
|
if (rows.length == 0 && this.slRepGroupId) {
|
||||||
|
if(!this.slUserId){
|
||||||
|
Swal.fire({
|
||||||
|
icon: "error",
|
||||||
|
title: "Oops...",
|
||||||
|
text: "Bitte wählen Sie den Benutzer!",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if(!this.slRepGroupId){
|
||||||
|
Swal.fire({
|
||||||
|
icon: "error",
|
||||||
|
title: "Oops...",
|
||||||
|
text: "Bitte wählen Sie die repräsentative Gruppe!",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if(!this.slRightGroupId){
|
||||||
|
Swal.fire({
|
||||||
|
icon: "error",
|
||||||
|
title: "Oops...",
|
||||||
|
text: "Bitte wählen Sie die richtige Gruppe!",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var newUserRep = {
|
||||||
|
userId: this.slUserId,
|
||||||
|
rightGroupId: this.slRightGroupId,
|
||||||
|
repGroupId: this.slRepGroupId,
|
||||||
|
addedWho: 'DEFAULT'
|
||||||
|
}
|
||||||
|
this.userRepService.create(newUserRep).subscribe({
|
||||||
|
next: (res) => {
|
||||||
|
this.slRepGroupId = null;
|
||||||
|
this.repUsers.safelyUnselectAll()
|
||||||
|
if(this.slUserId != null)
|
||||||
|
this.userReps.fetchData(this.slUserId)
|
||||||
|
},
|
||||||
|
error: (error) => {
|
||||||
|
const errorMessage = error?.error || "Es ist ein unerwarteter Fehler aufgetreten.";
|
||||||
|
Swal.fire({
|
||||||
|
icon: "error",
|
||||||
|
title: "Oops...",
|
||||||
|
text: `${errorMessage}\nBitte versuchen Sie es später noch einmal.`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.slRepGroupId = null;
|
||||||
|
}
|
||||||
|
else if(rows.length > 0) {
|
||||||
|
this.slRepGroupId = rows[0].source?.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
userRepOnSelectedRows = (rows: GuiSelectedRow[]) => {
|
||||||
|
if (rows.length == 0 && this.slUserRepId) {
|
||||||
|
this.userRepService.delete(this.slUserRepId).subscribe({
|
||||||
|
next: (res) => {
|
||||||
|
this.slUserRepId = null;
|
||||||
|
this.userReps.safelyUnselectAll();
|
||||||
|
if(this.slUserId != null)
|
||||||
|
this.userReps.fetchData(this.slUserId)
|
||||||
|
},
|
||||||
|
error: (err) => {
|
||||||
|
this.slUserRepId = null;
|
||||||
|
this.repUsers.safelyUnselectAll()
|
||||||
|
const errorMessage = err?.error || "Es ist ein unerwarteter Fehler aufgetreten.";
|
||||||
|
Swal.fire({
|
||||||
|
icon: "error",
|
||||||
|
title: "Oops...",
|
||||||
|
text: `${errorMessage}\nBitte versuchen Sie es später noch einmal.`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
else if(rows.length > 0) {
|
||||||
|
this.slUserRepId = rows[0].source?.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1 @@
|
|||||||
|
<app-user-table #userTable [cellEditing] = "cellEditing"></app-user-table>
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { UserComponent } from './user.component';
|
||||||
|
|
||||||
|
describe('UserComponent', () => {
|
||||||
|
let component: UserComponent;
|
||||||
|
let fixture: ComponentFixture<UserComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [UserComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(UserComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
import { Component, ViewChild } from '@angular/core';
|
||||||
|
import { GuiCellEdit } from '@generic-ui/ngx-grid';
|
||||||
|
import { UserTableComponent } from '../tables/user-table/user-table.component';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
standalone: true,
|
||||||
|
selector: 'app-user',
|
||||||
|
templateUrl: './user.component.html',
|
||||||
|
styleUrl: './user.component.css'
|
||||||
|
})
|
||||||
|
export class UserComponent {
|
||||||
|
|
||||||
|
cellEditing: GuiCellEdit = {
|
||||||
|
enabled: true,
|
||||||
|
rowEdit: (value: any, item: any, index: number) => {
|
||||||
|
return Boolean(index % 2);
|
||||||
|
},
|
||||||
|
cellEdit: (value: any, item: any, index: number) => {
|
||||||
|
return Boolean(index % 5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ViewChild("userTable") userTable! : UserTableComponent
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
<div class="container pt-5">
|
||||||
|
<div class="row justify-content-center mt-5">
|
||||||
|
<div class="text-center">
|
||||||
|
<img src="../../assets/img/Huhn_andersrum.webp" height="300vh">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row justify-content-center mt-5">
|
||||||
|
<div class="text-center">
|
||||||
|
<h1 style="font-size: 500%;">Digital Data - User Manager</h1>
|
||||||
|
<h5 style="font-weight: normal;">Ein zentrales Portal zur effizienten Verwaltung von Benutzerberechtigungen, Gruppen und
|
||||||
|
Modulen</h5>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
import { Component, Inject, OnInit } from '@angular/core';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { AuthenticationService } from '../services/authentication.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
standalone: true,
|
||||||
|
selector: 'app-home',
|
||||||
|
templateUrl: './home.component.html',
|
||||||
|
//styleUrls: ['./home.component.css']
|
||||||
|
})
|
||||||
|
export class HomeComponent implements OnInit {
|
||||||
|
|
||||||
|
username: string = '';
|
||||||
|
password: string = '';
|
||||||
|
|
||||||
|
constructor(private authService: AuthenticationService, private router: Router) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
i {
|
||||||
|
margin-left: -30px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
<div class="container p-0 m-0">
|
||||||
|
<div class="row justify-content-center p-0 m-0">
|
||||||
|
<div class="col p-0 m-0">
|
||||||
|
<div class="card px-5">
|
||||||
|
<div class="card-body mx-5 px-5">
|
||||||
|
<form (ngSubmit)="login()">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="username" class="form-label">Benutzername</label>
|
||||||
|
<input type="text" class="form-control" [(ngModel)]="username" name="Username" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="password" class="form-label">Kennwort</label>
|
||||||
|
<div class="d-flex">
|
||||||
|
<input type="password" [type]="IsPwdHidden?'password':'text'" class="form-control" [(ngModel)]="password" name="Password" required>
|
||||||
|
<i [ngClass]="'bi '+ (IsPwdHidden?'bi-eye-slash':'bi-eye') + ' mt-2'" (click)="onPasswordEyeClicked()"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary">
|
||||||
|
<span [class.spinner-border]="waitRes" [class.spinner-border-sm]="waitRes"
|
||||||
|
aria-hidden="true"></span>Anmeldung</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { LoginComponent } from './login.component';
|
||||||
|
|
||||||
|
describe('LoginComponent', () => {
|
||||||
|
let component: LoginComponent;
|
||||||
|
let fixture: ComponentFixture<LoginComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [LoginComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(LoginComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,47 @@
|
|||||||
|
import { Component, Inject, Input } from '@angular/core';
|
||||||
|
import { AuthenticationService } from '../services/authentication.service';
|
||||||
|
import Swal from 'sweetalert2';
|
||||||
|
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
standalone: true,
|
||||||
|
selector: 'app-login',
|
||||||
|
templateUrl: './login.component.html',
|
||||||
|
styleUrl: './login.component.css'
|
||||||
|
})
|
||||||
|
export class LoginComponent {
|
||||||
|
|
||||||
|
username: string = '';
|
||||||
|
password: string = '';
|
||||||
|
|
||||||
|
waitRes:boolean = false;
|
||||||
|
|
||||||
|
IsPwdHidden: boolean = true;
|
||||||
|
|
||||||
|
constructor(private authService: AuthenticationService, @Inject(MAT_DIALOG_DATA) public data: any) {
|
||||||
|
//localStorage.getItem('theme') === 'dark'
|
||||||
|
if(typeof(this.afterLogin) == typeof(data.afterLogin))
|
||||||
|
this.afterLogin = data.afterLogin;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Input() afterLogin: () => void = () => { }
|
||||||
|
|
||||||
|
login(): void {
|
||||||
|
this.waitRes = true;
|
||||||
|
this.authService.login(this.username, this.password).subscribe({
|
||||||
|
next: () => this.afterLogin(),
|
||||||
|
error: (err) => {
|
||||||
|
this.waitRes = false;
|
||||||
|
Swal.fire({
|
||||||
|
icon: "error",
|
||||||
|
title: "Oops...",
|
||||||
|
text: err.error.messages.join("\n"),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
complete: () => this.waitRes = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
onPasswordEyeClicked() {
|
||||||
|
this.IsPwdHidden = !this.IsPwdHidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,79 @@
|
|||||||
|
export interface User {
|
||||||
|
id?: number;
|
||||||
|
prename: string;
|
||||||
|
name?: string;
|
||||||
|
username: string;
|
||||||
|
shortname?: string;
|
||||||
|
email?: string;
|
||||||
|
language?: string;
|
||||||
|
comment?: string;
|
||||||
|
deleted?: boolean;
|
||||||
|
dateFormat?: string;
|
||||||
|
addedWho?: string;
|
||||||
|
changedWho?: string;
|
||||||
|
active?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export interface Group {
|
||||||
|
id?: number;
|
||||||
|
name?: string;
|
||||||
|
adSync?: boolean;
|
||||||
|
internal?: boolean;
|
||||||
|
active?: boolean;
|
||||||
|
comment?: string;
|
||||||
|
addedWho?: string;
|
||||||
|
changedWho?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Module {
|
||||||
|
id: number;
|
||||||
|
name?: string;
|
||||||
|
shortName?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ModuleOfUser {
|
||||||
|
id?: number;
|
||||||
|
userId: number;
|
||||||
|
moduleId: number;
|
||||||
|
isAdmin?: boolean;
|
||||||
|
comment?: string;
|
||||||
|
addedWho?: string;
|
||||||
|
changedWho?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GroupOfUser {
|
||||||
|
id?: number;
|
||||||
|
userId: number;
|
||||||
|
groupId: number;
|
||||||
|
comment?: string;
|
||||||
|
addedWho?: string;
|
||||||
|
changedWho?: string;
|
||||||
|
user?: User,
|
||||||
|
group?: Group
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UserRep {
|
||||||
|
id?: number,
|
||||||
|
userId: number,
|
||||||
|
repGroupId?: number,
|
||||||
|
rightGroupId: number,
|
||||||
|
addedWho: string,
|
||||||
|
repUserId?: number,
|
||||||
|
user?: User,
|
||||||
|
repGroup?: Group,
|
||||||
|
rightGroup?: Group,
|
||||||
|
repUser?: User
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DirGroup {
|
||||||
|
samaccountname: Array<string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DirUser {
|
||||||
|
samaccountname?: Array<string>;
|
||||||
|
givenname?: Array<string>;
|
||||||
|
sn?: Array<string>;
|
||||||
|
mail?: string;
|
||||||
|
addedWho?: string;
|
||||||
|
}
|
||||||
@ -0,0 +1,95 @@
|
|||||||
|
a.navbar-brand {
|
||||||
|
white-space: normal;
|
||||||
|
text-align: center;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
html {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.box-shadow {
|
||||||
|
box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bd-placeholder-img {
|
||||||
|
font-size: 1.125rem;
|
||||||
|
text-anchor: middle;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.bd-placeholder-img-lg {
|
||||||
|
font-size: 3.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.b-example-divider {
|
||||||
|
width: 100%;
|
||||||
|
height: 3rem;
|
||||||
|
background-color: rgba(0, 0, 0, .1);
|
||||||
|
border: solid rgba(0, 0, 0, .15);
|
||||||
|
border-width: 1px 0;
|
||||||
|
box-shadow: inset 0 .5em 1.5em rgba(0, 0, 0, .1), inset 0 .125em .5em rgba(0, 0, 0, .15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.b-example-vr {
|
||||||
|
flex-shrink: 0;
|
||||||
|
width: 1.5rem;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bi {
|
||||||
|
vertical-align: -.125em;
|
||||||
|
fill: currentColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-scroller {
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
height: 2.75rem;
|
||||||
|
overflow-y: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-scroller .nav {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
margin-top: -1px;
|
||||||
|
overflow-x: auto;
|
||||||
|
text-align: center;
|
||||||
|
white-space: nowrap;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-bd-primary {
|
||||||
|
--bd-violet-bg: #712cf9;
|
||||||
|
--bd-violet-rgb: 112.520718, 44.062154, 249.437846;
|
||||||
|
|
||||||
|
--bs-btn-font-weight: 600;
|
||||||
|
--bs-btn-color: var(--bs-white);
|
||||||
|
--bs-btn-bg: var(--bd-violet-bg);
|
||||||
|
--bs-btn-border-color: var(--bd-violet-bg);
|
||||||
|
--bs-btn-hover-color: var(--bs-white);
|
||||||
|
--bs-btn-hover-bg: #6528e0;
|
||||||
|
--bs-btn-hover-border-color: #6528e0;
|
||||||
|
--bs-btn-focus-shadow-rgb: var(--bd-violet-rgb);
|
||||||
|
--bs-btn-active-color: var(--bs-btn-hover-color);
|
||||||
|
--bs-btn-active-bg: #5a23c8;
|
||||||
|
--bs-btn-active-border-color: #5a23c8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bd-mode-toggle {
|
||||||
|
z-index: 1500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bd-mode-toggle .dropdown-menu .active .bi {
|
||||||
|
display: block !important;
|
||||||
|
}
|
||||||
@ -0,0 +1,65 @@
|
|||||||
|
<header>
|
||||||
|
<nav class="navbar navbar-expand-lg bg-body-tertiary fs-5">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<!-- DD Logo -->
|
||||||
|
<a class="light-stroke ms-5" href="https://digitaldata.works/">
|
||||||
|
<img fetchpriority="high" width="200vw"
|
||||||
|
[src]="(isDarkTheme)?'../../assets/img/DD_white.svg':'../../assets/img/digital_data.svg'">
|
||||||
|
</a>
|
||||||
|
<!-- Navbars -->
|
||||||
|
<div *ngIf="isLogedIn()" class="navbar-collapse collapse d-sm-inline-flex justify-content-center"
|
||||||
|
[ngClass]="{ show: isExpanded }">
|
||||||
|
<ul class=" navbar-nav flex-grow">
|
||||||
|
<li class="nav-item" [routerLinkActive]="['link-active']" [routerLinkActiveOptions]="{exact: true}">
|
||||||
|
<a class="nav-link" [routerLink]="['/user-assignment']" [routerLinkActive]="'active'">Autorisierung</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item" [routerLinkActive]="['link-active']" [routerLinkActiveOptions]="{exact: true}">
|
||||||
|
<a class="nav-link" [routerLink]="['/user-table']" [routerLinkActive]="'active'">Benutzer</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item" [routerLinkActive]="['link-active']" [routerLinkActiveOptions]="{exact: true}">
|
||||||
|
<a class="nav-link" [routerLink]="['/group-table']" [routerLinkActive]="'active'">Gruppen</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item" [routerLinkActive]="['link-active']" [routerLinkActiveOptions]="{exact: true}">
|
||||||
|
<a class="nav-link" [routerLink]="['/user-representation']" [routerLinkActive]="'active'">Vertretung</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item" [routerLinkActive]="['link-active']" [routerLinkActiveOptions]="{exact: true}">
|
||||||
|
<a class="nav-link" [routerLink]="['/module-table']" [routerLinkActive]="'active'">Module</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<!-- Right menu -->
|
||||||
|
<div class="navbar-collapse justify-content-end me-5">
|
||||||
|
<a class="navbar-brand" [routerLink]="['/']">User Manager Portal</a>
|
||||||
|
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse"
|
||||||
|
aria-label="Toggle navigation" [attr.aria-expanded]="isExpanded" (click)="toggle()">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
<app-color-mode-bttn></app-color-mode-bttn>
|
||||||
|
<div *ngIf="isLogedIn()" class="d-flex">
|
||||||
|
<button class="btn m-0 p-0" type="button" (click)="importUser()">
|
||||||
|
<img src="../../assets/img/user.svg">
|
||||||
|
</button>
|
||||||
|
<button class="btn m-0 p-0" type="button" (click)="importGroup()" style="stroke: #a9a8ad;">
|
||||||
|
<svg class="bi" width="3em" height="2.5em" viewBox="0 0 488.6 488.6" stroke="#a9a8ad">
|
||||||
|
<path opacity="0.5"
|
||||||
|
d="M480.9,333.2c-27.2-22.3-56.5-37.1-62.4-40c-0.7-0.3-1.1-1-1.1-1.8v-42.3c5.3-3.5,8.8-9.6,8.8-16.5v-43.9
|
||||||
|
c0-21.8-17.7-39.5-39.5-39.5H382h-4.7c-21.8,0-39.5,17.7-39.5,39.5v43.9c0,6.9,3.5,12.9,8.8,16.5v42.3c0,0.3-0.1,0.5-0.1,0.7
|
||||||
|
c8.3,5.7,17,12.1,25.5,19.1c9.9,8.2,15.6,20.2,15.6,33.2v35.3h101v-30.1C488.6,343.3,485.8,337.2,480.9,333.2z" />
|
||||||
|
<path opacity="0.5" d="M142,291.4v-42.3c5.3-3.5,8.8-9.6,8.8-16.5v-43.9c0-21.8-17.7-39.5-39.5-39.5h-4.7h-4.7c-21.8,0-39.5,17.7-39.5,39.5v43.9
|
||||||
|
c0,6.9,3.5,12.9,8.8,16.5v42.3c0,0.7-0.4,1.4-1.1,1.8c-6,2.9-35.3,17.7-62.4,40c-4.9,4-7.7,10.1-7.7,16.4v30.1h101v-35.3
|
||||||
|
c0-12.9,5.7-25,15.6-33.2c8.5-7,17.2-13.4,25.5-19.1C142.1,291.9,142,291.7,142,291.4z" />
|
||||||
|
<path opacity="0.5" d="M360.5,325.1c-31.9-26.2-66.3-43.6-73.4-47.1c-0.8-0.4-1.3-1.2-1.3-2.1v-49.7c6.2-4.2,10.4-11.3,10.4-19.3v-51.6
|
||||||
|
c0-25.6-20.8-46.4-46.4-46.4h-5.5h-5.5c-25.6,0-46.4,20.8-46.4,46.4v51.5c0,8.1,4.1,15.2,10.4,19.3v49.7c0,0.9-0.5,1.7-1.3,2.1
|
||||||
|
c-7,3.4-41.4,20.8-73.4,47.1c-5.8,4.7-9.1,11.8-9.1,19.3v35.3h108.9l10.8-49.3c-21.7-30.3,1.6-31.8,5.7-31.8l0,0l0,0
|
||||||
|
c4.1,0,27.4,1.5,5.7,31.8l10.8,49.3h108.9v-35.3C369.6,336.9,366.3,329.8,360.5,325.1z" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<button class="fs-5 btn d-flex align-items-center ms-2 me-0 pe-0" type="button" (click)="auth()">
|
||||||
|
<img fetchpriority="high" src="../../assets/img/login_logo.svg" alt="" style="stroke: #a9a8ad;">
|
||||||
|
{{isLogedIn() ? "Log out" : "Log in"}}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
@ -0,0 +1,67 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
|
import { UserGroupDirImportComponent } from '../components/user-group-dir-import/user-group-dir-import.component';
|
||||||
|
import { GroupDirImportComponent } from '../components/group-dir-import/group-dir-import.component';
|
||||||
|
import { AuthenticationService, IsLogedIn } from '../services/authentication.service';
|
||||||
|
import { LoginComponent } from '../login/login.component';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
standalone:true,
|
||||||
|
selector: 'app-nav-menu',
|
||||||
|
templateUrl: './nav-menu.component.html',
|
||||||
|
styleUrls: ['./nav-menu.component.css']
|
||||||
|
})
|
||||||
|
export class NavMenuComponent {
|
||||||
|
isLogedIn() {
|
||||||
|
return IsLogedIn();
|
||||||
|
}
|
||||||
|
isExpanded = false;
|
||||||
|
|
||||||
|
constructor(public dialog: MatDialog, private authService: AuthenticationService) {
|
||||||
|
this.authService.isAuthenticated().subscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
get isDarkTheme(): boolean {
|
||||||
|
return localStorage.getItem('theme') === 'dark'
|
||||||
|
}
|
||||||
|
|
||||||
|
collapse() {
|
||||||
|
this.isExpanded = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
toggle() {
|
||||||
|
this.isExpanded = !this.isExpanded;
|
||||||
|
}
|
||||||
|
|
||||||
|
importUser() {
|
||||||
|
const dialogRef = this.dialog.open(UserGroupDirImportComponent, {
|
||||||
|
width: "50vw"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
importGroup() {
|
||||||
|
const dialogRef = this.dialog.open(GroupDirImportComponent, {
|
||||||
|
width: "50vw"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
auth() {
|
||||||
|
this.authService.isAuthenticated().subscribe({
|
||||||
|
next: (res) => {
|
||||||
|
if (res)
|
||||||
|
this.authService.logout().subscribe();
|
||||||
|
else {
|
||||||
|
const dialogRef = this.dialog.open(LoginComponent, {
|
||||||
|
width: "35vw",
|
||||||
|
data: {
|
||||||
|
afterLogin: () => {
|
||||||
|
dialogRef.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: (err) => {
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,81 @@
|
|||||||
|
import { Inject, Injectable } from '@angular/core';
|
||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import Swal from 'sweetalert2';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class AuthenticationService {
|
||||||
|
constructor(
|
||||||
|
private router: Router,
|
||||||
|
private http: HttpClient,
|
||||||
|
@Inject('LOGIN_URL') private loginUrl: string,
|
||||||
|
@Inject('LOGOUT_URL') private logoutUrl: string,
|
||||||
|
@Inject('LOGIN_CHECK_URL') private checkUrl: string) { }
|
||||||
|
|
||||||
|
isAuthenticated(): Observable<boolean> {
|
||||||
|
return new Observable(observer => {
|
||||||
|
this.http.get<boolean>(this.checkUrl, { withCredentials: true })
|
||||||
|
.subscribe({
|
||||||
|
next: (response) => {
|
||||||
|
_isLogedIn = response;
|
||||||
|
observer.next(response)
|
||||||
|
},
|
||||||
|
error: (error) => {
|
||||||
|
this.showErrorAlert()
|
||||||
|
observer.error(error)
|
||||||
|
},
|
||||||
|
complete: () => observer.complete()
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
login(username: string, password: string): Observable<any> {
|
||||||
|
return new Observable(observer => {
|
||||||
|
const userData = { username, password };
|
||||||
|
|
||||||
|
this.http.post<any>(this.loginUrl, userData, { withCredentials: true })
|
||||||
|
.subscribe({
|
||||||
|
next: (response) => {
|
||||||
|
_isLogedIn = true;
|
||||||
|
observer.next(response);
|
||||||
|
},
|
||||||
|
error: (error) => {
|
||||||
|
this.showErrorAlert()
|
||||||
|
observer.error(error);
|
||||||
|
},
|
||||||
|
complete: () => {
|
||||||
|
observer.complete();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
logout(): Observable<any> {
|
||||||
|
return new Observable(observer => {
|
||||||
|
this.http.post<any>(this.logoutUrl, {}, { withCredentials: true })
|
||||||
|
.subscribe({
|
||||||
|
next: (response) => {
|
||||||
|
this.router.navigate(['/']);
|
||||||
|
_isLogedIn = false;
|
||||||
|
observer.next(response)
|
||||||
|
},
|
||||||
|
error: (error) => observer.error(error),
|
||||||
|
complete: () => observer.complete()
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
showErrorAlert() {
|
||||||
|
Swal.fire({
|
||||||
|
icon: "error",
|
||||||
|
title: "Oops...",
|
||||||
|
text: "The backend application is not responding.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let _isLogedIn: boolean = false;
|
||||||
|
export const IsLogedIn = () => _isLogedIn
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ColorModeService } from './color-mode.service';
|
||||||
|
|
||||||
|
describe('ColorModeService', () => {
|
||||||
|
let service: ColorModeService;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({});
|
||||||
|
service = TestBed.inject(ColorModeService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,87 @@
|
|||||||
|
import { Injectable, Inject, Renderer2, RendererFactory2, PLATFORM_ID } from '@angular/core';
|
||||||
|
import { DOCUMENT } from '@angular/common';
|
||||||
|
import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class ColorModeService {
|
||||||
|
|
||||||
|
private readonly renderer: Renderer2;
|
||||||
|
|
||||||
|
private themeSubject = new BehaviorSubject<Theme>(GetLocalTheme());
|
||||||
|
public themeChanges$ = this.themeSubject.asObservable();
|
||||||
|
|
||||||
|
constructor(@Inject(PLATFORM_ID) private platformId: object, private rendererFactory: RendererFactory2, @Inject(DOCUMENT) public document: Document) {
|
||||||
|
this.renderer = rendererFactory.createRenderer(null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
setTheme(theme: Theme) {
|
||||||
|
localStorage.setItem('theme', theme)
|
||||||
|
this.updateTheme();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateTheme() {
|
||||||
|
let theme: Theme = GetLocalTheme();
|
||||||
|
|
||||||
|
this.themeSubject.next(GetLocalTheme());
|
||||||
|
|
||||||
|
this.themeSubject.next(GetLocalTheme());
|
||||||
|
|
||||||
|
switch(theme) {
|
||||||
|
case Theme.Dark:
|
||||||
|
case Theme.Auto:
|
||||||
|
this.renderer.addClass(this.document.body, "mat-color-scheme-dark")
|
||||||
|
this.renderer.removeClass(this.document.body, "mat-color-scheme-light")
|
||||||
|
break;
|
||||||
|
case Theme.Light:
|
||||||
|
this.renderer.addClass(this.document.body, "mat-color-scheme-light")
|
||||||
|
this.renderer.removeClass(this.document.body, "mat-color-scheme-dark")
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ColorModeService.ThemeChangeSubscribers.forEach(sbc => sbc(theme));
|
||||||
|
|
||||||
|
const element = this.document.documentElement;
|
||||||
|
if (theme === Theme.Auto) {
|
||||||
|
const preferredTheme = window.matchMedia('(prefers-color-scheme: dark)').matches ? Theme.Dark : Theme.Light;
|
||||||
|
this.renderer.setAttribute(element, 'data-bs-theme', preferredTheme);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
this.renderer.setAttribute(element, 'data-bs-theme', theme);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ThemeChangeSubscribers: Array<(theme: Theme) => void> = new Array();
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum Theme {
|
||||||
|
Dark = 'dark',
|
||||||
|
Light = 'light',
|
||||||
|
Auto = 'auto'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ParseTheme: (value: string) => Theme | undefined = (value: string) => {
|
||||||
|
switch (value) {
|
||||||
|
case Theme.Dark:
|
||||||
|
return Theme.Dark;
|
||||||
|
case Theme.Light:
|
||||||
|
return Theme.Light;
|
||||||
|
case Theme.Auto:
|
||||||
|
return Theme.Auto;
|
||||||
|
default:
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function GetLocalTheme(): Theme {
|
||||||
|
let sTheme: string | null = localStorage.getItem('theme');
|
||||||
|
|
||||||
|
if (sTheme === null)
|
||||||
|
return Theme.Dark;
|
||||||
|
|
||||||
|
return ParseTheme(sTheme) ?? Theme.Dark;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const GetWinDefaultTheme = () => window.matchMedia('(prefers-color-scheme: dark)').matches ? Theme.Dark : Theme.Light;
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
import { Injectable, Inject } from '@angular/core';
|
||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
import { DirGroup, } from '../models/user-management.api.models';
|
||||||
|
import { ApiService } from './user-management.api.service';
|
||||||
|
import { Observable } from 'rxjs/internal/Observable';
|
||||||
|
import Swal from 'sweetalert2';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class DirGroupService extends ApiService<DirGroup> {
|
||||||
|
constructor(http: HttpClient, @Inject('DIR_GROUP_URL') private userUri: string) {
|
||||||
|
super(http, userUri);
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: Swal.fire
|
||||||
|
override getAll(): Observable<DirGroup[]> {
|
||||||
|
return new Observable<DirGroup[]>(observer => {
|
||||||
|
super.getAll()
|
||||||
|
.subscribe({
|
||||||
|
next: (response) => {
|
||||||
|
observer.next(response)
|
||||||
|
},
|
||||||
|
error: (error) => {
|
||||||
|
Swal.fire({
|
||||||
|
icon: "error",
|
||||||
|
title: "Oops...",
|
||||||
|
text: `Active Directory-Verbindung verloren. Bitte melden Sie sich erneut an`,
|
||||||
|
});
|
||||||
|
observer.error(error)
|
||||||
|
},
|
||||||
|
complete: () => observer.complete()
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
import { Injectable, Inject } from '@angular/core';
|
||||||
|
import { HttpClient, HttpParams } from '@angular/common/http';
|
||||||
|
import { DirUser } from '../models/user-management.api.models';
|
||||||
|
import { ApiService } from './user-management.api.service';
|
||||||
|
import { Observable } from 'rxjs/internal/Observable';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class DirUserService extends ApiService<DirUser> {
|
||||||
|
constructor(http: HttpClient, @Inject('DIR_USER_URL') private userUri: string) {
|
||||||
|
super(http, userUri);
|
||||||
|
}
|
||||||
|
|
||||||
|
override getAll(groupName?: string): Observable<DirUser[]> {
|
||||||
|
let params = new HttpParams();
|
||||||
|
if (groupName) {
|
||||||
|
params = params.set('groupName', groupName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.http.get<DirUser[]>(this.baseUrl, { params, withCredentials: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
import { HttpClient, HttpParams } from '@angular/common/http';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { Inject, Injectable } from '@angular/core';
|
||||||
|
import { DirUser } from '../models/user-management.api.models';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class DirService {
|
||||||
|
constructor(private http: HttpClient, @Inject('DIR_URL') private baseUrl: string) {
|
||||||
|
this.http = http;
|
||||||
|
}
|
||||||
|
|
||||||
|
getUser(groupName: string): Observable<DirUser[]> {
|
||||||
|
let params = new HttpParams();
|
||||||
|
if (groupName) {
|
||||||
|
params = params.set('groupName', groupName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.http.get<DirUser[]>(this.baseUrl, { params, withCredentials: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
import { Injectable, Inject } from '@angular/core';
|
||||||
|
import { HttpClient, HttpParams } from '@angular/common/http';
|
||||||
|
import { GroupOfUser } from '../models/user-management.api.models';
|
||||||
|
import { ApiService } from './user-management.api.service';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class GroupOfUserService extends ApiService<GroupOfUser> {
|
||||||
|
constructor(http: HttpClient, @Inject('GROUP_OF_USER_URL') private userUri: string) {
|
||||||
|
super(http, userUri);
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteByGroupUserId(groupId: number, userId: number): Observable<any> {
|
||||||
|
const url = `${this.baseUrl}?groupId=${groupId}&userId=${userId}`;
|
||||||
|
return this.http.delete<any>(url, { withCredentials: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
override getAll(withUser: boolean = false, withGroup: boolean = false): Observable<GroupOfUser[]> {
|
||||||
|
let params = new HttpParams();
|
||||||
|
if (withUser) {
|
||||||
|
params = params.set('withUser', withUser);
|
||||||
|
}
|
||||||
|
if (withGroup) {
|
||||||
|
params = params.set('withGroup', withGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.http.get<GroupOfUser[]>(this.baseUrl, { params, withCredentials: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
import { Injectable, Inject } from '@angular/core';
|
||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
import { DirGroup, Group, } from '../models/user-management.api.models';
|
||||||
|
import { ApiService } from './user-management.api.service';
|
||||||
|
import { Observable } from 'rxjs/internal/Observable';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class GroupService extends ApiService<Group> {
|
||||||
|
constructor(http: HttpClient, @Inject('GROUP_URL') private userUri: string) {
|
||||||
|
super(http, userUri);
|
||||||
|
}
|
||||||
|
|
||||||
|
createByDir(createModel: DirGroup): Observable<DirGroup> {
|
||||||
|
return this.http.post<DirGroup>(`${this.baseUrl}/byDir`, createModel, { withCredentials: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
import { Injectable, Inject } from '@angular/core';
|
||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
import { ModuleOfUser } from '../models/user-management.api.models';
|
||||||
|
import { ApiService } from './user-management.api.service';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class ModuleOfUserService extends ApiService<ModuleOfUser> {
|
||||||
|
constructor(http: HttpClient, @Inject('MODULE_OF_USER_URL') private userUri: string) {
|
||||||
|
super(http, userUri);
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteByModuleGroupId(moduleId: number, userId: number): Observable<any> {
|
||||||
|
const url = `${this.baseUrl}?moduleId=${moduleId}&userId=${userId}`;
|
||||||
|
return this.http.delete<any>(url, { withCredentials: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
import { Injectable, Inject } from '@angular/core';
|
||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
import { ApiService } from './user-management.api.service';
|
||||||
|
import { Module } from '../models/user-management.api.models';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class ModuleService extends ApiService<Module> {
|
||||||
|
constructor(http: HttpClient, @Inject('MODULE_URL') private userUri: string) {
|
||||||
|
super(http, userUri);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,39 @@
|
|||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { Inject, Injectable } from '@angular/core';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class ApiService<Model> {
|
||||||
|
constructor(http: HttpClient, @Inject('BASE_URL') baseUrl: string) {
|
||||||
|
this.http = http;
|
||||||
|
this.baseUrl = baseUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
http: HttpClient;
|
||||||
|
baseUrl: string;
|
||||||
|
|
||||||
|
getAll(): Observable<Model[]> {
|
||||||
|
return this.http.get<Model[]>(this.baseUrl, { withCredentials: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
getById(id: number): Observable<Model> {
|
||||||
|
const url = `${this.baseUrl}/${id}`;
|
||||||
|
return this.http.get<Model>(url, { withCredentials: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
create(createModel: Model): Observable<Model> {
|
||||||
|
return this.http.post<Model>(this.baseUrl, createModel, { withCredentials: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
update(updateModel: Model): Observable<Model> {
|
||||||
|
const url = `${this.baseUrl}`;
|
||||||
|
return this.http.put<Model>(url, updateModel, { withCredentials: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(id: number): Observable<any> {
|
||||||
|
const url = `${this.baseUrl}/${id}`;
|
||||||
|
return this.http.delete<any>(url, { withCredentials: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
import { Inject, Injectable } from "@angular/core";
|
||||||
|
import { UserRep } from "../models/user-management.api.models";
|
||||||
|
import { ApiService } from "./user-management.api.service";
|
||||||
|
import { HttpClient, HttpParams } from "@angular/common/http";
|
||||||
|
import { Observable } from "rxjs";
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: "root"
|
||||||
|
})
|
||||||
|
export class UserRepService extends ApiService<UserRep> {
|
||||||
|
constructor(http: HttpClient, @Inject('USER_REP_URL') private userRepUri: string) {
|
||||||
|
super(http, userRepUri)
|
||||||
|
}
|
||||||
|
|
||||||
|
override getAll(withUser: boolean = false, withRepGroup: boolean = false, withRightGroup: boolean = false, withRepUser: boolean = false, userId?: number): Observable<UserRep[]> {
|
||||||
|
let params = new HttpParams();
|
||||||
|
if (withUser) {
|
||||||
|
params = params.set('withUser', withUser);
|
||||||
|
}
|
||||||
|
if (withRepGroup) {
|
||||||
|
params = params.set('withRepGroup', withRepGroup);
|
||||||
|
}
|
||||||
|
if (withRightGroup) {
|
||||||
|
params = params.set('withRightGroup', withRightGroup);
|
||||||
|
}
|
||||||
|
if (withRepUser) {
|
||||||
|
params = params.set('withRepUser', withRepUser);
|
||||||
|
}
|
||||||
|
if (userId) {
|
||||||
|
params = params.set('userId', userId)
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.http.get<UserRep[]>(`${this.baseUrl}`, { params: params, withCredentials: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
import { Injectable, Inject } from '@angular/core';
|
||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
import { DirUser, User } from '../models/user-management.api.models';
|
||||||
|
import { ApiService } from './user-management.api.service';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class UserService extends ApiService<User> {
|
||||||
|
constructor(http: HttpClient, @Inject('USER_URL') private userUri: string) {
|
||||||
|
super(http, userUri);
|
||||||
|
}
|
||||||
|
|
||||||
|
getByModuleId(moduleId: number, assigned: boolean = true): Observable<User[]> {
|
||||||
|
const url = `${this.baseUrl}/ByModuleId/${moduleId}?assigned=${assigned}`;
|
||||||
|
return this.http.get<User[]>(url, { withCredentials: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
getByGroupId(groupId: number, assigned: boolean = true): Observable<User[]> {
|
||||||
|
const url = `${this.baseUrl}/ByGroupId/${groupId}?assigned=${assigned}`;
|
||||||
|
return this.http.get<User[]>(url, { withCredentials: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
createByDir(createModel: DirUser): Observable<DirUser> {
|
||||||
|
return this.http.post<DirUser>(`${this.baseUrl}/byDir`, createModel, { withCredentials: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user