Compare commits
305 Commits
Author | SHA1 | Date |
---|---|---|
|
1fbec200ab | |
|
253af06ad5 | |
|
5dc1677851 | |
|
73a69f79c9 | |
|
858a204036 | |
|
844d3dc0f0 | |
|
23bf2b51ea | |
|
e4e6a8907e | |
|
0096daca42 | |
|
de56a23a2b | |
|
3bae27d88e | |
|
265b4888c3 | |
|
ce1a06d51b | |
|
a0d833726d | |
|
c7919cf412 | |
|
cbe15bba4d | |
|
b6e8134810 | |
|
6205f3ad2f | |
|
436267cfb9 | |
|
2b3f7b6e34 | |
|
e9a00119aa | |
|
b2d34a6880 | |
|
cf5f2754c6 | |
|
80d9f5cb0f | |
|
321a31eecb | |
|
636a66b93b | |
|
6a06e0a477 | |
|
411491091b | |
|
9e9d7b9873 | |
|
dad3635f4f | |
|
2958e05c98 | |
|
c365ef2a14 | |
|
1f7101c7a0 | |
|
824e55102e | |
|
4758190b6d | |
|
64fa13f36f | |
|
c7d6e41874 | |
|
02fbf57384 | |
|
bd0135b796 | |
|
390bc54213 | |
|
7659c09cd6 | |
|
380cf50080 | |
|
eac4b0a948 | |
|
ebd448ce71 | |
|
672f0eade4 | |
|
2b0d70dd58 | |
|
4f2bf0ec05 | |
|
1c417e5f35 | |
|
4fed92505e | |
|
74f5f79206 | |
|
b5a6bb0559 | |
|
5baf4d8e3d | |
|
4d32540784 | |
|
9ef61500cb | |
|
41f9521d4a | |
|
673fe5e7fd | |
|
945ae8ca0b | |
|
11a4773570 | |
|
49671ed686 | |
|
34bf065de9 | |
|
23d2b591f8 | |
|
6f16d07537 | |
|
e33726bf6a | |
|
edfab0be94 | |
|
da9fbc1fdb | |
|
c568a555a2 | |
|
7bff91aa42 | |
|
081da1efc6 | |
|
7dc1f662e6 | |
|
fd0778d096 | |
|
d430091d54 | |
|
1d28e443a3 | |
|
8fdee4fc9b | |
|
22d775e793 | |
|
3cd61cfc8f | |
|
1fbf494061 | |
|
4998463ba1 | |
|
1f2c953509 | |
|
2f47b2ec66 | |
|
97b1ce15ab | |
|
04ed52754c | |
|
3a5c4efecd | |
|
fdf33addc1 | |
|
b5510ffa13 | |
|
a0b3f0a4f7 | |
|
5d54cf78ec | |
|
3599a40ede | |
|
da8451d405 | |
|
dcf9390870 | |
|
84863cb0ac | |
|
4e7c823094 | |
|
ebe14e0125 | |
|
063ed4c310 | |
|
44c4c60297 | |
|
c6b3deea41 | |
|
b1af49c641 | |
|
b4bf4909fa | |
|
1bf77166d6 | |
|
3d62161aaa | |
|
2f968499f6 | |
|
081f9a9846 | |
|
09d3420387 | |
|
523b4bfc60 | |
|
7e133f2b2b | |
|
07acbc5f87 | |
|
6c8ad465ea | |
|
50755bd1d5 | |
|
d4eec6e5ff | |
|
9f9d73644b | |
|
335f4683fc | |
|
a9dd983f53 | |
|
bb31acb4cc | |
|
f7dcafbd52 | |
|
06f969f43f | |
|
f004de1ebd | |
|
a125252be9 | |
|
e8e68649cd | |
|
984e4fe4db | |
|
d1af610e93 | |
|
fd612b1a66 | |
|
251708e21e | |
|
82eea78c30 | |
|
083b46a94d | |
|
8312132e1f | |
|
e230b3b41d | |
|
44199881cc | |
|
a5617ad012 | |
|
bd14cbcfd0 | |
|
dc99c2d2a7 | |
|
40385bc73c | |
|
fda7d9b154 | |
|
294e85508b | |
|
16c367e770 | |
|
68d5f7f006 | |
|
d2b3c8f772 | |
|
769d55a624 | |
|
7a7c3e8e0d | |
|
a6806c5fa7 | |
|
70319d718f | |
|
adc11df008 | |
|
8e10d135e1 | |
|
b0d24b4799 | |
|
9ab68cc6e2 | |
|
67ebc5b926 | |
|
83c3b3caed | |
|
754dc8ed15 | |
|
2b69ef3bd6 | |
|
b23d1727e8 | |
|
c568d5a8e7 | |
|
5907404f77 | |
|
9b67d6ef43 | |
|
9c004441a8 | |
|
bc8ba38f01 | |
|
858db45e0d | |
|
42b10c63c1 | |
|
1d78965c92 | |
|
b7e0595867 | |
|
a6356e1457 | |
|
d1bf12dd6a | |
|
a296706757 | |
|
7d2c0e4c29 | |
|
20452c83eb | |
|
4ef99a7c98 | |
|
6fd741e7b4 | |
|
b29a3f58f1 | |
|
bae2069661 | |
|
1ae55c3254 | |
|
7e06e60598 | |
|
900cb423b3 | |
|
dddfcd4e60 | |
|
c441edead4 | |
|
114d919141 | |
|
1962beaf8a | |
|
0d888dec62 | |
|
a0bc697edb | |
|
9c121a3027 | |
|
f0e3a34f00 | |
|
e7d99be6c2 | |
|
27bdcbf98b | |
|
1c4343bb48 | |
|
f1ddf20d0c | |
|
56966fd767 | |
|
2d9ccd01b4 | |
|
09f83f6af7 | |
|
0350e0110c | |
|
37aff33b11 | |
|
b4a389e5bd | |
|
8ed2d62a81 | |
|
b0ede36d3b | |
|
024914d993 | |
|
952938a253 | |
|
7698be2bca | |
|
ee37287f9e | |
|
3c9458d15a | |
|
c6a81a8152 | |
|
8d6e4615c7 | |
|
9af35975f5 | |
|
12f7bad764 | |
|
97556caa95 | |
|
805c0026bc | |
|
9669202dbd | |
|
c8c1bdd0bd | |
|
bada1762aa | |
|
218816d1f1 | |
|
9e071814e9 | |
|
4437acdac2 | |
|
7b3e1534eb | |
|
828f711549 | |
|
75357b82c8 | |
|
3e64ae03ba | |
|
7490456b71 | |
|
ecd3980f61 | |
|
2b21a621ae | |
|
e2056524cf | |
|
9401448d0c | |
|
112eb23195 | |
|
c7d33ff502 | |
|
3c1663aeb1 | |
|
7e174c9617 | |
|
17c22c8714 | |
|
c139bd6b05 | |
|
1299a0607f | |
|
3bd923cbbb | |
|
4d757c4379 | |
|
d9f3fd6203 | |
|
0b790543b9 | |
|
24d81ac533 | |
|
22ed91551e | |
|
ab5a4448dc | |
|
fe72fcb5fb | |
|
fe89e6dc37 | |
|
6e739270de | |
|
45bf13e63a | |
|
b81db79237 | |
|
f092754464 | |
|
1494ed50c8 | |
|
bdbb16d3fd | |
|
b2bf6b8c96 | |
|
e3e00f3765 | |
|
b85ed5ad5c | |
|
4e45f1b552 | |
|
e119ce867d | |
|
ab5cf647a1 | |
|
22ee53e9db | |
|
ab2309c958 | |
|
b516103008 | |
|
3a7bff9e74 | |
|
37460cb01d | |
|
c063e0f50f | |
|
8d66494458 | |
|
46dd71889d | |
|
6f226d6da6 | |
|
6e83a9f827 | |
|
898444f1af | |
|
cf2a693281 | |
|
bed68408d8 | |
|
1949186e2a | |
|
c13db7f36e | |
|
5661ec68ca | |
|
b16670939e | |
|
634b7794a0 | |
|
87f436624c | |
|
ed23c4bd23 | |
|
b74f129dbe | |
|
d689583b56 | |
|
d469b93a96 | |
|
e9baede635 | |
|
fd5f4b5f41 | |
|
5b18c24115 | |
|
013536bc9e | |
|
71db1ad9b6 | |
|
98491bc3c8 | |
|
369e09ee86 | |
|
4c45e51493 | |
|
f15920a016 | |
|
1c791df45e | |
|
f51b255209 | |
|
d9d8eddc1d | |
|
101f5dd5e4 | |
|
44fcb23717 | |
|
2631469b71 | |
|
0550c1b25d | |
|
a9afb951e2 | |
|
5bc5a9d23a | |
|
016e5a821a | |
|
4f905778f8 | |
|
faee839580 | |
|
5980f469ec | |
|
7dcb6ecb1c | |
|
c7d6477bf6 | |
|
13cea10946 | |
|
81e20ec9f9 | |
|
3a7eaddcce | |
|
e9b4411ea7 | |
|
e3cf6a85d0 | |
|
aecc16c332 | |
|
aa9b82cda5 | |
|
2eb08d0198 | |
|
7e9e5c638c | |
|
00d9fdf536 | |
|
252f961b73 | |
|
21078a8ab0 | |
|
a6797f8f77 | |
|
398e0ffa57 | |
|
d8dad3b14b |
|
@ -1,2 +1,9 @@
|
||||||
ogWebconsole/.env
|
ogWebconsole/.env
|
||||||
ogWebconsole/test-results/ogGui-junit-report.xml
|
ogWebconsole/test-results/ogGui-junit-report.xml
|
||||||
|
node_modules/
|
||||||
|
### Debian packaging
|
||||||
|
debian/oggui
|
||||||
|
debian/*.substvars
|
||||||
|
debian/*.log
|
||||||
|
debian/.debhelper/
|
||||||
|
debian/files
|
||||||
|
|
94
CHANGELOG.md
94
CHANGELOG.md
|
@ -1,15 +1,97 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
## [0.11.2] - 2025-4-16
|
||||||
|
### Fixed
|
||||||
|
- Se ha corregido un error en la actualizacion del estado de los pcs en la vista tarjetas.
|
||||||
|
|
||||||
|
---
|
||||||
|
## [0.11.1] - 2025-4-16
|
||||||
|
### Improved
|
||||||
|
- Nuevos campos en la tabla de clientes. Tipo de firmware y mac.
|
||||||
|
|
||||||
|
## Fixed
|
||||||
|
- Se ha corregido error al crear OUs, que no refrescaba la web.
|
||||||
|
- Se ha corregido error en el formulario de creacion de imagenes. Si se seleccionaba una imagen para un versionado, no dejaba deseleccionar.
|
||||||
|
- Se ha corregido un bug en el particionador que impedia ejecutar, cuando eliminabamos una particion.
|
||||||
|
|
||||||
|
---
|
||||||
|
## [0.11.0] - 2025-4-11
|
||||||
|
### Added
|
||||||
|
- Se ha diseñado el nuevo formulario para poder ejecutar script. Sistema mejorado con variables etiquetadas.
|
||||||
|
- Se puede añadir descripcion a una imagen.
|
||||||
|
- Se han añadido al formulario de crear/editar repositorio, la posibilidad de añadir usuario y puerto ssh.
|
||||||
|
- Nuevo estado en pc => desconectado.
|
||||||
|
- Se ha añadido nueva accion para renombrar imagen monolitica.
|
||||||
|
|
||||||
|
### Improved
|
||||||
|
- Se ha mejorado la interfaz de usuario tanto para el despliegue de imagenes, como el particionado.
|
||||||
|
- Se ha mejorado la responsividad de la vista de grupos.
|
||||||
|
- Cambios en el comportamiento general de muchos componentes modales. Se han añadido spinners de carga mas intuitivos.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## [0.10.1] - 2025-3-27
|
||||||
|
### Improved
|
||||||
|
- Mejoras en el comportamiento del arbol de grupos.
|
||||||
|
- Nueva regexp para controlar las "macs" en la creacion de clientes.
|
||||||
|
|
||||||
|
---
|
||||||
|
## [0.10.0] - 2025-3-25
|
||||||
|
### Added
|
||||||
|
- Nuevo componenten de estado global.
|
||||||
|
- Servicio para que el ogGui obtenga de forma dinamica las variables de entorno.
|
||||||
|
- Nueva funcionalidad para convertir imagen en imagen virtual.
|
||||||
|
- Nueva funcionalidad para importar imágenes externas al sistema.
|
||||||
|
- Despliegue de imangenes sin cache. Cambios en el formulario de "despliegue".
|
||||||
|
|
||||||
|
### Improved
|
||||||
|
- Mejoras en la internacionalización.
|
||||||
|
- Nueva UX ogRepository. Ahora se gestionan las imagenes de forma mas sencilla.
|
||||||
|
- Cambios en ogLive. Mejora en la sincronizacion y obtención de datos en la API
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Cambios en la expresion regular para la validacion de documentos DHCP en la carga masiva de pc.
|
||||||
|
|
||||||
|
---
|
||||||
|
## [0.9.2] - 2025-03-19
|
||||||
|
### Changed
|
||||||
|
- Jenkinsfile to pubilsh packages in repo in case og release
|
||||||
|
|
||||||
|
---
|
||||||
|
## [0.9.1] - 2025-03-12
|
||||||
|
### Changed
|
||||||
|
- Se ha modificado el acceso a Mercure añadiendo nueva variable de entorno.
|
||||||
|
|
||||||
|
---
|
||||||
|
## [0.9.0] - 2025-3-4
|
||||||
|
### Added
|
||||||
|
- Integracion con Mercure. Subscriber tanto en "Trazas" con en "Clientes".
|
||||||
|
- Nueva funcionalidad para checkear la integridad de una imagen. Boton en apartado "imagenes" dentro del repositorio.
|
||||||
|
- Centralizacion de estilos.
|
||||||
|
- Nueva funcionalidad para realizar backup de imágenes.
|
||||||
|
- Botón para cancelar despliegues de imagenes. Aparece en "trazas" tan solo para los comendos "deploy" y para el estado "en progreso".
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Nueva interfaz en "Grupos". Se ha aprovechado mejor el espacio y acortado el tamaño de las filas, para poder tener mas elementos por pantalla.
|
||||||
|
- Cambios en filtros de "Grupos". Ahora se pueden filtrar por "Centro" y "Unidad Organizativa" y estado. Ahora se busca en base de datos, y no en una lista de clientes dados.
|
||||||
|
- Refactorizados compontentes de crear/editar clientes en uno solo.
|
||||||
|
- Cambios en DHCP. Nueva UX en "ver clientes". Ahora tenemos un buscador detallado.
|
||||||
|
- Para gestionar/añadir clientes a subredes ahora tenemos un botón para "añadir todos" y tan solo nos aparecn los equipos que no estén previamente asignados en una subred.
|
||||||
|
|
||||||
|
---
|
||||||
|
## [0.7.0] - 2024-12-10
|
||||||
|
### Refactored
|
||||||
|
- Refactored the group screen, removing the separate tabs for clients, advanced search, and organizational units.
|
||||||
|
- Added support for partitioning functionality in the client detail view.
|
||||||
|
|
||||||
|
---
|
||||||
## [0.6.1] - 2024-11-19
|
## [0.6.1] - 2024-11-19
|
||||||
|
|
||||||
### Improved
|
### Improved
|
||||||
- Introduced a new automatic sync mode for the ogdhcp and ogBoot components.
|
- Introduced a new automatic sync mode for the ogdhcp and ogBoot components.
|
||||||
- Improve test coverage.
|
- Improve test coverage.
|
||||||
- New view for clients inside the classroom on the main page.
|
- New view for clients inside the classroom on the main page.
|
||||||
|
|
||||||
|
---
|
||||||
## [0.6.0] - 2024-11-19
|
## [0.6.0] - 2024-11-19
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
- Added functionality to execute actions from the menu in the general groups screen.
|
- Added functionality to execute actions from the menu in the general groups screen.
|
||||||
- Displayed the selected center on the general screen for better context.
|
- Displayed the selected center on the general screen for better context.
|
||||||
|
@ -30,12 +112,8 @@
|
||||||
- Made predefined commands read-only to prevent accidental modifications.
|
- Made predefined commands read-only to prevent accidental modifications.
|
||||||
- Simplified the task creation modal to enhance user experience.
|
- Simplified the task creation modal to enhance user experience.
|
||||||
- Adjusted the translation system to cover new elements and improve consistency (work in progress).
|
- Adjusted the translation system to cover new elements and improve consistency (work in progress).
|
||||||
- New element view from clients on groups main view
|
- New element view from clients on groups main view.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Resolved an issue that prevented editing software profiles correctly.
|
- Resolved an issue that prevented editing software profiles correctly.
|
||||||
- Fixed a bug where newly created commands failed to execute in the commands section.
|
- Fixed a bug where newly created commands failed to execute in the commands section.
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,107 @@
|
||||||
|
@Library('jenkins-shared-library') _
|
||||||
|
pipeline {
|
||||||
|
agent {
|
||||||
|
label 'jenkins-slave'
|
||||||
|
}
|
||||||
|
environment {
|
||||||
|
DEBIAN_FRONTEND = 'noninteractive'
|
||||||
|
DEFAULT_DEV_NAME = 'Opengnsys Team'
|
||||||
|
DEFAULT_DEV_EMAIL = 'opengnsys@qindel.com'
|
||||||
|
}
|
||||||
|
options {
|
||||||
|
skipDefaultCheckout()
|
||||||
|
}
|
||||||
|
parameters {
|
||||||
|
string(name: 'DEV_NAME', defaultValue: '', description: 'Nombre del desarrollador')
|
||||||
|
string(name: 'DEV_EMAIL', defaultValue: '', description: 'Email del desarrollador')
|
||||||
|
}
|
||||||
|
stages {
|
||||||
|
stage('Prepare Workspace') {
|
||||||
|
steps {
|
||||||
|
script {
|
||||||
|
env.BUILD_DIR = "${WORKSPACE}/oggui"
|
||||||
|
sh "mkdir -p ${env.BUILD_DIR}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('Checkout') {
|
||||||
|
steps {
|
||||||
|
dir("${env.BUILD_DIR}") {
|
||||||
|
checkout scm
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('Generate Changelog') {
|
||||||
|
when {
|
||||||
|
expression {
|
||||||
|
return env.TAG_NAME != null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
steps {
|
||||||
|
script {
|
||||||
|
def devName = params.DEV_NAME ? params.DEV_NAME : env.DEFAULT_DEV_NAME
|
||||||
|
def devEmail = params.DEV_EMAIL ? params.DEV_EMAIL : env.DEFAULT_DEV_EMAIL
|
||||||
|
|
||||||
|
generateDebianChangelog(env.BUILD_DIR, devName, devEmail)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Generate Changelog (Nightly)'){
|
||||||
|
when {
|
||||||
|
branch 'main'
|
||||||
|
}
|
||||||
|
steps {
|
||||||
|
script {
|
||||||
|
def devName = params.DEV_NAME ? params.DEV_NAME : env.DEFAULT_DEV_NAME
|
||||||
|
def devEmail = params.DEV_EMAIL ? params.DEV_EMAIL : env.DEFAULT_DEV_EMAIL
|
||||||
|
generateDebianChangelog(env.BUILD_DIR, devName, devEmail,"nightly")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
stage('Build') {
|
||||||
|
steps {
|
||||||
|
script {
|
||||||
|
construirPaquete(env.BUILD_DIR, "../artifacts", "172.17.8.68", "/var/tmp/opengnsys/debian-repo/oggui")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage ('Publish to Debian Repository') {
|
||||||
|
when {
|
||||||
|
expression {
|
||||||
|
return env.TAG_NAME != null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
agent { label 'debian-repo' }
|
||||||
|
steps {
|
||||||
|
script {
|
||||||
|
// Construir el patrón de versión esperado en el nombre del paquete
|
||||||
|
def versionPattern = "${env.TAG_NAME}-${env.BUILD_NUMBER}"
|
||||||
|
publicarEnAptly('/var/tmp/opengnsys/debian-repo/oggui', 'opengnsys-devel', versionPattern)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage ('Publish to Debian Repository (Nightly)') {
|
||||||
|
when {
|
||||||
|
branch 'main'
|
||||||
|
}
|
||||||
|
agent { label 'debian-repo' }
|
||||||
|
steps {
|
||||||
|
script {
|
||||||
|
// Construir el patrón de versión esperado en el nombre del paquete
|
||||||
|
def versionPattern = "-${env.BUILD_NUMBER}~nightly"
|
||||||
|
publicarEnAptly('/var/tmp/opengnsys/debian-repo/oggui', 'nightly', versionPattern)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
post {
|
||||||
|
always {
|
||||||
|
notifyBuildStatus('narenas@qindel.com')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
CONFIG_FILE="/opt/opengnsys/oggui/src/.env"
|
||||||
|
HASH_FILE="/opt/opengnsys/oggui/var/lib/oggui/oggui.config.hash"
|
||||||
|
APP_DIR="/opt/opengnsys/oggui/browser"
|
||||||
|
SRC_DIR="/opt/opengnsys/oggui/src"
|
||||||
|
COMPILED_DIR=$SRC_DIR/dist
|
||||||
|
NGINX_SERVICE="nginx"
|
||||||
|
|
||||||
|
# Verificar si el archivo de configuración cambió
|
||||||
|
if [ -f "$CONFIG_FILE" ] && [ -f "$HASH_FILE" ]; then
|
||||||
|
OLD_HASH=$(cat "$HASH_FILE")
|
||||||
|
NEW_HASH=$(md5sum "$CONFIG_FILE")
|
||||||
|
|
||||||
|
if [ "$OLD_HASH" != "$NEW_HASH" ]; then
|
||||||
|
echo "🔄 Cambios detectados en $CONFIG_FILE, recompilando Angular..."
|
||||||
|
cd "$SRC_DIR"
|
||||||
|
npm install -g @angular/cli
|
||||||
|
npm install
|
||||||
|
/usr/local/bin/ng build --base-href=/ --output-path=dist/oggui --optimization=true --configuration=production --localize=false
|
||||||
|
md5sum "$CONFIG_FILE" > "$HASH_FILE"
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
echo "No hay cambios en $CONFIG_FILE, no es necesario recompilar."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "Archivo de configuración no encontrado o sin hash previo. No se recompilará."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Iniciar Nginx
|
||||||
|
systemctl restart "$NGINX_SERVICE"
|
|
@ -0,0 +1,14 @@
|
||||||
|
oggui (0.0.1-1) unstable; urgency=medium
|
||||||
|
|
||||||
|
* Add debian files
|
||||||
|
* Update .gitignore
|
||||||
|
* refs #1637 refactor: remove unused client edit and create components; add manage client component
|
||||||
|
* refs #1619. Style: enhance cards view layout and paginator integration in groups component
|
||||||
|
* refactor: update paginator settings and improve page change handling in groups component
|
||||||
|
* Merge branch 'develop' of ssh://ognproject.evlt.uma.es:21987/opengnsys/oggui into develop
|
||||||
|
* style: clean up and optimize CSS for groups component; enhance HTML structure and improve responsiveness
|
||||||
|
* Updated groups paginator
|
||||||
|
* Merge branch 'develop' of ssh://ognproject.evlt.uma.es:21987/opengnsys/oggui into develop
|
||||||
|
* refs #1567. New subnet field: 'dns'
|
||||||
|
|
||||||
|
-- Tu Nombre <tuemail@example.com> Mon, 10 Mar 2025 14:48:36 +0000
|
|
@ -0,0 +1 @@
|
||||||
|
12
|
|
@ -0,0 +1,13 @@
|
||||||
|
Source: oggui
|
||||||
|
Section: web
|
||||||
|
Priority: optional
|
||||||
|
Maintainer: Nicolas Arenas <nicolas.arenas@qindel.com>
|
||||||
|
Build-Depends: debhelper (>= 12), nodejs, npm
|
||||||
|
Standards-Version: 4.5.0
|
||||||
|
|
||||||
|
Package: oggui
|
||||||
|
Architecture: any
|
||||||
|
Maintainer: Nicolas Arenas <nicolas.arenas@qindel.com>
|
||||||
|
Depends: ${shlibs:Depends}, ${misc:Depends}, nginx
|
||||||
|
Description: OpenGnsys GUI created for the Opengnsys Team
|
||||||
|
Opengnsys Graphical Intercface
|
|
@ -0,0 +1 @@
|
||||||
|
oggui
|
|
@ -0,0 +1,2 @@
|
||||||
|
oggui_1.0.1+deb-pkg20250310-1_amd64.buildinfo web optional
|
||||||
|
oggui_1.0.1+deb-pkg20250310-1_amd64.deb web optional
|
|
@ -0,0 +1,10 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
. /usr/share/debconf/confmodule
|
||||||
|
|
||||||
|
db_input high opengnsys/oggui_ogcoreUrl || true
|
||||||
|
db_input high opengnsys/oggui_ogmercureUrl || true
|
||||||
|
|
||||||
|
db_go
|
|
@ -0,0 +1,4 @@
|
||||||
|
ogWebconsole/dist/oggui/browser /opt/opengnsys/oggui/
|
||||||
|
etc /opt/opengnsys/oggui/
|
||||||
|
ogWebconsole/ssl/* /opt/opengnsys/oggui/etc/nginx/certs/
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
. /usr/share/debconf/confmodule
|
||||||
|
|
||||||
|
db_get opengnsys/oggui_ogcoreUrl
|
||||||
|
OGCORE_URL="$RET"
|
||||||
|
db_get opengnsys/oggui_ogmercureUrl
|
||||||
|
OGMERCURE_URL="$RET"
|
||||||
|
|
||||||
|
# Asegurarse de que el usuario exista
|
||||||
|
USER="opengnsys"
|
||||||
|
CONFIG_FILE="/opt/opengnsys/oggui/browser/assets/config.json"
|
||||||
|
|
||||||
|
restore_config_if_modified() {
|
||||||
|
local new="$1"
|
||||||
|
local backup="$1.bak"
|
||||||
|
|
||||||
|
if [ -f "$backup" ]; then
|
||||||
|
if ! cmp -s "$new" "$backup"; then
|
||||||
|
echo ">>> Archivo modificado por el usuario detectado en $new"
|
||||||
|
echo " - Guardando archivo nuevo como ${new}.new"
|
||||||
|
mv -f "$new" "${new}.new"
|
||||||
|
echo " - Restaurando archivo anterior desde backup"
|
||||||
|
mv -f "$backup" "$new"
|
||||||
|
else
|
||||||
|
echo ">>> El archivo $new no ha cambiado desde la última versión, eliminando backup"
|
||||||
|
rm -f "$backup"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Detectar si es una instalación nueva o una actualización
|
||||||
|
if [ "$1" = "configure" ] && [ -z "$2" ]; then
|
||||||
|
if [ ! -f "$CONFIG_FILE" ]; then
|
||||||
|
jq --arg apiUrl "$OGCORE_URL" --arg mercureUrl "$OGMERCURE_URL" \
|
||||||
|
'.apiUrl = $apiUrl | .mercureUrl = $mercureUrl' "$CONFIG_FILE" > "${CONFIG_FILE}.tmp" && mv "${CONFIG_FILE}.tmp" "$CONFIG_FILE"
|
||||||
|
fi
|
||||||
|
ln -s /opt/opengnsys/oggui/etc/nginx/oggui.conf /etc/nginx/sites-enabled/oggui.conf
|
||||||
|
ln -s $CONFIG_FILE /opt/opengnsys/oggui/etc/config.json
|
||||||
|
mkdir -p /etc/nginx/certs/
|
||||||
|
cp -p /opt/opengnsys/oggui/etc/nginx/certs/* /etc/nginx/certs/
|
||||||
|
chown -R www-data:www-data /etc/nginx/certs
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl restart nginx
|
||||||
|
elif [ "$1" = "configure" ] && [ -n "$2" ]; then
|
||||||
|
cd /opt/opengnsys/oggui
|
||||||
|
echo "Actualización desde la versión $2"
|
||||||
|
# Si upgrade recupero los archivos de configuracion
|
||||||
|
echo ">>> Backup de archivos de configuración reales en /opt/opengnsys"
|
||||||
|
restore_config_if_modified "/opt/opengnsys/oggui/etc/nginx/oggui.conf"
|
||||||
|
restore_config_if_modified "$CONFIG_FILE"
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Cambiar la propiedad de los archivos al usuario especificado
|
||||||
|
chown opengnsys:www-data /opt/opengnsys/
|
||||||
|
chown -R opengnsys:www-data /opt/opengnsys/oggui
|
||||||
|
exit 0
|
|
@ -0,0 +1,32 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
|
||||||
|
NGINX_FILE="/etc/nginx/sites-enabled/oggui.conf"
|
||||||
|
UNIT_FILE="/etc/systemd/system/oggui.service"
|
||||||
|
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
remove)
|
||||||
|
echo "El paquete se está desinstalando..."
|
||||||
|
# Aquí puedes hacer limpieza de archivos o servicios
|
||||||
|
if [ -L "$NGINX_FILE" ]; then
|
||||||
|
rm -f "$NGINX_FILE"
|
||||||
|
systemctl restart nginx
|
||||||
|
fi
|
||||||
|
if [ -L "$UNIT_FILE" ]; then
|
||||||
|
rm -f "$UNIT_FILE"
|
||||||
|
systemctl daemon-reload
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
purge)
|
||||||
|
echo "Eliminando configuración residual..."
|
||||||
|
;;
|
||||||
|
|
||||||
|
upgrade)
|
||||||
|
echo "Actualizando paquete..."
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
exit 0
|
|
@ -0,0 +1,6 @@
|
||||||
|
# Automatically added by dh_installdebconf/13.14.1ubuntu5
|
||||||
|
if [ "$1" = purge ] && [ -e /usr/share/debconf/confmodule ]; then
|
||||||
|
. /usr/share/debconf/confmodule
|
||||||
|
db_purge
|
||||||
|
fi
|
||||||
|
# End automatically added section
|
|
@ -0,0 +1,32 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
backup_file_if_exists() {
|
||||||
|
local original="$1"
|
||||||
|
local backup="$1.bak"
|
||||||
|
|
||||||
|
if [ -e "$original" ]; then
|
||||||
|
echo " - Guardando backup de $original en $backup"
|
||||||
|
cp -a "$original" "$backup"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
CONFIG_FILE="/opt/opengnsys/oggui/browser/assets/config.json"
|
||||||
|
# Asegurarse de que el usuario exista
|
||||||
|
USER="opengnsys"
|
||||||
|
HOME_DIR="/opt/opengnsys"
|
||||||
|
if id "$USER" &>/dev/null; then
|
||||||
|
echo "El usuario $USER ya existe."
|
||||||
|
else
|
||||||
|
echo "Creando el usuario $USER con home en $HOME_DIR."
|
||||||
|
useradd -m -d "$HOME_DIR" -s /bin/bash "$USER"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Si upgrade hago backup del archivo de configuración
|
||||||
|
if [ "$1" = "upgrade" ]; then
|
||||||
|
echo ">>> Backup de archivos de configuración reales en /opt/opengnsys"
|
||||||
|
backup_file_if_exists "/opt/opengnsys/oggui/etc/nginx/sites-available/oggui.conf"
|
||||||
|
backup_file_if_exists "$CONFIG_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 0
|
|
@ -0,0 +1,13 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
set -x
|
||||||
|
|
||||||
|
# Solo eliminar archivos de configuración si se está eliminando el paquete
|
||||||
|
if [ "$1" = "remove" ] || [ "$1" = "purge" ]; then
|
||||||
|
rm -f /etc/nginx/sites-enabled/oggui.conf
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl restart nginx
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 0
|
|
@ -0,0 +1,9 @@
|
||||||
|
Template: opengnsys/oggui_ogcoreUrl
|
||||||
|
Type: string
|
||||||
|
Default: https://127.0.0.1:8443
|
||||||
|
Description: Introduzca la URL delAPI de OgCore
|
||||||
|
|
||||||
|
Template: opengnsys/oggui_ogmercureUrl
|
||||||
|
Type: string
|
||||||
|
Default: https://127.0.0.1:3000/.well-known/mercure
|
||||||
|
Description: Introduzca el endpoint de mercure
|
|
@ -0,0 +1,11 @@
|
||||||
|
#!/usr/bin/make -f
|
||||||
|
|
||||||
|
%:
|
||||||
|
dh $@
|
||||||
|
|
||||||
|
override_dh_auto_build:
|
||||||
|
cd ogWebconsole && npm install
|
||||||
|
cd ogWebconsole && npx ng build --base-href=/ --output-path=dist/oggui --optimization=true --configuration=production
|
||||||
|
|
||||||
|
override_dh_auto_install:
|
||||||
|
dh_auto_install
|
|
@ -0,0 +1,20 @@
|
||||||
|
server {
|
||||||
|
listen 4200 ssl;
|
||||||
|
server_name localhost;
|
||||||
|
|
||||||
|
root /opt/opengnsys/oggui/browser;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Manejo de archivos estáticos
|
||||||
|
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
|
||||||
|
try_files $uri =404;
|
||||||
|
}
|
||||||
|
ssl_certificate /opt/opengnsys/oggui/etc/nginx/certs/oggui.uds-test.net.crt.pem;
|
||||||
|
ssl_certificate_key /opt/opengnsys/oggui/etc/nginx/certs/oggui.uds-test.net.key.pem;
|
||||||
|
# Configuración para evitar problemas con rutas de Angular
|
||||||
|
error_page 404 /index.html;
|
||||||
|
}
|
|
@ -1 +1,2 @@
|
||||||
NG_APP_BASE_API_URL=https://127.0.0.1:8443
|
# NG_APP_BASE_API_URL=https://127.0.0.1:8443
|
||||||
|
# NG_APP_OGCORE_MERCURE_BASE_URL=http://localhost:3000/.well-known/mercure
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
NG_APP_BASE_API_URL=https://localhost:8443
|
||||||
|
NG_APP_OGCORE_MERCURE_BASE_URL=http://localhost:3000/.well-known/mercure
|
|
@ -41,3 +41,5 @@ testem.log
|
||||||
.DS_Store
|
.DS_Store
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
|
|
||||||
|
test-results/
|
||||||
|
|
||||||
|
|
|
@ -4,12 +4,6 @@
|
||||||
"newProjectRoot": "projects",
|
"newProjectRoot": "projects",
|
||||||
"projects": {
|
"projects": {
|
||||||
"ogWebconsole": {
|
"ogWebconsole": {
|
||||||
"i18n": {
|
|
||||||
"sourceLocale": "es",
|
|
||||||
"locales": {
|
|
||||||
"en-US": "src/locale/en.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"projectType": "application",
|
"projectType": "application",
|
||||||
"schematics": {
|
"schematics": {
|
||||||
"@schematics/angular:component": {
|
"@schematics/angular:component": {
|
||||||
|
@ -30,7 +24,7 @@
|
||||||
"builder": "@ngx-env/builder:application",
|
"builder": "@ngx-env/builder:application",
|
||||||
"options": {
|
"options": {
|
||||||
"baseHref": "/oggui/",
|
"baseHref": "/oggui/",
|
||||||
"localize": true,
|
"localize": false,
|
||||||
"aot": true,
|
"aot": true,
|
||||||
"outputPath": "dist/og-webconsole",
|
"outputPath": "dist/og-webconsole",
|
||||||
"index": "src/index.html",
|
"index": "src/index.html",
|
||||||
|
@ -41,20 +35,23 @@
|
||||||
],
|
],
|
||||||
"tsConfig": "tsconfig.app.json",
|
"tsConfig": "tsconfig.app.json",
|
||||||
"assets": [
|
"assets": [
|
||||||
"src/favicon.ico",
|
"src/favicon.ico",
|
||||||
"src/assets",
|
"src/assets",
|
||||||
{
|
{
|
||||||
"glob": "**/*",
|
"glob": "**/*",
|
||||||
"input": "src/locale",
|
"input": "src/locale",
|
||||||
"output": "/locale"
|
"output": "/locale"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"styles": [
|
"styles": [
|
||||||
"src/custom-theme.scss",
|
"src/custom-theme.scss",
|
||||||
"src/styles.css",
|
"src/styles.css",
|
||||||
"node_modules/ngx-toastr/toastr.css"
|
"node_modules/ngx-toastr/toastr.css"
|
||||||
],
|
],
|
||||||
"scripts": []
|
"scripts": [],
|
||||||
|
"allowedCommonJsDependencies": [
|
||||||
|
"rfdc"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"configurations": {
|
"configurations": {
|
||||||
"production": {
|
"production": {
|
||||||
|
@ -66,8 +63,8 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "anyComponentStyle",
|
"type": "anyComponentStyle",
|
||||||
"maximumWarning": "2kb",
|
"maximumWarning": "7kb",
|
||||||
"maximumError": "4kb"
|
"maximumError": "10kb"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"outputHashing": "all"
|
"outputHashing": "all"
|
||||||
|
@ -76,16 +73,6 @@
|
||||||
"optimization": false,
|
"optimization": false,
|
||||||
"extractLicenses": false,
|
"extractLicenses": false,
|
||||||
"sourceMap": false
|
"sourceMap": false
|
||||||
},
|
|
||||||
"es": {
|
|
||||||
"localize": [
|
|
||||||
"es-ES"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"en": {
|
|
||||||
"localize": [
|
|
||||||
"en-US"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"defaultConfiguration": "production"
|
"defaultConfiguration": "production"
|
||||||
|
@ -104,29 +91,16 @@
|
||||||
},
|
},
|
||||||
"development": {
|
"development": {
|
||||||
"buildTarget": "ogWebconsole:build:development"
|
"buildTarget": "ogWebconsole:build:development"
|
||||||
},
|
|
||||||
"es": {
|
|
||||||
"buildTarget": "ogWebconsole:build:es"
|
|
||||||
},
|
|
||||||
"en": {
|
|
||||||
"buildTarget": "ogWebconsole:build:en"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"defaultConfiguration": "development"
|
"defaultConfiguration": "development"
|
||||||
},
|
},
|
||||||
"extract-i18n": {
|
|
||||||
"builder": "@ngx-env/builder:extract-i18n",
|
|
||||||
"options": {
|
|
||||||
"buildTarget": "ogWebconsole:build"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"test": {
|
"test": {
|
||||||
"builder": "@ngx-env/builder:karma",
|
"builder": "@ngx-env/builder:karma",
|
||||||
"options": {
|
"options": {
|
||||||
"polyfills": [
|
"polyfills": [
|
||||||
"zone.js",
|
"zone.js",
|
||||||
"zone.js/testing",
|
"zone.js/testing"
|
||||||
"@angular/localize/init"
|
|
||||||
],
|
],
|
||||||
"tsConfig": "tsconfig.spec.json",
|
"tsConfig": "tsconfig.spec.json",
|
||||||
"assets": [
|
"assets": [
|
||||||
|
@ -134,7 +108,8 @@
|
||||||
"src/assets"
|
"src/assets"
|
||||||
],
|
],
|
||||||
"styles": [
|
"styles": [
|
||||||
"src/styles.css"
|
"src/styles.css",
|
||||||
|
"src/custom-theme.scss"
|
||||||
],
|
],
|
||||||
"scripts": []
|
"scripts": []
|
||||||
}
|
}
|
||||||
|
@ -145,4 +120,4 @@
|
||||||
"cli": {
|
"cli": {
|
||||||
"analytics": "95fac95c-8936-41a8-8c9c-1fae82fe6912"
|
"analytics": "95fac95c-8936-41a8-8c9c-1fae82fe6912"
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -24,6 +24,7 @@
|
||||||
"jwt-decode": "^4.0.0",
|
"jwt-decode": "^4.0.0",
|
||||||
"ngx-joyride": "^2.5.0",
|
"ngx-joyride": "^2.5.0",
|
||||||
"ngx-toastr": "^19.0.0",
|
"ngx-toastr": "^19.0.0",
|
||||||
|
"papaparse": "^5.4.1",
|
||||||
"rxjs": "~7.8.0",
|
"rxjs": "~7.8.0",
|
||||||
"tslib": "^2.3.0",
|
"tslib": "^2.3.0",
|
||||||
"zone.js": "^0.14.6"
|
"zone.js": "^0.14.6"
|
||||||
|
@ -35,6 +36,7 @@
|
||||||
"@angular/localize": "^18.1.0",
|
"@angular/localize": "^18.1.0",
|
||||||
"@ngx-env/builder": "^18.0.1",
|
"@ngx-env/builder": "^18.0.1",
|
||||||
"@types/jasmine": "~5.1.0",
|
"@types/jasmine": "~5.1.0",
|
||||||
|
"@types/papaparse": "^5.3.15",
|
||||||
"jasmine-core": "~5.1.0",
|
"jasmine-core": "~5.1.0",
|
||||||
"karma": "~6.4.0",
|
"karma": "~6.4.0",
|
||||||
"karma-chrome-launcher": "~3.2.0",
|
"karma-chrome-launcher": "~3.2.0",
|
||||||
|
@ -5902,6 +5904,15 @@
|
||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/papaparse": {
|
||||||
|
"version": "5.3.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/papaparse/-/papaparse-5.3.15.tgz",
|
||||||
|
"integrity": "sha512-JHe6vF6x/8Z85nCX4yFdDslN11d+1pr12E526X8WAfhadOeaOTx5AuIkvDKIBopfvlzpzkdMx4YyvSKCM9oqtw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/qs": {
|
"node_modules/@types/qs": {
|
||||||
"version": "6.9.15",
|
"version": "6.9.15",
|
||||||
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.15.tgz",
|
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.15.tgz",
|
||||||
|
@ -11955,6 +11966,11 @@
|
||||||
"node": "^16.14.0 || >=18.0.0"
|
"node": "^16.14.0 || >=18.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/papaparse": {
|
||||||
|
"version": "5.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.4.1.tgz",
|
||||||
|
"integrity": "sha512-HipMsgJkZu8br23pW15uvo6sib6wne/4woLZPlFf3rpDyMe9ywEXUsuD7+6K9PRkJlVT51j/sCOYDKGGS3ZJrw=="
|
||||||
|
},
|
||||||
"node_modules/parent-module": {
|
"node_modules/parent-module": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
"jwt-decode": "^4.0.0",
|
"jwt-decode": "^4.0.0",
|
||||||
"ngx-joyride": "^2.5.0",
|
"ngx-joyride": "^2.5.0",
|
||||||
"ngx-toastr": "^19.0.0",
|
"ngx-toastr": "^19.0.0",
|
||||||
|
"papaparse": "^5.4.1",
|
||||||
"rxjs": "~7.8.0",
|
"rxjs": "~7.8.0",
|
||||||
"tslib": "^2.3.0",
|
"tslib": "^2.3.0",
|
||||||
"zone.js": "^0.14.6"
|
"zone.js": "^0.14.6"
|
||||||
|
@ -37,6 +38,7 @@
|
||||||
"@angular/localize": "^18.1.0",
|
"@angular/localize": "^18.1.0",
|
||||||
"@ngx-env/builder": "^18.0.1",
|
"@ngx-env/builder": "^18.0.1",
|
||||||
"@types/jasmine": "~5.1.0",
|
"@types/jasmine": "~5.1.0",
|
||||||
|
"@types/papaparse": "^5.3.15",
|
||||||
"jasmine-core": "~5.1.0",
|
"jasmine-core": "~5.1.0",
|
||||||
"karma": "~6.4.0",
|
"karma": "~6.4.0",
|
||||||
"karma-chrome-launcher": "~3.2.0",
|
"karma-chrome-launcher": "~3.2.0",
|
||||||
|
|
|
@ -13,14 +13,11 @@ import { PXEimagesComponent } from './components/ogboot/pxe-images/pxe-images.co
|
||||||
import { PxeComponent } from './components/ogboot/pxe/pxe.component';
|
import { PxeComponent } from './components/ogboot/pxe/pxe.component';
|
||||||
import { PxeBootFilesComponent } from './components/ogboot/pxe-boot-files/pxe-boot-files.component';
|
import { PxeBootFilesComponent } from './components/ogboot/pxe-boot-files/pxe-boot-files.component';
|
||||||
import {OgbootStatusComponent} from "./components/ogboot/ogboot-status/ogboot-status.component";
|
import {OgbootStatusComponent} from "./components/ogboot/ogboot-status/ogboot-status.component";
|
||||||
import { OgdhcpComponent } from './components/ogdhcp/ogdhcp.component';
|
|
||||||
import { OgDhcpSubnetsComponent } from './components/ogdhcp/og-dhcp-subnets/og-dhcp-subnets.component';
|
|
||||||
import { CalendarComponent } from "./components/calendar/calendar.component";
|
import { CalendarComponent } from "./components/calendar/calendar.component";
|
||||||
import { CommandsComponent } from './components/commands/main-commands/commands.component';
|
import { CommandsComponent } from './components/commands/main-commands/commands.component';
|
||||||
import { CommandsGroupsComponent } from './components/commands/commands-groups/commands-groups.component';
|
import { CommandsGroupsComponent } from './components/commands/commands-groups/commands-groups.component';
|
||||||
import { CommandsTaskComponent } from './components/commands/commands-task/commands-task.component';
|
import { CommandsTaskComponent } from './components/commands/commands-task/commands-task.component';
|
||||||
import { TaskLogsComponent } from './components/commands/commands-task/task-logs/task-logs.component';
|
import { TaskLogsComponent } from './components/commands/commands-task/task-logs/task-logs.component';
|
||||||
import { StatusComponent } from "./components/ogdhcp/og-dhcp-subnets/status/status.component";
|
|
||||||
import { ClientMainViewComponent } from './components/groups/components/client-main-view/client-main-view.component';
|
import { ClientMainViewComponent } from './components/groups/components/client-main-view/client-main-view.component';
|
||||||
import { ImagesComponent } from './components/images/images.component';
|
import { ImagesComponent } from './components/images/images.component';
|
||||||
import {SoftwareComponent} from "./components/software/software.component";
|
import {SoftwareComponent} from "./components/software/software.component";
|
||||||
|
@ -31,7 +28,7 @@ import {
|
||||||
} from "./components/groups/components/client-main-view/partition-assistant/partition-assistant.component";
|
} from "./components/groups/components/client-main-view/partition-assistant/partition-assistant.component";
|
||||||
import {RepositoriesComponent} from "./components/repositories/repositories.component";
|
import {RepositoriesComponent} from "./components/repositories/repositories.component";
|
||||||
import {
|
import {
|
||||||
CreateImageComponent
|
CreateClientImageComponent
|
||||||
} from "./components/groups/components/client-main-view/create-image/create-image.component";
|
} from "./components/groups/components/client-main-view/create-image/create-image.component";
|
||||||
import {
|
import {
|
||||||
DeployImageComponent
|
DeployImageComponent
|
||||||
|
@ -40,50 +37,54 @@ import {
|
||||||
MainRepositoryViewComponent
|
MainRepositoryViewComponent
|
||||||
} from "./components/repositories/main-repository-view/main-repository-view.component";
|
} from "./components/repositories/main-repository-view/main-repository-view.component";
|
||||||
import {EnvVarsComponent} from "./components/admin/env-vars/env-vars.component";
|
import {EnvVarsComponent} from "./components/admin/env-vars/env-vars.component";
|
||||||
|
import {MenusComponent} from "./components/menus/menus.component";
|
||||||
|
import {OgDhcpSubnetsComponent} from "./components/ogdhcp/og-dhcp-subnets.component";
|
||||||
|
import {StatusComponent} from "./components/ogdhcp/status/status.component";
|
||||||
|
import {
|
||||||
|
RunScriptAssistantComponent
|
||||||
|
} from "./components/groups/components/client-main-view/run-script-assistant/run-script-assistant.component";
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{ path: '', redirectTo: 'auth/login', pathMatch: 'full' },
|
{ path: '', redirectTo: 'auth/login', pathMatch: 'full' },
|
||||||
{
|
{ path: '', component: MainLayoutComponent,
|
||||||
path: '',
|
children: [
|
||||||
component: MainLayoutComponent,
|
{ path: 'dashboard', component: DashboardComponent },
|
||||||
children: [
|
{ path: 'admin', component: AdminComponent },
|
||||||
{ path: 'dashboard', component: DashboardComponent },
|
{ path: 'users', component: UsersComponent },
|
||||||
{ path: 'admin', component: AdminComponent },
|
{ path: 'env-vars', component: EnvVarsComponent },
|
||||||
{ path: 'users', component: UsersComponent },
|
{ path: 'user-groups', component: RolesComponent },
|
||||||
{ path: 'env-vars', component: EnvVarsComponent },
|
{ path: 'groups', component: GroupsComponent },
|
||||||
{ path: 'user-groups', component: RolesComponent },
|
{ path: 'pxe-images', component: PXEimagesComponent },
|
||||||
{ path: 'groups', component: GroupsComponent },
|
{ path: 'pxe', component: PxeComponent },
|
||||||
{ path: 'pxe-images', component: PXEimagesComponent },
|
{ path: 'pxe-boot-file', component: PxeBootFilesComponent },
|
||||||
{ path: 'pxe', component: PxeComponent },
|
{ path: 'ogboot-status', component: OgbootStatusComponent },
|
||||||
{ path: 'pxe-boot-file', component: PxeBootFilesComponent },
|
{ path: 'subnets', component: OgDhcpSubnetsComponent },
|
||||||
{ path: 'ogboot-status', component: OgbootStatusComponent },
|
{ path: 'ogdhcp-status', component: StatusComponent },
|
||||||
{ path: 'dhcp', component: OgdhcpComponent },
|
{ path: 'commands', component: CommandsComponent },
|
||||||
{ path: 'subnets', component: OgDhcpSubnetsComponent },
|
{ path: 'commands-groups', component: CommandsGroupsComponent },
|
||||||
{ path: 'ogdhcp-status', component: StatusComponent },
|
{ path: 'commands-task', component: CommandsTaskComponent },
|
||||||
{ path: 'commands', component: CommandsComponent },
|
{ path: 'commands-logs', component: TaskLogsComponent },
|
||||||
{ path: 'commands-groups', component: CommandsGroupsComponent },
|
{ path: 'calendars', component: CalendarComponent },
|
||||||
{ path: 'commands-task', component: CommandsTaskComponent },
|
{ path: 'clients/deploy-image', component: DeployImageComponent },
|
||||||
{ path: 'commands-logs', component: TaskLogsComponent },
|
{ path: 'clients/partition-assistant', component: PartitionAssistantComponent },
|
||||||
{ path: 'calendars', component: CalendarComponent },
|
{ path: 'clients/run-script', component: RunScriptAssistantComponent },
|
||||||
{ path: 'clients/:id', component: ClientMainViewComponent },
|
{ path: 'clients/:id', component: ClientMainViewComponent },
|
||||||
{ path: 'clients/:id/partition-assistant', component: PartitionAssistantComponent },
|
{ path: 'clients/:id/create-image', component: CreateClientImageComponent },
|
||||||
{ path: 'clients/:id/create-image', component: CreateImageComponent },
|
{ path: 'repositories', component: RepositoriesComponent },
|
||||||
{ path: 'clients/:id/deploy-image', component: DeployImageComponent },
|
{ path: 'repository/:id', component: MainRepositoryViewComponent },
|
||||||
{ path: 'images', component: ImagesComponent },
|
{ path: 'software', component: SoftwareComponent },
|
||||||
{ path: 'repositories', component: RepositoriesComponent },
|
{ path: 'software-profiles', component: SoftwareProfileComponent },
|
||||||
{ path: 'repository/:id', component: MainRepositoryViewComponent },
|
{ path: 'operative-systems', component: OperativeSystemComponent },
|
||||||
{ path: 'software', component: SoftwareComponent },
|
{ path: 'menus', component: MenusComponent },
|
||||||
{ path: 'software-profiles', component: SoftwareProfileComponent },
|
],
|
||||||
{ path: 'operative-systems', component: OperativeSystemComponent },
|
},
|
||||||
],
|
{
|
||||||
},
|
path: 'auth',
|
||||||
{
|
component: AuthLayoutComponent,
|
||||||
path: 'auth',
|
children: [
|
||||||
component: AuthLayoutComponent,
|
{ path: 'login', component: LoginComponent },
|
||||||
children: [
|
],
|
||||||
{ path: 'login', component: LoginComponent },
|
},
|
||||||
],
|
{ path: '**', component: PageNotFoundComponent },
|
||||||
},
|
|
||||||
{ path: '**', component: PageNotFoundComponent },
|
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
import { NgModule, CUSTOM_ELEMENTS_SCHEMA, LOCALE_ID, APP_INITIALIZER } from '@angular/core';
|
||||||
|
import { ConfigService } from './services/config.service';
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
import { AppRoutingModule } from './app-routing.module';
|
import { AppRoutingModule } from './app-routing.module';
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
|
@ -25,6 +26,7 @@ import { MatListModule } from '@angular/material/list';
|
||||||
import { UsersComponent } from './components/admin/users/users/users.component';
|
import { UsersComponent } from './components/admin/users/users/users.component';
|
||||||
import { RolesComponent } from './components/admin/roles/roles/roles.component';
|
import { RolesComponent } from './components/admin/roles/roles/roles.component';
|
||||||
import { MatTableModule } from '@angular/material/table';
|
import { MatTableModule } from '@angular/material/table';
|
||||||
|
import { MatButtonToggleModule } from '@angular/material/button-toggle';
|
||||||
import { MatDialogModule } from '@angular/material/dialog';
|
import { MatDialogModule } from '@angular/material/dialog';
|
||||||
import { AddUserModalComponent } from './components/admin/users/users/add-user-modal/add-user-modal.component';
|
import { AddUserModalComponent } from './components/admin/users/users/add-user-modal/add-user-modal.component';
|
||||||
import { MatSelectModule } from '@angular/material/select';
|
import { MatSelectModule } from '@angular/material/select';
|
||||||
|
@ -32,17 +34,14 @@ import { AddRoleModalComponent } from './components/admin/roles/roles/add-role-m
|
||||||
import { ChangePasswordModalComponent } from './components/admin/users/users/change-password-modal/change-password-modal.component';
|
import { ChangePasswordModalComponent } from './components/admin/users/users/change-password-modal/change-password-modal.component';
|
||||||
import { GroupsComponent } from './components/groups/groups.component';
|
import { GroupsComponent } from './components/groups/groups.component';
|
||||||
import { MatDividerModule } from '@angular/material/divider';
|
import { MatDividerModule } from '@angular/material/divider';
|
||||||
import { CreateOrganizationalUnitComponent } from './components/groups/shared/organizational-units/create-organizational-unit/create-organizational-unit.component';
|
|
||||||
import { MatStepperModule } from '@angular/material/stepper';
|
import { MatStepperModule } from '@angular/material/stepper';
|
||||||
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
|
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
|
||||||
import { CreateClientComponent } from './components/groups/shared/clients/create-client/create-client.component';
|
|
||||||
import { DeleteModalComponent } from './shared/delete_modal/delete-modal/delete-modal.component';
|
import { DeleteModalComponent } from './shared/delete_modal/delete-modal/delete-modal.component';
|
||||||
import { EditOrganizationalUnitComponent } from './components/groups/shared/organizational-units/edit-organizational-unit/edit-organizational-unit.component';
|
|
||||||
import { EditClientComponent } from './components/groups/shared/clients/edit-client/edit-client.component';
|
|
||||||
import { ClassroomViewComponent } from './components/groups/shared/classroom-view/classroom-view.component';
|
import { ClassroomViewComponent } from './components/groups/shared/classroom-view/classroom-view.component';
|
||||||
import { MatProgressSpinner } from "@angular/material/progress-spinner";
|
import { MatProgressSpinner } from "@angular/material/progress-spinner";
|
||||||
|
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
||||||
import { MatMenu, MatMenuItem, MatMenuTrigger } from "@angular/material/menu";
|
import { MatMenu, MatMenuItem, MatMenuTrigger } from "@angular/material/menu";
|
||||||
import {MatAutocomplete, MatAutocompleteTrigger} from "@angular/material/autocomplete";
|
import { MatAutocomplete, MatAutocompleteTrigger } from "@angular/material/autocomplete";
|
||||||
import { MatChip, MatChipListbox, MatChipOption, MatChipSet, MatChipsModule } from "@angular/material/chips";
|
import { MatChip, MatChipListbox, MatChipOption, MatChipSet, MatChipsModule } from "@angular/material/chips";
|
||||||
import { ClientViewComponent } from './components/groups/shared/client-view/client-view.component';
|
import { ClientViewComponent } from './components/groups/shared/client-view/client-view.component';
|
||||||
import { MatTab, MatTabGroup } from "@angular/material/tabs";
|
import { MatTab, MatTabGroup } from "@angular/material/tabs";
|
||||||
|
@ -52,7 +51,6 @@ import { DragDropModule } from '@angular/cdk/drag-drop';
|
||||||
import { ToastrModule } from 'ngx-toastr';
|
import { ToastrModule } from 'ngx-toastr';
|
||||||
import { ShowOrganizationalUnitComponent } from './components/groups/shared/organizational-units/show-organizational-unit/show-organizational-unit.component';
|
import { ShowOrganizationalUnitComponent } from './components/groups/shared/organizational-units/show-organizational-unit/show-organizational-unit.component';
|
||||||
import { MatGridList, MatGridTile } from "@angular/material/grid-list";
|
import { MatGridList, MatGridTile } from "@angular/material/grid-list";
|
||||||
import { TreeViewComponent } from './components/groups/shared/tree-view/tree-view.component';
|
|
||||||
import {
|
import {
|
||||||
MatNestedTreeNode,
|
MatNestedTreeNode,
|
||||||
MatTree,
|
MatTree,
|
||||||
|
@ -65,7 +63,6 @@ import { LegendComponent } from './components/groups/shared/legend/legend.compon
|
||||||
import { ClassroomViewDialogComponent } from './components/groups/shared/classroom-view/classroom-view-modal';
|
import { ClassroomViewDialogComponent } from './components/groups/shared/classroom-view/classroom-view-modal';
|
||||||
import { MatPaginator } from "@angular/material/paginator";
|
import { MatPaginator } from "@angular/material/paginator";
|
||||||
import { SaveFiltersDialogComponent } from './components/groups/shared/save-filters-dialog/save-filters-dialog.component';
|
import { SaveFiltersDialogComponent } from './components/groups/shared/save-filters-dialog/save-filters-dialog.component';
|
||||||
import { AcctionsModalComponent } from './components/groups/shared/acctions-modal/acctions-modal.component';
|
|
||||||
import { PXEimagesComponent } from './components/ogboot/pxe-images/pxe-images.component';
|
import { PXEimagesComponent } from './components/ogboot/pxe-images/pxe-images.component';
|
||||||
import { CreatePXEImageComponent } from './components/ogboot/pxe-images/create-image/create-image/create-image.component';
|
import { CreatePXEImageComponent } from './components/ogboot/pxe-images/create-image/create-image/create-image.component';
|
||||||
import { InfoImageComponent } from './components/ogboot/pxe-images/info-image/info-image/info-image.component';
|
import { InfoImageComponent } from './components/ogboot/pxe-images/info-image/info-image/info-image.component';
|
||||||
|
@ -74,12 +71,7 @@ import { CreatePxeTemplateComponent } from './components/ogboot/pxe/create-pxeTe
|
||||||
import { PxeBootFilesComponent } from './components/ogboot/pxe-boot-files/pxe-boot-files.component';
|
import { PxeBootFilesComponent } from './components/ogboot/pxe-boot-files/pxe-boot-files.component';
|
||||||
import { MatExpansionPanel, MatExpansionPanelDescription, MatExpansionPanelTitle } from "@angular/material/expansion";
|
import { MatExpansionPanel, MatExpansionPanelDescription, MatExpansionPanelTitle } from "@angular/material/expansion";
|
||||||
import { OgbootStatusComponent } from './components/ogboot/ogboot-status/ogboot-status.component';
|
import { OgbootStatusComponent } from './components/ogboot/ogboot-status/ogboot-status.component';
|
||||||
import { CreatePxeBootFileComponent } from './components/ogboot/pxe-boot-files/create-pxeBootFile/create-pxe-boot-file/create-pxe-boot-file.component';
|
|
||||||
import { NgxChartsModule } from '@swimlane/ngx-charts';
|
import { NgxChartsModule } from '@swimlane/ngx-charts';
|
||||||
import { OgdhcpComponent } from './components/ogdhcp/ogdhcp.component';
|
|
||||||
import { OgDhcpSubnetsComponent } from './components/ogdhcp/og-dhcp-subnets/og-dhcp-subnets.component';
|
|
||||||
import { CreateSubnetComponent } from './components/ogdhcp/og-dhcp-subnets/create-subnet/create-subnet.component';
|
|
||||||
import { AddClientsToSubnetComponent } from './components/ogdhcp/og-dhcp-subnets/add-clients-to-subnet/add-clients-to-subnet.component';
|
|
||||||
import { CommandsComponent } from './components/commands/main-commands/commands.component';
|
import { CommandsComponent } from './components/commands/main-commands/commands.component';
|
||||||
import { CommandDetailComponent } from './components/commands/main-commands/detail-command/command-detail.component';
|
import { CommandDetailComponent } from './components/commands/main-commands/detail-command/command-detail.component';
|
||||||
import { CreateCommandComponent } from './components/commands/main-commands/create-command/create-command.component';
|
import { CreateCommandComponent } from './components/commands/main-commands/create-command/create-command.component';
|
||||||
|
@ -87,7 +79,7 @@ import { MatDatepickerModule } from '@angular/material/datepicker';
|
||||||
import { MatNativeDateModule } from '@angular/material/core';
|
import { MatNativeDateModule } from '@angular/material/core';
|
||||||
import { CalendarComponent } from './components/calendar/calendar.component';
|
import { CalendarComponent } from './components/calendar/calendar.component';
|
||||||
import { CreateCalendarComponent } from './components/calendar/create-calendar/create-calendar.component';
|
import { CreateCalendarComponent } from './components/calendar/create-calendar/create-calendar.component';
|
||||||
import {MatRadioButton, MatRadioGroup} from "@angular/material/radio";
|
import { MatRadioButton, MatRadioGroup } from "@angular/material/radio";
|
||||||
import { CreateCalendarRuleComponent } from './components/calendar/create-calendar-rule/create-calendar-rule.component';
|
import { CreateCalendarRuleComponent } from './components/calendar/create-calendar-rule/create-calendar-rule.component';
|
||||||
import { CommandsGroupsComponent } from './components/commands/commands-groups/commands-groups.component';
|
import { CommandsGroupsComponent } from './components/commands/commands-groups/commands-groups.component';
|
||||||
import { CommandsTaskComponent } from './components/commands/commands-task/commands-task.component';
|
import { CommandsTaskComponent } from './components/commands/commands-task/commands-task.component';
|
||||||
|
@ -95,16 +87,12 @@ import { CreateCommandGroupComponent } from './components/commands/commands-grou
|
||||||
import { DetailCommandGroupComponent } from './components/commands/commands-groups/detail-command-group/detail-command-group.component';
|
import { DetailCommandGroupComponent } from './components/commands/commands-groups/detail-command-group/detail-command-group.component';
|
||||||
import { CreateTaskComponent } from './components/commands/commands-task/create-task/create-task.component';
|
import { CreateTaskComponent } from './components/commands/commands-task/create-task/create-task.component';
|
||||||
import { DetailTaskComponent } from './components/commands/commands-task/detail-task/detail-task.component';
|
import { DetailTaskComponent } from './components/commands/commands-task/detail-task/detail-task.component';
|
||||||
import { ClientTabViewComponent } from './components/groups/components/client-tab-view/client-tab-view.component';
|
|
||||||
import { AdvancedSearchComponent } from './components/groups/components/advanced-search/advanced-search.component';
|
|
||||||
import { TaskLogsComponent } from './components/commands/commands-task/task-logs/task-logs.component';
|
import { TaskLogsComponent } from './components/commands/commands-task/task-logs/task-logs.component';
|
||||||
import { OrganizationalUnitTabViewComponent } from './components/groups/components/organizational-unit-tab-view/organizational-unit-tab-view.component';
|
import { MatSliderModule } from '@angular/material/slider';
|
||||||
import { ServerInfoDialogComponent } from './components/ogdhcp/og-dhcp-subnets/server-info-dialog/server-info-dialog.component';
|
|
||||||
import { StatusComponent } from './components/ogdhcp/og-dhcp-subnets/status/status.component';
|
|
||||||
import {MatSliderModule} from '@angular/material/slider';
|
|
||||||
import { ClientMainViewComponent } from './components/groups/components/client-main-view/client-main-view.component';
|
import { ClientMainViewComponent } from './components/groups/components/client-main-view/client-main-view.component';
|
||||||
import { ImagesComponent } from './components/images/images.component';
|
import { ImagesComponent } from './components/images/images.component';
|
||||||
import { CreateImageComponent } from './components/images/create-image/create-image.component';
|
import { CreateImageComponent } from './components/images/create-image/create-image.component';
|
||||||
|
import { CreateClientImageComponent } from './components/groups/components/client-main-view/create-image/create-image.component';
|
||||||
import { PartitionAssistantComponent } from './components/groups/components/client-main-view/partition-assistant/partition-assistant.component';
|
import { PartitionAssistantComponent } from './components/groups/components/client-main-view/partition-assistant/partition-assistant.component';
|
||||||
import { SoftwareComponent } from './components/software/software.component';
|
import { SoftwareComponent } from './components/software/software.component';
|
||||||
import { CreateSoftwareComponent } from './components/software/create-software/create-software.component';
|
import { CreateSoftwareComponent } from './components/software/create-software/create-software.component';
|
||||||
|
@ -114,7 +102,7 @@ import { OperativeSystemComponent } from './components/operative-system/operativ
|
||||||
import { CreateOperativeSystemComponent } from './components/operative-system/create-operative-system/create-operative-system.component';
|
import { CreateOperativeSystemComponent } from './components/operative-system/create-operative-system/create-operative-system.component';
|
||||||
import { ShowTemplateContentComponent } from './components/ogboot/pxe/show-template-content/show-template-content.component';
|
import { ShowTemplateContentComponent } from './components/ogboot/pxe/show-template-content/show-template-content.component';
|
||||||
import { RepositoriesComponent } from './components/repositories/repositories.component';
|
import { RepositoriesComponent } from './components/repositories/repositories.component';
|
||||||
import { CreateRepositoryComponent } from './components/repositories/create-repository/create-repository.component';
|
import { ManageRepositoryComponent } from './components/repositories/manage-repository/manage-repository.component';
|
||||||
import { ExecuteCommandComponent } from './components/commands/main-commands/execute-command/execute-command.component';
|
import { ExecuteCommandComponent } from './components/commands/main-commands/execute-command/execute-command.component';
|
||||||
import { DeployImageComponent } from './components/groups/components/client-main-view/deploy-image/deploy-image.component';
|
import { DeployImageComponent } from './components/groups/components/client-main-view/deploy-image/deploy-image.component';
|
||||||
import { MainRepositoryViewComponent } from './components/repositories/main-repository-view/main-repository-view.component';
|
import { MainRepositoryViewComponent } from './components/repositories/main-repository-view/main-repository-view.component';
|
||||||
|
@ -123,11 +111,49 @@ import { JoyrideModule } from 'ngx-joyride';
|
||||||
import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
|
import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
|
||||||
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
|
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
|
||||||
import { EnvVarsComponent } from './components/admin/env-vars/env-vars.component';
|
import { EnvVarsComponent } from './components/admin/env-vars/env-vars.component';
|
||||||
|
import { MatSortModule } from '@angular/material/sort';
|
||||||
|
import { MenusComponent } from './components/menus/menus.component';
|
||||||
|
import { CreateMenuComponent } from './components/menus/create-menu/create-menu.component';
|
||||||
|
import { CreateMultipleClientComponent } from './components/groups/shared/clients/create-multiple-client/create-multiple-client.component';
|
||||||
|
import { ExportImageComponent } from './components/images/export-image/export-image.component';
|
||||||
|
import { ImportImageComponent } from "./components/repositories/import-image/import-image.component";
|
||||||
|
import { LoadingComponent } from './shared/loading/loading.component';
|
||||||
|
import { InputDialogComponent } from './components/commands/commands-task/task-logs/input-dialog/input-dialog.component';
|
||||||
|
import { ManageOrganizationalUnitComponent } from './components/groups/shared/organizational-units/manage-organizational-unit/manage-organizational-unit.component';
|
||||||
|
import { BackupImageComponent } from './components/repositories/backup-image/backup-image.component';
|
||||||
|
import { ServerInfoDialogComponent } from "./components/ogdhcp/server-info-dialog/server-info-dialog.component";
|
||||||
|
import { StatusComponent } from "./components/ogdhcp/status/status.component";
|
||||||
|
import { OgDhcpSubnetsComponent } from "./components/ogdhcp/og-dhcp-subnets.component";
|
||||||
|
import { CreateSubnetComponent } from "./components/ogdhcp/create-subnet/create-subnet.component";
|
||||||
|
import { AddClientsToSubnetComponent } from "./components/ogdhcp/add-clients-to-subnet/add-clients-to-subnet.component";
|
||||||
|
import { ShowClientsComponent } from './components/ogdhcp/show-clients/show-clients.component';
|
||||||
|
import { OperationResultDialogComponent } from './components/ogdhcp/operation-result-dialog/operation-result-dialog.component';
|
||||||
|
import { ManageClientComponent } from './components/groups/shared/clients/manage-client/manage-client.component';
|
||||||
|
import { ConvertImageComponent } from './components/repositories/convert-image/convert-image.component';
|
||||||
|
import { registerLocaleData } from '@angular/common';
|
||||||
|
import localeEs from '@angular/common/locales/es';
|
||||||
|
import { GlobalStatusComponent } from './components/global-status/global-status.component';
|
||||||
|
import { ShowMonoliticImagesComponent } from './components/repositories/show-monolitic-images/show-monolitic-images.component';
|
||||||
|
import { StatusTabComponent } from './components/global-status/status-tab/status-tab.component';
|
||||||
|
import { ConvertImageToVirtualComponent } from './components/repositories/convert-image-to-virtual/convert-image-to-virtual.component';
|
||||||
|
import { RunScriptAssistantComponent } from './components/groups/components/client-main-view/run-script-assistant/run-script-assistant.component';
|
||||||
|
import {
|
||||||
|
SaveScriptComponent
|
||||||
|
} from "./components/groups/components/client-main-view/run-script-assistant/save-script/save-script.component";
|
||||||
|
import { EditImageComponent } from './components/repositories/edit-image/edit-image.component';
|
||||||
|
import { ShowGitImagesComponent } from './components/repositories/show-git-images/show-git-images.component';
|
||||||
|
import { RenameImageComponent } from './components/repositories/rename-image/rename-image.component';
|
||||||
|
|
||||||
export function HttpLoaderFactory(http: HttpClient) {
|
export function HttpLoaderFactory(http: HttpClient) {
|
||||||
return new TranslateHttpLoader(http, './locale/', '.json');
|
return new TranslateHttpLoader(http, './locale/', '.json');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function initializeApp(configService: ConfigService) {
|
||||||
|
return () => configService.loadConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
registerLocaleData(localeEs, 'es-ES');
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
AppComponent,
|
AppComponent,
|
||||||
|
@ -144,19 +170,14 @@ export function HttpLoaderFactory(http: HttpClient) {
|
||||||
AddRoleModalComponent,
|
AddRoleModalComponent,
|
||||||
ChangePasswordModalComponent,
|
ChangePasswordModalComponent,
|
||||||
GroupsComponent,
|
GroupsComponent,
|
||||||
CreateOrganizationalUnitComponent,
|
ManageClientComponent,
|
||||||
CreateClientComponent,
|
|
||||||
DeleteModalComponent,
|
DeleteModalComponent,
|
||||||
EditOrganizationalUnitComponent,
|
|
||||||
EditClientComponent,
|
|
||||||
ClassroomViewComponent,
|
ClassroomViewComponent,
|
||||||
ClientViewComponent,
|
ClientViewComponent,
|
||||||
ShowOrganizationalUnitComponent,
|
ShowOrganizationalUnitComponent,
|
||||||
TreeViewComponent,
|
|
||||||
LegendComponent,
|
LegendComponent,
|
||||||
ClassroomViewDialogComponent,
|
ClassroomViewDialogComponent,
|
||||||
SaveFiltersDialogComponent,
|
SaveFiltersDialogComponent,
|
||||||
AcctionsModalComponent,
|
|
||||||
PXEimagesComponent,
|
PXEimagesComponent,
|
||||||
CreatePXEImageComponent,
|
CreatePXEImageComponent,
|
||||||
InfoImageComponent,
|
InfoImageComponent,
|
||||||
|
@ -164,8 +185,6 @@ export function HttpLoaderFactory(http: HttpClient) {
|
||||||
CreatePxeTemplateComponent,
|
CreatePxeTemplateComponent,
|
||||||
PxeBootFilesComponent,
|
PxeBootFilesComponent,
|
||||||
OgbootStatusComponent,
|
OgbootStatusComponent,
|
||||||
CreatePxeBootFileComponent,
|
|
||||||
OgdhcpComponent,
|
|
||||||
OgDhcpSubnetsComponent,
|
OgDhcpSubnetsComponent,
|
||||||
CreateSubnetComponent,
|
CreateSubnetComponent,
|
||||||
AddClientsToSubnetComponent,
|
AddClientsToSubnetComponent,
|
||||||
|
@ -174,6 +193,7 @@ export function HttpLoaderFactory(http: HttpClient) {
|
||||||
CreateCommandComponent,
|
CreateCommandComponent,
|
||||||
CalendarComponent,
|
CalendarComponent,
|
||||||
CreateCalendarComponent,
|
CreateCalendarComponent,
|
||||||
|
CreateClientImageComponent,
|
||||||
CreateCalendarRuleComponent,
|
CreateCalendarRuleComponent,
|
||||||
CommandsGroupsComponent,
|
CommandsGroupsComponent,
|
||||||
CommandsTaskComponent,
|
CommandsTaskComponent,
|
||||||
|
@ -181,10 +201,7 @@ export function HttpLoaderFactory(http: HttpClient) {
|
||||||
DetailCommandGroupComponent,
|
DetailCommandGroupComponent,
|
||||||
CreateTaskComponent,
|
CreateTaskComponent,
|
||||||
DetailTaskComponent,
|
DetailTaskComponent,
|
||||||
ClientTabViewComponent,
|
|
||||||
AdvancedSearchComponent,
|
|
||||||
TaskLogsComponent,
|
TaskLogsComponent,
|
||||||
OrganizationalUnitTabViewComponent,
|
|
||||||
ServerInfoDialogComponent,
|
ServerInfoDialogComponent,
|
||||||
StatusComponent,
|
StatusComponent,
|
||||||
ClientMainViewComponent,
|
ClientMainViewComponent,
|
||||||
|
@ -199,60 +216,84 @@ export function HttpLoaderFactory(http: HttpClient) {
|
||||||
CreateOperativeSystemComponent,
|
CreateOperativeSystemComponent,
|
||||||
ShowTemplateContentComponent,
|
ShowTemplateContentComponent,
|
||||||
RepositoriesComponent,
|
RepositoriesComponent,
|
||||||
CreateRepositoryComponent,
|
ManageRepositoryComponent,
|
||||||
ExecuteCommandComponent,
|
ExecuteCommandComponent,
|
||||||
ExecuteCommandOuComponent,
|
ExecuteCommandOuComponent,
|
||||||
DeployImageComponent,
|
DeployImageComponent,
|
||||||
MainRepositoryViewComponent,
|
MainRepositoryViewComponent,
|
||||||
ExecuteCommandOuComponent,
|
ExecuteCommandOuComponent,
|
||||||
EnvVarsComponent,
|
EnvVarsComponent,
|
||||||
|
MenusComponent,
|
||||||
|
CreateMenuComponent,
|
||||||
|
CreateMultipleClientComponent,
|
||||||
|
ExportImageComponent,
|
||||||
|
ImportImageComponent,
|
||||||
|
LoadingComponent,
|
||||||
|
InputDialogComponent,
|
||||||
|
ManageOrganizationalUnitComponent,
|
||||||
|
BackupImageComponent,
|
||||||
|
ShowClientsComponent,
|
||||||
|
OperationResultDialogComponent,
|
||||||
|
ConvertImageComponent,
|
||||||
|
GlobalStatusComponent,
|
||||||
|
ShowMonoliticImagesComponent,
|
||||||
|
StatusTabComponent,
|
||||||
|
ConvertImageToVirtualComponent,
|
||||||
|
RunScriptAssistantComponent,
|
||||||
|
SaveScriptComponent,
|
||||||
|
EditImageComponent,
|
||||||
|
ShowGitImagesComponent,
|
||||||
|
RenameImageComponent
|
||||||
],
|
],
|
||||||
bootstrap: [AppComponent],
|
bootstrap: [AppComponent],
|
||||||
imports: [BrowserModule,
|
imports: [BrowserModule,
|
||||||
AppRoutingModule,
|
AppRoutingModule,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
MatToolbarModule,
|
MatToolbarModule,
|
||||||
MatIconModule,
|
MatIconModule,
|
||||||
MatButtonModule,
|
MatButtonToggleModule,
|
||||||
MatSidenavModule,
|
MatButtonModule,
|
||||||
NoopAnimationsModule,
|
MatSidenavModule,
|
||||||
MatCardModule,
|
NoopAnimationsModule,
|
||||||
MatCheckboxModule,
|
MatCardModule,
|
||||||
MatFormFieldModule,
|
MatCheckboxModule,
|
||||||
MatInputModule,
|
MatFormFieldModule,
|
||||||
MatListModule,
|
MatInputModule,
|
||||||
MatTableModule,
|
MatListModule,
|
||||||
MatDialogModule,
|
MatTableModule,
|
||||||
MatSelectModule,
|
MatDialogModule,
|
||||||
MatDividerModule,
|
MatSelectModule,
|
||||||
MatStepperModule,
|
MatDividerModule,
|
||||||
DragDropModule,
|
MatProgressBarModule,
|
||||||
MatSlideToggleModule, MatMenu, MatMenuTrigger, MatMenuItem, MatAutocomplete, MatChipListbox, MatChipOption, MatChipSet, MatChipsModule, MatChip, MatProgressSpinner, MatTabGroup, MatTab, MatTooltip,
|
MatStepperModule,
|
||||||
MatExpansionModule,
|
DragDropModule,
|
||||||
NgxChartsModule,
|
MatSlideToggleModule, MatMenu, MatMenuTrigger, MatMenuItem, MatAutocomplete, MatChipListbox, MatChipOption, MatChipSet, MatChipsModule, MatChip, MatProgressSpinner, MatTabGroup, MatTab, MatTooltip,
|
||||||
MatDatepickerModule,
|
MatExpansionModule,
|
||||||
MatNativeDateModule,
|
NgxChartsModule,
|
||||||
MatSliderModule,
|
MatDatepickerModule,
|
||||||
TranslateModule.forRoot({
|
MatNativeDateModule,
|
||||||
loader: {
|
MatSliderModule,
|
||||||
provide: TranslateLoader,
|
MatSortModule,
|
||||||
useFactory: HttpLoaderFactory,
|
TranslateModule.forRoot({
|
||||||
deps: [HttpClient]
|
loader: {
|
||||||
}
|
provide: TranslateLoader,
|
||||||
}),
|
useFactory: HttpLoaderFactory,
|
||||||
JoyrideModule.forRoot(),
|
deps: [HttpClient]
|
||||||
ToastrModule.forRoot(
|
}
|
||||||
{
|
}),
|
||||||
timeOut: 5000,
|
JoyrideModule.forRoot(),
|
||||||
positionClass: 'toast-bottom-right',
|
ToastrModule.forRoot(
|
||||||
preventDuplicates: true,
|
{
|
||||||
progressBar: true,
|
timeOut: 5000,
|
||||||
progressAnimation: 'increasing',
|
positionClass: 'toast-bottom-right',
|
||||||
closeButton: true
|
preventDuplicates: true,
|
||||||
}
|
progressBar: true,
|
||||||
), MatGridList, MatTree, MatTreeNode, MatNestedTreeNode, MatTreeNodeToggle, MatTreeNodeDef, MatTreeNodePadding, MatTreeNodeOutlet, MatPaginator, MatGridTile, MatExpansionPanel, MatExpansionPanelTitle, MatExpansionPanelDescription, MatRadioGroup, MatRadioButton, MatAutocompleteTrigger
|
progressAnimation: 'increasing',
|
||||||
],
|
closeButton: true
|
||||||
|
}
|
||||||
|
), MatGridList, MatTree, MatTreeNode, MatNestedTreeNode, MatTreeNodeToggle, MatTreeNodeDef, MatTreeNodePadding, MatTreeNodeOutlet, MatPaginator, MatGridTile, MatExpansionPanel, MatExpansionPanelTitle, MatExpansionPanelDescription, MatRadioGroup, MatRadioButton, MatAutocompleteTrigger
|
||||||
|
],
|
||||||
schemas: [
|
schemas: [
|
||||||
CUSTOM_ELEMENTS_SCHEMA,
|
CUSTOM_ELEMENTS_SCHEMA,
|
||||||
],
|
],
|
||||||
|
@ -262,8 +303,16 @@ export function HttpLoaderFactory(http: HttpClient) {
|
||||||
useClass: CustomInterceptor,
|
useClass: CustomInterceptor,
|
||||||
multi: true
|
multi: true
|
||||||
},
|
},
|
||||||
|
{ provide: LOCALE_ID, useValue: 'es-ES' },
|
||||||
provideAnimationsAsync(),
|
provideAnimationsAsync(),
|
||||||
provideHttpClient(withInterceptorsFromDi())
|
provideHttpClient(withInterceptorsFromDi()),
|
||||||
|
ConfigService,
|
||||||
|
{
|
||||||
|
provide: APP_INITIALIZER,
|
||||||
|
useFactory: initializeApp,
|
||||||
|
deps: [ConfigService],
|
||||||
|
multi: true
|
||||||
|
}
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class AppModule { }
|
export class AppModule { }
|
||||||
|
|
|
@ -14,16 +14,6 @@
|
||||||
margin: 0 10px;
|
margin: 0 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Estilos de los botones */
|
|
||||||
button {
|
|
||||||
height: 150px;
|
|
||||||
width: 150px;
|
|
||||||
margin: 5px;
|
|
||||||
padding: 5px;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Estilos del texto debajo de los botones */
|
/* Estilos del texto debajo de los botones */
|
||||||
span{
|
span{
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<button mat-fab color="primary" class="fab-button" routerLink="/users">
|
<button class="action-button" routerLink="/users">
|
||||||
<mat-icon>group</mat-icon>
|
<mat-icon>group</mat-icon>
|
||||||
<span>{{ 'labelUsers' | translate }}</span>
|
<span>{{ 'labelUsers' | translate }}</span>
|
||||||
</button>
|
</button>
|
||||||
<button mat-fab color="primary" class="fab-button" routerLink="/user-groups">
|
<button class="action-button" routerLink="/user-groups">
|
||||||
<mat-icon>admin_panel_settings</mat-icon>
|
<mat-icon>admin_panel_settings</mat-icon>
|
||||||
<span>{{ 'labelRoles' | translate }}</span>
|
<span>{{ 'labelRoles' | translate }}</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -45,13 +45,4 @@ describe('AdminComponent', () => {
|
||||||
expect(button).toBeTruthy();
|
expect(button).toBeTruthy();
|
||||||
expect(button.querySelector('mat-icon').textContent.trim()).toBe('group');
|
expect(button.querySelector('mat-icon').textContent.trim()).toBe('group');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('debería aplicar la clase "fab-button" a ambos botones', () => {
|
|
||||||
const buttons = fixture.nativeElement.querySelectorAll('button');
|
|
||||||
buttons.forEach((button: HTMLElement) => {
|
|
||||||
expect(button.classList).toContain('fab-button');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,9 +1,5 @@
|
||||||
.env-settings {
|
.env-settings {
|
||||||
padding: 16px;
|
padding: 0rem 1rem 0rem 1rem;
|
||||||
|
|
||||||
h1 {
|
|
||||||
margin-bottom: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mat-table {
|
.mat-table {
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
|
@ -19,10 +15,6 @@
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
|
|
||||||
button {
|
|
||||||
min-width: 120px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
<div class="env-settings">
|
<div class="env-settings">
|
||||||
<h1>Editar Variables de Entorno</h1>
|
<div class="header-container">
|
||||||
|
<div class="header-container-title">
|
||||||
|
<h2>Editar Variables de Entorno</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<mat-table [dataSource]="envVars" class="mat-elevation-z8">
|
<mat-table [dataSource]="envVars" class="mat-elevation-z8">
|
||||||
<!-- Nombre de la variable -->
|
<!-- Nombre de la variable -->
|
||||||
|
@ -22,8 +26,8 @@
|
||||||
<mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
|
<mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
|
||||||
</mat-table>
|
</mat-table>
|
||||||
|
|
||||||
<div class="actions" >
|
<div class="actions">
|
||||||
<button mat-raised-button color="primary" (click)="saveEnvVars()">Guardar Cambios</button>
|
<button class="action-button" (click)="loadEnvVars()">Recargar</button>
|
||||||
<button mat-raised-button color="accent" (click)="loadEnvVars()">Recargar</button>
|
<button class="submit-button" (click)="saveEnvVars()">Guardar Cambios</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
|
@ -13,24 +13,29 @@ import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { ToastrModule, ToastrService } from 'ngx-toastr';
|
import { ToastrModule, ToastrService } from 'ngx-toastr';
|
||||||
import { DataService } from '../users/users/data.service';
|
import { DataService } from '../users/users/data.service';
|
||||||
import { MatTableModule } from '@angular/material/table';
|
import { MatTableModule } from '@angular/material/table';
|
||||||
|
import { ConfigService } from '@services/config.service';
|
||||||
|
|
||||||
describe('EnvVarsComponent', () => {
|
describe('EnvVarsComponent', () => {
|
||||||
let component: EnvVarsComponent;
|
let component: EnvVarsComponent;
|
||||||
let fixture: ComponentFixture<EnvVarsComponent>;
|
let fixture: ComponentFixture<EnvVarsComponent>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
|
const mockConfigService = {
|
||||||
|
apiUrl: 'http://mock-api-url'
|
||||||
|
};
|
||||||
|
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
declarations: [EnvVarsComponent],
|
declarations: [EnvVarsComponent],
|
||||||
imports: [
|
imports: [
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
MatDialogModule,
|
MatDialogModule,
|
||||||
MatFormFieldModule,
|
MatFormFieldModule,
|
||||||
MatInputModule,
|
MatInputModule,
|
||||||
MatCheckboxModule,
|
MatCheckboxModule,
|
||||||
MatButtonModule,
|
MatButtonModule,
|
||||||
BrowserAnimationsModule,
|
BrowserAnimationsModule,
|
||||||
MatTableModule,
|
MatTableModule,
|
||||||
ToastrModule.forRoot(),
|
ToastrModule.forRoot(),
|
||||||
TranslateModule.forRoot()
|
TranslateModule.forRoot()
|
||||||
],
|
],
|
||||||
|
@ -47,6 +52,10 @@ describe('EnvVarsComponent', () => {
|
||||||
{
|
{
|
||||||
provide: MAT_DIALOG_DATA,
|
provide: MAT_DIALOG_DATA,
|
||||||
useValue: {}
|
useValue: {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: ConfigService,
|
||||||
|
useValue: mockConfigService
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import {HttpClient} from "@angular/common/http";
|
import { HttpClient } from "@angular/common/http";
|
||||||
import {ToastrService} from "ngx-toastr";
|
import { ToastrService } from "ngx-toastr";
|
||||||
|
import { ConfigService } from '@services/config.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-env-vars',
|
selector: 'app-env-vars',
|
||||||
|
@ -8,16 +9,17 @@ import {ToastrService} from "ngx-toastr";
|
||||||
styleUrl: './env-vars.component.css'
|
styleUrl: './env-vars.component.css'
|
||||||
})
|
})
|
||||||
export class EnvVarsComponent {
|
export class EnvVarsComponent {
|
||||||
baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
|
|
||||||
envVars: { name: string; value: string }[] = [];
|
envVars: { name: string; value: string }[] = [];
|
||||||
displayedColumns: string[] = ['name', 'value'];
|
displayedColumns: string[] = ['name', 'value'];
|
||||||
|
private apiUrl: string;
|
||||||
private apiUrl = `${this.baseUrl}/env-vars`;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private http: HttpClient,
|
private http: HttpClient,
|
||||||
private toastService: ToastrService,
|
private toastService: ToastrService,
|
||||||
) {}
|
private configService: ConfigService
|
||||||
|
) {
|
||||||
|
this.apiUrl = `${this.configService.apiUrl}/env-vars`;
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.loadEnvVars();
|
this.loadEnvVars();
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
.full-width {
|
.full-width {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-container {
|
.form-container {
|
||||||
|
margin-top: 2em;
|
||||||
padding: 40px;
|
padding: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,16 +17,24 @@
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.checkbox-group {
|
.checkbox-group {
|
||||||
margin: 15px 0;
|
margin: 15px 0;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
.time-fields {
|
.time-fields {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 15px; /* Espacio entre los campos */
|
gap: 15px;
|
||||||
|
/* Espacio entre los campos */
|
||||||
}
|
}
|
||||||
|
|
||||||
.time-field {
|
.time-field {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.action-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 1em;
|
||||||
|
padding: 1.5em;
|
||||||
|
}
|
|
@ -16,7 +16,7 @@
|
||||||
</section>
|
</section>
|
||||||
</form>
|
</form>
|
||||||
</mat-dialog-content>
|
</mat-dialog-content>
|
||||||
<mat-dialog-actions align="end">
|
<mat-dialog-actions class="action-container">
|
||||||
<button mat-button (click)="onNoClick()">{{ 'buttonCancel' | translate }}</button>
|
<button class="ordinary-button" (click)="onNoClick()">{{ 'buttonCancel' | translate }}</button>
|
||||||
<button mat-button (click)="onSubmit()">{{ 'buttonAdd' | translate }}</button>
|
<button class="submit-button" (click)="onSubmit()">{{ 'buttonAdd' | translate }}</button>
|
||||||
</mat-dialog-actions>
|
</mat-dialog-actions>
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||||
import {FormBuilder, FormGroup, Validators} from '@angular/forms';
|
import {FormBuilder, FormGroup, Validators} from '@angular/forms';
|
||||||
import {DataService} from "../data.service";
|
import {DataService} from "../data.service";
|
||||||
import {ToastrService} from "ngx-toastr";
|
import {ToastrService} from "ngx-toastr";
|
||||||
|
import { ConfigService } from '@services/config.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-add-role-modal',
|
selector: 'app-add-role-modal',
|
||||||
|
@ -11,9 +12,9 @@ import {ToastrService} from "ngx-toastr";
|
||||||
styleUrls: ['./add-role-modal.component.css']
|
styleUrls: ['./add-role-modal.component.css']
|
||||||
})
|
})
|
||||||
export class AddRoleModalComponent {
|
export class AddRoleModalComponent {
|
||||||
baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
|
|
||||||
roleForm: FormGroup<any>;
|
roleForm: FormGroup<any>;
|
||||||
roleId: string | null = null;
|
roleId: string | null = null;
|
||||||
|
baseUrl: string;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public dialogRef: MatDialogRef<AddRoleModalComponent>,
|
public dialogRef: MatDialogRef<AddRoleModalComponent>,
|
||||||
|
@ -21,8 +22,10 @@ export class AddRoleModalComponent {
|
||||||
private http: HttpClient,
|
private http: HttpClient,
|
||||||
private fb: FormBuilder,
|
private fb: FormBuilder,
|
||||||
private dataService: DataService,
|
private dataService: DataService,
|
||||||
private toastService: ToastrService
|
private toastService: ToastrService,
|
||||||
|
private configService: ConfigService
|
||||||
) {
|
) {
|
||||||
|
this.baseUrl = this.configService.apiUrl;
|
||||||
this.roleForm = this.fb.group({
|
this.roleForm = this.fb.group({
|
||||||
name: ['', Validators.required],
|
name: ['', Validators.required],
|
||||||
superAdmin: [false],
|
superAdmin: [false],
|
||||||
|
|
|
@ -2,15 +2,19 @@ import { Injectable } from '@angular/core';
|
||||||
import {HttpClient, HttpParams} from '@angular/common/http';
|
import {HttpClient, HttpParams} from '@angular/common/http';
|
||||||
import { Observable, throwError } from 'rxjs';
|
import { Observable, throwError } from 'rxjs';
|
||||||
import { catchError, map } from 'rxjs/operators';
|
import { catchError, map } from 'rxjs/operators';
|
||||||
|
import { ConfigService } from '@services/config.service';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class DataService {
|
export class DataService {
|
||||||
baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
|
baseUrl: string;
|
||||||
private apiUrl = `${this.baseUrl}/user-groups?page=1&itemsPerPage=1000`;
|
private apiUrl: string;
|
||||||
|
|
||||||
constructor(private http: HttpClient) {}
|
constructor(private http: HttpClient, private configService: ConfigService) {
|
||||||
|
this.baseUrl = this.configService.apiUrl;
|
||||||
|
this.apiUrl = `${this.baseUrl}/user-groups?page=1&itemsPerPage=1000`;
|
||||||
|
}
|
||||||
|
|
||||||
getUserGroups(filters: { [key: string]: string }): Observable<{ totalItems: any; data: any }> {
|
getUserGroups(filters: { [key: string]: string }): Observable<{ totalItems: any; data: any }> {
|
||||||
const params = new HttpParams({ fromObject: filters });
|
const params = new HttpParams({ fromObject: filters });
|
||||||
|
|
|
@ -1,21 +1,30 @@
|
||||||
table {
|
.header-container {
|
||||||
width: 100%;
|
display: flex;
|
||||||
margin-top: 50px;
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 10px 10px;
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-container {
|
.header-container-title {
|
||||||
|
flex-grow: 1;
|
||||||
|
text-align: left;
|
||||||
|
margin-left: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 0 5px;
|
margin: 1.5rem 0rem 1.5rem 0rem;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.divider {
|
|
||||||
margin: 20px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-string {
|
.search-string {
|
||||||
flex: 2;
|
flex: 2;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
|
@ -26,20 +35,12 @@ table {
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mat-elevation-z8 {
|
.mat-elevation-z8 {
|
||||||
box-shadow: 0px 0px 0px rgba(0,0,0,0.2);
|
box-shadow: 0px 0px 0px rgba(0, 0, 0, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.paginator-container {
|
.paginator-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: end;
|
justify-content: end;
|
||||||
margin-bottom: 30px;
|
margin-bottom: 30px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,24 +1,25 @@
|
||||||
<div class="header-container">
|
<div class="header-container">
|
||||||
<h2 class="title">{{ 'adminRolesTitle' | translate }}</h2>
|
<div class="header-container-title">
|
||||||
|
<h2>{{ 'adminRolesTitle' | translate }}</h2>
|
||||||
|
</div>
|
||||||
<div class="images-button-row">
|
<div class="images-button-row">
|
||||||
<button mat-flat-button color="primary" (click)="addUser()">
|
<button class="action-button" (click)="addUser()">
|
||||||
{{ 'addRole' | translate }}
|
{{ 'addRole' | translate }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<mat-divider class="divider"></mat-divider>
|
|
||||||
<div class="search-container">
|
<div class="search-container">
|
||||||
<mat-form-field appearance="fill" class="search-string">
|
<mat-form-field appearance="fill" class="search-string">
|
||||||
<mat-label>{{ 'searchRoleLabel' | translate }}</mat-label>
|
<mat-label>{{ 'searchRoleLabel' | translate }}</mat-label>
|
||||||
<input matInput placeholder="{{ 'searchPlaceholder' | translate }}" [(ngModel)]="filters['name']" (keyup.enter)="search()">
|
<input matInput placeholder="{{ 'searchPlaceholder' | translate }}" [(ngModel)]="filters['name']"
|
||||||
|
(keyup.enter)="search()">
|
||||||
<mat-icon matSuffix>search</mat-icon>
|
<mat-icon matSuffix>search</mat-icon>
|
||||||
<mat-hint>{{ 'searchHint' | translate }}</mat-hint>
|
<mat-hint>{{ 'searchHint' | translate }}</mat-hint>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="loading" class="loading-container">
|
<app-loading [isLoading]="loading"></app-loading>
|
||||||
<mat-spinner></mat-spinner>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div *ngIf="!loading">
|
<div *ngIf="!loading">
|
||||||
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
|
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
|
||||||
|
@ -33,7 +34,8 @@
|
||||||
<button mat-icon-button color="primary" (click)="editRole(role)">
|
<button mat-icon-button color="primary" (click)="editRole(role)">
|
||||||
<mat-icon>edit</mat-icon>
|
<mat-icon>edit</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
<button mat-icon-button color="warn" (click)="deleteRole(role)" [disabled]="role.permissions.includes('ROLE_SUPER_ADMIN')">
|
<button mat-icon-button color="warn" (click)="deleteRole(role)"
|
||||||
|
[disabled]="role.permissions.includes('ROLE_SUPER_ADMIN')">
|
||||||
<mat-icon>delete</mat-icon>
|
<mat-icon>delete</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
</td>
|
</td>
|
||||||
|
@ -44,10 +46,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="paginator-container">
|
<div class="paginator-container">
|
||||||
<mat-paginator [length]="length"
|
<mat-paginator [length]="length" [pageSize]="itemsPerPage" [pageIndex]="page" [pageSizeOptions]="pageSizeOptions"
|
||||||
[pageSize]="itemsPerPage"
|
(page)="onPageChange($event)">
|
||||||
[pageIndex]="page"
|
|
||||||
[pageSizeOptions]="pageSizeOptions"
|
|
||||||
(page)="onPageChange($event)">
|
|
||||||
</mat-paginator>
|
</mat-paginator>
|
||||||
</div>
|
</div>
|
|
@ -4,7 +4,7 @@ import { MatDialog } from '@angular/material/dialog';
|
||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { ToastrService } from 'ngx-toastr';
|
import { ToastrService } from 'ngx-toastr';
|
||||||
import { DataService } from './data.service';
|
import { DataService } from './data.service';
|
||||||
import { of } from 'rxjs';
|
import { ConfigService } from '@services/config.service';
|
||||||
import { MatDivider } from '@angular/material/divider';
|
import { MatDivider } from '@angular/material/divider';
|
||||||
import { MatFormField } from '@angular/material/form-field';
|
import { MatFormField } from '@angular/material/form-field';
|
||||||
import { MatLabel } from '@angular/material/form-field';
|
import { MatLabel } from '@angular/material/form-field';
|
||||||
|
@ -12,6 +12,7 @@ import { MatIcon } from '@angular/material/icon';
|
||||||
import { MatHint } from '@angular/material/form-field';
|
import { MatHint } from '@angular/material/form-field';
|
||||||
import { MatPaginator } from '@angular/material/paginator';
|
import { MatPaginator } from '@angular/material/paginator';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { LoadingComponent } from '../../../../shared/loading/loading.component';
|
||||||
describe('RolesComponent', () => {
|
describe('RolesComponent', () => {
|
||||||
let component: RolesComponent;
|
let component: RolesComponent;
|
||||||
let fixture: ComponentFixture<RolesComponent>;
|
let fixture: ComponentFixture<RolesComponent>;
|
||||||
|
@ -19,22 +20,25 @@ describe('RolesComponent', () => {
|
||||||
let mockHttpClient: jasmine.SpyObj<HttpClient>;
|
let mockHttpClient: jasmine.SpyObj<HttpClient>;
|
||||||
let mockToastrService: jasmine.SpyObj<ToastrService>;
|
let mockToastrService: jasmine.SpyObj<ToastrService>;
|
||||||
let mockDataService: jasmine.SpyObj<DataService>;
|
let mockDataService: jasmine.SpyObj<DataService>;
|
||||||
|
let mockConfigService: jasmine.SpyObj<ConfigService>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
const matDialogSpy = jasmine.createSpyObj('MatDialog', ['open']);
|
const matDialogSpy = jasmine.createSpyObj('MatDialog', ['open']);
|
||||||
const httpClientSpy = jasmine.createSpyObj('HttpClient', ['get', 'post', 'put', 'delete']);
|
const httpClientSpy = jasmine.createSpyObj('HttpClient', ['get', 'post', 'put', 'delete']);
|
||||||
const toastrServiceSpy = jasmine.createSpyObj('ToastrService', ['success', 'error']);
|
const toastrServiceSpy = jasmine.createSpyObj('ToastrService', ['success', 'error']);
|
||||||
const dataServiceSpy = jasmine.createSpyObj('DataService', ['getRoles']);
|
const dataServiceSpy = jasmine.createSpyObj('DataService', ['getRoles']);
|
||||||
|
const configServiceSpy = jasmine.createSpyObj('ConfigService', [], { apiUrl: 'http://mock-api-url' });
|
||||||
|
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
declarations: [RolesComponent],
|
declarations: [RolesComponent, LoadingComponent],
|
||||||
imports: [MatDivider, MatFormField, MatLabel, MatIcon, MatHint, MatPaginator,
|
imports: [MatDivider, MatFormField, MatLabel, MatIcon, MatHint, MatPaginator,
|
||||||
TranslateModule.forRoot()],
|
TranslateModule.forRoot()],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: MatDialog, useValue: matDialogSpy },
|
{ provide: MatDialog, useValue: matDialogSpy },
|
||||||
{ provide: HttpClient, useValue: httpClientSpy },
|
{ provide: HttpClient, useValue: httpClientSpy },
|
||||||
{ provide: ToastrService, useValue: toastrServiceSpy },
|
{ provide: ToastrService, useValue: toastrServiceSpy },
|
||||||
{ provide: DataService, useValue: dataServiceSpy }
|
{ provide: DataService, useValue: dataServiceSpy },
|
||||||
|
{ provide: ConfigService, useValue: configServiceSpy }
|
||||||
]
|
]
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
});
|
});
|
||||||
|
@ -46,6 +50,7 @@ describe('RolesComponent', () => {
|
||||||
mockHttpClient = TestBed.inject(HttpClient) as jasmine.SpyObj<HttpClient>;
|
mockHttpClient = TestBed.inject(HttpClient) as jasmine.SpyObj<HttpClient>;
|
||||||
mockToastrService = TestBed.inject(ToastrService) as jasmine.SpyObj<ToastrService>;
|
mockToastrService = TestBed.inject(ToastrService) as jasmine.SpyObj<ToastrService>;
|
||||||
mockDataService = TestBed.inject(DataService) as jasmine.SpyObj<DataService>;
|
mockDataService = TestBed.inject(DataService) as jasmine.SpyObj<DataService>;
|
||||||
|
mockConfigService = TestBed.inject(ConfigService) as jasmine.SpyObj<ConfigService>;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it('should create', () => {
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { DataService } from "./data.service";
|
||||||
import { PageEvent } from "@angular/material/paginator";
|
import { PageEvent } from "@angular/material/paginator";
|
||||||
import { DeleteModalComponent } from '../../../../shared/delete_modal/delete-modal/delete-modal.component';
|
import { DeleteModalComponent } from '../../../../shared/delete_modal/delete-modal/delete-modal.component';
|
||||||
import { AddRoleModalComponent } from './add-role-modal/add-role-modal.component';
|
import { AddRoleModalComponent } from './add-role-modal/add-role-modal.component';
|
||||||
|
import { ConfigService } from '@services/config.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-roles',
|
selector: 'app-roles',
|
||||||
|
@ -14,7 +15,7 @@ import { AddRoleModalComponent } from './add-role-modal/add-role-modal.component
|
||||||
styleUrls: ['./roles.component.css']
|
styleUrls: ['./roles.component.css']
|
||||||
})
|
})
|
||||||
export class RolesComponent implements OnInit {
|
export class RolesComponent implements OnInit {
|
||||||
baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
|
baseUrl: string = this.configService.apiUrl;
|
||||||
dataSource = new MatTableDataSource<any>();
|
dataSource = new MatTableDataSource<any>();
|
||||||
filters: { [key: string]: string } = {};
|
filters: { [key: string]: string } = {};
|
||||||
loading: boolean = false;
|
loading: boolean = false;
|
||||||
|
@ -48,7 +49,8 @@ export class RolesComponent implements OnInit {
|
||||||
public dialog: MatDialog,
|
public dialog: MatDialog,
|
||||||
private http: HttpClient,
|
private http: HttpClient,
|
||||||
private dataService: DataService,
|
private dataService: DataService,
|
||||||
private toastService: ToastrService
|
private toastService: ToastrService,
|
||||||
|
private configService: ConfigService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
|
|
|
@ -1,30 +1,12 @@
|
||||||
.full-width {
|
.user-form {
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
.form-container {
|
|
||||||
padding: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-group {
|
|
||||||
margin-top: 20px;
|
|
||||||
margin-bottom: 26px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.full-width {
|
|
||||||
width: 100%;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox-group {
|
|
||||||
margin: 15px 0;
|
|
||||||
align-items: flex-start;
|
|
||||||
}
|
|
||||||
|
|
||||||
.time-fields {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 15px; /* Espacio entre los campos */
|
flex-direction: column;
|
||||||
|
margin-top: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.time-field {
|
.action-container {
|
||||||
flex: 1;
|
display: flex;
|
||||||
}
|
justify-content: flex-end;
|
||||||
|
gap: 1em;
|
||||||
|
padding: 1.5em;
|
||||||
|
}
|
|
@ -1,4 +1,6 @@
|
||||||
<h1 mat-dialog-title>{{ 'dialogTitleAddUser' | translate }}</h1>
|
<app-loading [isLoading]="loading"></app-loading>
|
||||||
|
|
||||||
|
<h1 mat-dialog-title>{{ isEditMode ? ('dialogTitleEditUser' | translate) : ('dialogTitleAddUser' | translate) }}</h1>
|
||||||
<mat-dialog-content class="form-container">
|
<mat-dialog-content class="form-container">
|
||||||
<form [formGroup]="userForm" class="user-form">
|
<form [formGroup]="userForm" class="user-form">
|
||||||
<mat-form-field appearance="fill" class="full-width">
|
<mat-form-field appearance="fill" class="full-width">
|
||||||
|
@ -27,9 +29,18 @@
|
||||||
</mat-option>
|
</mat-option>
|
||||||
</mat-select>
|
</mat-select>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="fill" class="full-width">
|
||||||
|
<mat-label>Vista tarjetas</mat-label>
|
||||||
|
<mat-select formControlName="groupsView" required>
|
||||||
|
<mat-option *ngFor="let option of views" [value]="option.value">
|
||||||
|
{{ option.name }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
</form>
|
</form>
|
||||||
</mat-dialog-content>
|
</mat-dialog-content>
|
||||||
<mat-dialog-actions align="end">
|
<mat-dialog-actions class="action-container">
|
||||||
<button mat-button (click)="onNoClick()">{{ 'buttonCancel' | translate }}</button>
|
<button class="ordinary-button" (click)="onNoClick()">{{ 'buttonCancel' | translate }}</button>
|
||||||
<button mat-button (click)="onSubmit()">{{ 'buttonAdd' | translate }}</button>
|
<button class="submit-button" (click)="onSubmit()" [disabled]="userForm.invalid">{{ isEditMode ? ('buttonEdit' | translate) : ('buttonAdd' | translate) }}</button>
|
||||||
</mat-dialog-actions>
|
</mat-dialog-actions>
|
|
@ -1,9 +1,10 @@
|
||||||
import { Component, EventEmitter, Inject, OnInit, Output } from '@angular/core';
|
import { Component, EventEmitter, Inject, OnInit, Output } from '@angular/core';
|
||||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
import {ToastrService} from "ngx-toastr";
|
import { ToastrService } from "ngx-toastr";
|
||||||
import {HttpClient} from "@angular/common/http";
|
import { HttpClient } from "@angular/common/http";
|
||||||
import {DataService} from "../data.service";
|
import { DataService } from "../data.service";
|
||||||
|
import { ConfigService } from '@services/config.service';
|
||||||
|
|
||||||
interface UserGroup {
|
interface UserGroup {
|
||||||
'@id': string;
|
'@id': string;
|
||||||
|
@ -17,12 +18,20 @@ interface UserGroup {
|
||||||
styleUrls: ['./add-user-modal.component.css']
|
styleUrls: ['./add-user-modal.component.css']
|
||||||
})
|
})
|
||||||
export class AddUserModalComponent implements OnInit {
|
export class AddUserModalComponent implements OnInit {
|
||||||
baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
|
baseUrl: string;
|
||||||
@Output() userAdded = new EventEmitter<void>();
|
@Output() userAdded = new EventEmitter<void>();
|
||||||
|
@Output() userEdited = new EventEmitter<void>();
|
||||||
userForm: FormGroup<any>;
|
userForm: FormGroup<any>;
|
||||||
userGroups: UserGroup[] = [];
|
userGroups: UserGroup[] = [];
|
||||||
organizationalUnits: any[] = [];
|
organizationalUnits: any[] = [];
|
||||||
userId: string | null = null;
|
userId: string | null = null;
|
||||||
|
loading: boolean = false;
|
||||||
|
isEditMode: boolean = false;
|
||||||
|
|
||||||
|
protected views = [
|
||||||
|
{ value: 'card', name: 'Tarjetas' },
|
||||||
|
{ value: 'list', name: 'Listado' },
|
||||||
|
];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public dialogRef: MatDialogRef<AddUserModalComponent>,
|
public dialogRef: MatDialogRef<AddUserModalComponent>,
|
||||||
|
@ -30,46 +39,60 @@ export class AddUserModalComponent implements OnInit {
|
||||||
private http: HttpClient,
|
private http: HttpClient,
|
||||||
private fb: FormBuilder,
|
private fb: FormBuilder,
|
||||||
private dataService: DataService,
|
private dataService: DataService,
|
||||||
private toastService: ToastrService
|
private toastService: ToastrService,
|
||||||
|
private configService: ConfigService
|
||||||
) {
|
) {
|
||||||
|
this.baseUrl = this.configService.apiUrl;
|
||||||
this.userForm = this.fb.group({
|
this.userForm = this.fb.group({
|
||||||
username: ['', Validators.required],
|
username: ['', Validators.required],
|
||||||
password: ['', Validators.required],
|
password: ['', Validators.required],
|
||||||
role: ['', Validators.required],
|
role: ['', Validators.required],
|
||||||
|
groupsView: ['card', Validators.required],
|
||||||
organizationalUnits: [[]]
|
organizationalUnits: [[]]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
this.isEditMode = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.dataService.getUserGroups().subscribe((data) => {
|
this.dataService.getUserGroups().subscribe((data) => {
|
||||||
this.userGroups = data['hydra:member'];
|
this.userGroups = data['hydra:member'];
|
||||||
});
|
});
|
||||||
|
|
||||||
this.dataService.getOrganizationalUnits().subscribe((data) => {
|
this.dataService.getOrganizationalUnits().subscribe((data) => {
|
||||||
this.organizationalUnits = data['hydra:member'].filter((item: any) => item.type === 'organizational-unit');
|
this.organizationalUnits = data['hydra:member'].filter((item: any) => item.type === 'organizational-unit');
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this.data) {
|
if (this.data) {
|
||||||
this.load()
|
this.load();
|
||||||
|
} else {
|
||||||
|
this.userForm.get('password')?.setValidators([Validators.required]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
load(): void {
|
load(): void {
|
||||||
|
this.loading = true;
|
||||||
this.dataService.getUser(this.data).subscribe({
|
this.dataService.getUser(this.data).subscribe({
|
||||||
next: (response) => {
|
next: (response) => {
|
||||||
console.log(response);
|
|
||||||
|
|
||||||
const organizationalUnitIds = response.allowedOrganizationalUnits.map((unit: any) => unit['@id']);
|
const organizationalUnitIds = response.allowedOrganizationalUnits.map((unit: any) => unit['@id']);
|
||||||
|
|
||||||
// Patch the values to the form
|
|
||||||
this.userForm.patchValue({
|
this.userForm.patchValue({
|
||||||
username: response.username,
|
username: response.username,
|
||||||
role: response.userGroups[0]['@id'],
|
role: response.userGroups.length > 0 ? response.userGroups[0]['@id'] : null,
|
||||||
organizationalUnits: organizationalUnitIds
|
organizationalUnits: organizationalUnitIds,
|
||||||
|
groupsView: response.groupsView
|
||||||
});
|
});
|
||||||
|
|
||||||
this.userId = response['@id'];
|
this.userId = response['@id'];
|
||||||
|
this.userForm.get('password')?.clearValidators();
|
||||||
|
this.userForm.get('password')?.updateValueAndValidity();
|
||||||
|
this.loading = false;
|
||||||
},
|
},
|
||||||
error: (err) => {
|
error: (err) => {
|
||||||
console.error('Error fetching remote calendar:', err);
|
this.loading = false;
|
||||||
|
console.error('Error fetching user:', err);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -80,37 +103,49 @@ export class AddUserModalComponent implements OnInit {
|
||||||
|
|
||||||
onSubmit(): void {
|
onSubmit(): void {
|
||||||
if (this.userForm.valid) {
|
if (this.userForm.valid) {
|
||||||
const payload = {
|
const payload: any = {
|
||||||
username: this.userForm.value.username,
|
username: this.userForm.value.username,
|
||||||
allowedOrganizationalUnits: this.userForm.value.organizationalUnit,
|
allowedOrganizationalUnits: this.userForm.value.organizationalUnits,
|
||||||
password: this.userForm.value.password,
|
|
||||||
enabled: true,
|
enabled: true,
|
||||||
userGroups: [this.userForm.value.role ]
|
userGroups: [this.userForm.value.role],
|
||||||
|
groupsView: this.userForm.value.groupsView
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (!this.userId && this.userForm.value.password) {
|
||||||
|
payload.password = this.userForm.value.password;
|
||||||
|
} else if (this.userId && this.userForm.value.password.trim() !== '') {
|
||||||
|
payload.password = this.userForm.value.password;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.loading = true;
|
||||||
|
|
||||||
if (this.userId) {
|
if (this.userId) {
|
||||||
this.http.put(`${this.baseUrl}${this.userId}`, payload).subscribe(
|
this.http.put(`${this.baseUrl}${this.userId}`, payload).subscribe(
|
||||||
(response) => {
|
(response) => {
|
||||||
this.toastService.success('Usuario editado correctamente');
|
this.toastService.success('Usuario editado correctamente');
|
||||||
|
this.userEdited.emit();
|
||||||
this.dialogRef.close();
|
this.dialogRef.close();
|
||||||
|
this.loading = false;
|
||||||
},
|
},
|
||||||
(error) => {
|
(error) => {
|
||||||
this.toastService.error(error['error']['hydra:description']);
|
this.toastService.error(error['error']['hydra:description']);
|
||||||
console.error('Error al editar el rol', error);
|
this.loading = false;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
this.http.post(`${this.baseUrl}/users`, payload).subscribe(
|
this.http.post(`${this.baseUrl}/users`, payload).subscribe(
|
||||||
(response) => {
|
(response) => {
|
||||||
this.toastService.success('Usuario añadido correctamente');
|
this.toastService.success('Usuario añadido correctamente');
|
||||||
|
this.userAdded.emit();
|
||||||
this.dialogRef.close();
|
this.dialogRef.close();
|
||||||
|
this.loading = false;
|
||||||
},
|
},
|
||||||
(error) => {
|
(error) => {
|
||||||
this.toastService.error(error['error']['hydra:description']);
|
this.toastService.error(error['error']['hydra:description']);
|
||||||
console.error('Error al añadir añadido', error);
|
this.loading = false;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,6 +1,5 @@
|
||||||
.user-form .form-field {
|
.user-form .form-field {
|
||||||
display: block;
|
display: block;
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.checkbox-group label {
|
.checkbox-group label {
|
||||||
|
@ -17,3 +16,14 @@ mat-spinner {
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
align-self: center;
|
align-self: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.action-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 1em;
|
||||||
|
padding: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-container {
|
||||||
|
margin-top: 2em;
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
<h1 mat-dialog-title>{{ 'dialogTitleEditUser' | translate }}</h1>
|
<h1 mat-dialog-title>{{ 'dialogTitleChangePassword' | translate }}</h1>
|
||||||
<mat-dialog-content class="form-container">
|
<mat-dialog-content class="form-container">
|
||||||
<form [formGroup]="userForm" class="user-form">
|
<form [formGroup]="userForm" class="user-form">
|
||||||
<mat-form-field class="form-field">
|
<mat-form-field class="form-field">
|
||||||
|
@ -23,7 +23,7 @@
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</mat-dialog-content>
|
</mat-dialog-content>
|
||||||
<mat-dialog-actions align="end">
|
<mat-dialog-actions class="action-container">
|
||||||
<button mat-button (click)="onNoClick()">{{ 'buttonCancel' | translate }}</button>
|
<button class="ordinary-button" (click)="onNoClick()">{{ 'buttonCancel' | translate }}</button>
|
||||||
<button mat-button (click)="onSubmit()" [disabled]="loading">{{ 'buttonEdit' | translate }}</button>
|
<button class="submit-button" (click)="onSubmit()" [disabled]="loading">{{ 'buttonEdit' | translate }}</button>
|
||||||
</mat-dialog-actions>
|
</mat-dialog-actions>
|
|
@ -3,15 +3,19 @@ import { Injectable } from '@angular/core';
|
||||||
import {HttpClient, HttpParams} from '@angular/common/http';
|
import {HttpClient, HttpParams} from '@angular/common/http';
|
||||||
import { Observable, throwError } from 'rxjs';
|
import { Observable, throwError } from 'rxjs';
|
||||||
import { catchError, map } from 'rxjs/operators';
|
import { catchError, map } from 'rxjs/operators';
|
||||||
|
import { ConfigService } from '@services/config.service';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class DataService {
|
export class DataService {
|
||||||
baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
|
baseUrl: string;
|
||||||
private apiUrl = `${this.baseUrl}/users?page=1&itemsPerPage=1000`;
|
private apiUrl: string;
|
||||||
|
|
||||||
constructor(private http: HttpClient) {}
|
constructor(private http: HttpClient, private configService: ConfigService) {
|
||||||
|
this.baseUrl = this.configService.apiUrl;
|
||||||
|
this.apiUrl = `${this.baseUrl}/users?page=1&itemsPerPage=1000`;
|
||||||
|
}
|
||||||
|
|
||||||
getUsers(filters: { [key: string]: string }): Observable<{ totalItems: any; data: any }> {
|
getUsers(filters: { [key: string]: string }): Observable<{ totalItems: any; data: any }> {
|
||||||
const params = new HttpParams({ fromObject: filters });
|
const params = new HttpParams({ fromObject: filters });
|
||||||
|
|
|
@ -1,6 +1,19 @@
|
||||||
|
.header-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 10px 10px;
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-container-title {
|
||||||
|
flex-grow: 1;
|
||||||
|
text-align: left;
|
||||||
|
margin-left: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
table {
|
table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-top: 50px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-container {
|
.search-container {
|
||||||
|
@ -8,14 +21,10 @@ table {
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 0 5px;
|
margin: 1.5rem 0rem 1.5rem 0rem;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.divider {
|
|
||||||
margin: 20px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-string {
|
.search-string {
|
||||||
flex: 2;
|
flex: 2;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
|
@ -26,13 +35,6 @@ table {
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mat-elevation-z8 {
|
.mat-elevation-z8 {
|
||||||
box-shadow: 0px 0px 0px rgba(0,0,0,0.2);
|
box-shadow: 0px 0px 0px rgba(0,0,0,0.2);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,30 +1,40 @@
|
||||||
<div class="header-container">
|
<div class="header-container">
|
||||||
<h2 class="title">{{ 'adminImagesTitle' | translate }}</h2>
|
<div class="header-container-title">
|
||||||
|
<h2>{{ 'adminUsersTitle' | translate }}</h2>
|
||||||
|
</div>
|
||||||
<div class="images-button-row">
|
<div class="images-button-row">
|
||||||
<button mat-flat-button color="primary" (click)="addUser()">
|
<button class="action-button" (click)="addUser()">
|
||||||
{{ 'addUser' | translate }}
|
{{ 'addUser' | translate }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<mat-divider class="divider"></mat-divider>
|
|
||||||
<div class="search-container">
|
<div class="search-container">
|
||||||
<mat-form-field appearance="fill" class="search-string">
|
<mat-form-field appearance="fill" class="search-string">
|
||||||
<mat-label>{{ 'searchLabel' | translate }}</mat-label>
|
<mat-label>{{ 'searchLabel' | translate }}</mat-label>
|
||||||
<input matInput placeholder="{{ 'searchPlaceholder' | translate }}" [(ngModel)]="filters['name']" (keyup.enter)="search()">
|
<input matInput placeholder="{{ 'searchPlaceholder' | translate }}" [(ngModel)]="filters['name']"
|
||||||
|
(keyup.enter)="search()">
|
||||||
<mat-icon matSuffix>search</mat-icon>
|
<mat-icon matSuffix>search</mat-icon>
|
||||||
<mat-hint>{{ 'searchHint' | translate }}</mat-hint>
|
<mat-hint>{{ 'searchHint' | translate }}</mat-hint>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="loading" class="loading-container">
|
<app-loading [isLoading]="loading"></app-loading>
|
||||||
<mat-spinner></mat-spinner>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div *ngIf="!loading">
|
<div *ngIf="!loading">
|
||||||
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
|
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
|
||||||
<ng-container *ngFor="let column of columns" [matColumnDef]="column.columnDef">
|
<ng-container *ngFor="let column of columns" [matColumnDef]="column.columnDef">
|
||||||
<th mat-header-cell *matHeaderCellDef> {{ column.header }} </th>
|
<th mat-header-cell *matHeaderCellDef> {{ column.header }} </th>
|
||||||
<td mat-cell *matCellDef="let user"> {{ column.cell(user) }} </td>
|
<td mat-cell *matCellDef="let user">
|
||||||
|
<ng-container *ngIf="column.columnDef === 'groupsView'">
|
||||||
|
<mat-chip>
|
||||||
|
{{ user[column.columnDef] === 'card' ? 'Vista tarjetas' : 'Listado' }}
|
||||||
|
</mat-chip>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container *ngIf="column.columnDef !== 'groupsView'">
|
||||||
|
{{ column.cell(user) }}
|
||||||
|
</ng-container>
|
||||||
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="actions">
|
<ng-container matColumnDef="actions">
|
||||||
|
@ -44,10 +54,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="paginator-container">
|
<div class="paginator-container">
|
||||||
<mat-paginator [length]="length"
|
<mat-paginator [length]="length" [pageSize]="itemsPerPage" [pageIndex]="page" [pageSizeOptions]="pageSizeOptions"
|
||||||
[pageSize]="itemsPerPage"
|
(page)="onPageChange($event)">
|
||||||
[pageIndex]="page"
|
|
||||||
[pageSizeOptions]="pageSizeOptions"
|
|
||||||
(page)="onPageChange($event)">
|
|
||||||
</mat-paginator>
|
</mat-paginator>
|
||||||
</div>
|
</div>
|
|
@ -7,6 +7,7 @@ import { ToastrService } from 'ngx-toastr';
|
||||||
import { of } from 'rxjs';
|
import { of } from 'rxjs';
|
||||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { ConfigService } from '@services/config.service';
|
||||||
|
|
||||||
class MockToastrService {
|
class MockToastrService {
|
||||||
success() {}
|
success() {}
|
||||||
|
@ -18,6 +19,11 @@ describe('UsersComponent', () => {
|
||||||
let fixture: ComponentFixture<UsersComponent>;
|
let fixture: ComponentFixture<UsersComponent>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
|
const mockConfigService = {
|
||||||
|
apiUrl: 'http://mock-api-url',
|
||||||
|
mercureUrl: 'http://mock-mercure-url'
|
||||||
|
};
|
||||||
|
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
declarations: [UsersComponent],
|
declarations: [UsersComponent],
|
||||||
imports: [
|
imports: [
|
||||||
|
@ -28,6 +34,7 @@ describe('UsersComponent', () => {
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: ToastrService, useClass: MockToastrService },
|
{ provide: ToastrService, useClass: MockToastrService },
|
||||||
|
{ provide: ConfigService, useValue: mockConfigService }
|
||||||
],
|
],
|
||||||
schemas: [NO_ERRORS_SCHEMA], // Ignorar elementos desconocidos
|
schemas: [NO_ERRORS_SCHEMA], // Ignorar elementos desconocidos
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { AddUserModalComponent } from './add-user-modal/add-user-modal.component
|
||||||
import { DeleteModalComponent } from '../../../../shared/delete_modal/delete-modal/delete-modal.component';
|
import { DeleteModalComponent } from '../../../../shared/delete_modal/delete-modal/delete-modal.component';
|
||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { ToastrService } from 'ngx-toastr';
|
import { ToastrService } from 'ngx-toastr';
|
||||||
import { DataService } from "./data.service";
|
import { ConfigService } from '@services/config.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-users',
|
selector: 'app-users',
|
||||||
|
@ -13,7 +13,8 @@ import { DataService } from "./data.service";
|
||||||
styleUrls: ['./users.component.css']
|
styleUrls: ['./users.component.css']
|
||||||
})
|
})
|
||||||
export class UsersComponent implements OnInit {
|
export class UsersComponent implements OnInit {
|
||||||
baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
|
baseUrl: string;
|
||||||
|
private apiUrl: string;
|
||||||
dataSource = new MatTableDataSource<any>();
|
dataSource = new MatTableDataSource<any>();
|
||||||
filters: { [key: string]: string } = {};
|
filters: { [key: string]: string } = {};
|
||||||
loading: boolean = false;
|
loading: boolean = false;
|
||||||
|
@ -32,6 +33,11 @@ export class UsersComponent implements OnInit {
|
||||||
header: 'Nombre de Usuario',
|
header: 'Nombre de Usuario',
|
||||||
cell: (user: any) => `${user.username}`
|
cell: (user: any) => `${user.username}`
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
columnDef: 'groupsView',
|
||||||
|
header: 'Vista de Grupos',
|
||||||
|
cell: (user: any) => `${user.groupsView}`
|
||||||
|
},
|
||||||
{
|
{
|
||||||
columnDef: 'allowedOrganizationalUnits',
|
columnDef: 'allowedOrganizationalUnits',
|
||||||
header: 'Unidades Organizacionales Permitidas',
|
header: 'Unidades Organizacionales Permitidas',
|
||||||
|
@ -45,14 +51,15 @@ export class UsersComponent implements OnInit {
|
||||||
];
|
];
|
||||||
displayedColumns = [...this.columns.map(column => column.columnDef), 'actions'];
|
displayedColumns = [...this.columns.map(column => column.columnDef), 'actions'];
|
||||||
|
|
||||||
private apiUrl = `${this.baseUrl}/users`;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public dialog: MatDialog,
|
public dialog: MatDialog,
|
||||||
|
private configService: ConfigService,
|
||||||
private http: HttpClient,
|
private http: HttpClient,
|
||||||
private dataService: DataService,
|
|
||||||
private toastService: ToastrService
|
private toastService: ToastrService
|
||||||
) {}
|
) {
|
||||||
|
this.baseUrl = this.configService.apiUrl;
|
||||||
|
this.apiUrl = `${this.baseUrl}/users`;
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.search();
|
this.search();
|
||||||
|
@ -87,6 +94,10 @@ export class UsersComponent implements OnInit {
|
||||||
data: user['@id']
|
data: user['@id']
|
||||||
});
|
});
|
||||||
|
|
||||||
|
dialogRef.componentInstance.userEdited.subscribe(() => {
|
||||||
|
this.search();
|
||||||
|
});
|
||||||
|
|
||||||
dialogRef.afterClosed().subscribe(result => {
|
dialogRef.afterClosed().subscribe(result => {
|
||||||
if (result) {
|
if (result) {
|
||||||
this.search();
|
this.search();
|
||||||
|
@ -124,4 +135,4 @@ export class UsersComponent implements OnInit {
|
||||||
this.length = event.length;
|
this.length = event.length;
|
||||||
this.search();
|
this.search();
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,15 +1,20 @@
|
||||||
.title {
|
.header-container {
|
||||||
font-size: 24px;
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 10px 10px;
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-container-title {
|
||||||
|
flex-grow: 1;
|
||||||
|
text-align: left;
|
||||||
|
margin-left: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.calendar-button-row {
|
.calendar-button-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-start;
|
gap: 15px;
|
||||||
margin-top: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.divider {
|
|
||||||
margin: 20px 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.lists-container {
|
.lists-container {
|
||||||
|
@ -23,15 +28,14 @@
|
||||||
|
|
||||||
table {
|
table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-top: 50px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-container {
|
.search-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 0 5px;
|
margin: 1.5rem 0rem 1.5rem 0rem;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,19 +49,12 @@ table {
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mat-elevation-z8 {
|
.mat-elevation-z8 {
|
||||||
box-shadow: 0px 0px 0px rgba(0,0,0,0.2);
|
box-shadow: 0px 0px 0px rgba(0, 0, 0, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.paginator-container {
|
.paginator-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: end;
|
justify-content: end;
|
||||||
margin-bottom: 30px;
|
margin-bottom: 30px;
|
||||||
}
|
}
|
|
@ -2,30 +2,33 @@
|
||||||
<button mat-icon-button color="primary" (click)="iniciarTour()">
|
<button mat-icon-button color="primary" (click)="iniciarTour()">
|
||||||
<mat-icon>help</mat-icon>
|
<mat-icon>help</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
<h2 joyrideStep="titleStep" text="{{ 'titleStepText' | translate }}" class="title">{{ 'adminCalendarsTitle' | translate }}</h2>
|
<div class="header-container-title">
|
||||||
|
<h2 joyrideStep="titleStep" text="{{ 'titleStepText' | translate }}">{{ 'adminCalendarsTitle' | translate }}</h2>
|
||||||
|
</div>
|
||||||
<div class="calendar-button-row">
|
<div class="calendar-button-row">
|
||||||
<button joyrideStep="addButtonStep" text="{{ 'addButtonStepText' | translate }}" mat-flat-button color="primary" (click)="addImage()">
|
<button joyrideStep="addButtonStep" text="{{ 'addButtonStepText' | translate }}" class="action-button"
|
||||||
|
(click)="addCalendar()">
|
||||||
{{ 'addCalendar' | translate }}
|
{{ 'addCalendar' | translate }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<mat-divider class="divider"></mat-divider>
|
|
||||||
|
|
||||||
<div class="search-container">
|
<div class="search-container">
|
||||||
<mat-form-field joyrideStep="searchStep" text="{{ 'searchStepText' | translate }}" appearance="fill" class="search-string">
|
<mat-form-field joyrideStep="searchStep" text="{{ 'searchStepText' | translate }}" appearance="fill"
|
||||||
|
class="search-string">
|
||||||
<mat-label>{{ 'searchCalendarLabel' | translate }}</mat-label>
|
<mat-label>{{ 'searchCalendarLabel' | translate }}</mat-label>
|
||||||
<input matInput placeholder="{{ 'searchPlaceholder' | translate }}" [(ngModel)]="filters['name']" (keyup.enter)="search()">
|
<input matInput placeholder="{{ 'searchPlaceholder' | translate }}" [(ngModel)]="filters['name']"
|
||||||
|
(keyup.enter)="search()">
|
||||||
<mat-icon matSuffix>search</mat-icon>
|
<mat-icon matSuffix>search</mat-icon>
|
||||||
<mat-hint>{{ 'searchHint' | translate }}</mat-hint>
|
<mat-hint>{{ 'searchHint' | translate }}</mat-hint>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="loading" class="loading-container">
|
<app-loading [isLoading]="loading"></app-loading>
|
||||||
<mat-spinner></mat-spinner>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div *ngIf="!loading">
|
<div *ngIf="!loading">
|
||||||
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8" joyrideStep="tableStep" text="{{ 'tableStepText' | translate }}">
|
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8" joyrideStep="tableStep"
|
||||||
|
text="{{ 'tableStepText' | translate }}">
|
||||||
<ng-container *ngFor="let column of columns" [matColumnDef]="column.columnDef">
|
<ng-container *ngFor="let column of columns" [matColumnDef]="column.columnDef">
|
||||||
<th mat-header-cell *matHeaderCellDef> {{ column.header }} </th>
|
<th mat-header-cell *matHeaderCellDef> {{ column.header }} </th>
|
||||||
<td mat-cell *matCellDef="let image">
|
<td mat-cell *matCellDef="let image">
|
||||||
|
@ -34,7 +37,8 @@
|
||||||
{{ image[column.columnDef] ? 'check_circle' : 'cancel' }}
|
{{ image[column.columnDef] ? 'check_circle' : 'cancel' }}
|
||||||
</mat-icon>
|
</mat-icon>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-container *ngIf="column.columnDef !== 'isDefault' && column.columnDef !== 'installed' && column.columnDef !== 'downloadUrl'">
|
<ng-container
|
||||||
|
*ngIf="column.columnDef !== 'isDefault' && column.columnDef !== 'installed' && column.columnDef !== 'downloadUrl'">
|
||||||
{{ column.cell(image) }}
|
{{ column.cell(image) }}
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</td>
|
</td>
|
||||||
|
@ -50,7 +54,7 @@
|
||||||
<mat-icon>sync</mat-icon>
|
<mat-icon>sync</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
<button *ngIf="syncUds" mat-icon-button color="primary">
|
<button *ngIf="syncUds" mat-icon-button color="primary">
|
||||||
<mat-spinner diameter="24"></mat-spinner>
|
<app-loading [isLoading]="syncUds"></app-loading>
|
||||||
</button>
|
</button>
|
||||||
<button mat-icon-button color="warn" (click)="deleteCalendar(calendar)">
|
<button mat-icon-button color="warn" (click)="deleteCalendar(calendar)">
|
||||||
<mat-icon>delete</mat-icon>
|
<mat-icon>delete</mat-icon>
|
||||||
|
@ -64,10 +68,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="paginator-container">
|
<div class="paginator-container">
|
||||||
<mat-paginator [length]="length"
|
<mat-paginator [length]="length" [pageSize]="itemsPerPage" [pageIndex]="page" [pageSizeOptions]="pageSizeOptions"
|
||||||
[pageSize]="itemsPerPage"
|
(page)="onPageChange($event)">
|
||||||
[pageIndex]="page"
|
|
||||||
[pageSizeOptions]="pageSizeOptions"
|
|
||||||
(page)="onPageChange($event)">
|
|
||||||
</mat-paginator>
|
</mat-paginator>
|
||||||
</div>
|
</div>
|
|
@ -15,14 +15,21 @@ import { CalendarComponent } from './calendar.component';
|
||||||
import { MatProgressSpinner } from '@angular/material/progress-spinner';
|
import { MatProgressSpinner } from '@angular/material/progress-spinner';
|
||||||
import { JoyrideModule, JoyrideService } from 'ngx-joyride';
|
import { JoyrideModule, JoyrideService } from 'ngx-joyride';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { LoadingComponent } from '../../shared/loading/loading.component';
|
||||||
|
import { ConfigService } from '@services/config.service';
|
||||||
|
|
||||||
describe('CalendarComponent', () => {
|
describe('CalendarComponent', () => {
|
||||||
let component: CalendarComponent;
|
let component: CalendarComponent;
|
||||||
let fixture: ComponentFixture<CalendarComponent>;
|
let fixture: ComponentFixture<CalendarComponent>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
|
const mockConfigService = {
|
||||||
|
apiUrl: 'http://mock-api-url',
|
||||||
|
mercureUrl: 'http://mock-mercure-url'
|
||||||
|
};
|
||||||
|
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
declarations: [CalendarComponent],
|
declarations: [CalendarComponent, LoadingComponent],
|
||||||
imports: [
|
imports: [
|
||||||
HttpClientTestingModule,
|
HttpClientTestingModule,
|
||||||
ToastrModule.forRoot(),
|
ToastrModule.forRoot(),
|
||||||
|
@ -40,6 +47,9 @@ describe('CalendarComponent', () => {
|
||||||
JoyrideModule.forRoot(),
|
JoyrideModule.forRoot(),
|
||||||
TranslateModule.forRoot(),
|
TranslateModule.forRoot(),
|
||||||
],
|
],
|
||||||
|
providers: [
|
||||||
|
{ provide: ConfigService, useValue: mockConfigService }
|
||||||
|
]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ import { PageEvent } from "@angular/material/paginator";
|
||||||
import { CreateCalendarComponent } from "./create-calendar/create-calendar.component";
|
import { CreateCalendarComponent } from "./create-calendar/create-calendar.component";
|
||||||
import { DeleteModalComponent } from "../../shared/delete_modal/delete-modal/delete-modal.component";
|
import { DeleteModalComponent } from "../../shared/delete_modal/delete-modal/delete-modal.component";
|
||||||
import { JoyrideService } from 'ngx-joyride';
|
import { JoyrideService } from 'ngx-joyride';
|
||||||
|
import { ConfigService } from '@services/config.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-calendar',
|
selector: 'app-calendar',
|
||||||
|
@ -16,7 +17,8 @@ import { JoyrideService } from 'ngx-joyride';
|
||||||
styleUrl: './calendar.component.css'
|
styleUrl: './calendar.component.css'
|
||||||
})
|
})
|
||||||
export class CalendarComponent implements OnInit {
|
export class CalendarComponent implements OnInit {
|
||||||
baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
|
baseUrl: string;
|
||||||
|
private apiUrl: string;
|
||||||
images: { downloadUrl: string; name: string; uuid: string }[] = [];
|
images: { downloadUrl: string; name: string; uuid: string }[] = [];
|
||||||
dataSource = new MatTableDataSource<any>();
|
dataSource = new MatTableDataSource<any>();
|
||||||
length: number = 0;
|
length: number = 0;
|
||||||
|
@ -52,21 +54,24 @@ export class CalendarComponent implements OnInit {
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
displayedColumns = [...this.columns.map(column => column.columnDef), 'actions'];
|
displayedColumns = [...this.columns.map(column => column.columnDef), 'actions'];
|
||||||
private apiUrl = `${this.baseUrl}/remote-calendars`;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public dialog: MatDialog,
|
public dialog: MatDialog,
|
||||||
private http: HttpClient,
|
private http: HttpClient,
|
||||||
private dataService: DataService,
|
private dataService: DataService,
|
||||||
private toastService: ToastrService,
|
private toastService: ToastrService,
|
||||||
|
private configService: ConfigService,
|
||||||
private joyrideService: JoyrideService
|
private joyrideService: JoyrideService
|
||||||
) {}
|
) {
|
||||||
|
this.baseUrl = this.configService.apiUrl;
|
||||||
|
this.apiUrl = `${this.baseUrl}/remote-calendars`;
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.search();
|
this.search();
|
||||||
}
|
}
|
||||||
|
|
||||||
addImage(): void {
|
addCalendar(): void {
|
||||||
const dialogRef = this.dialog.open(CreateCalendarComponent, {
|
const dialogRef = this.dialog.open(CreateCalendarComponent, {
|
||||||
width: '400px'
|
width: '400px'
|
||||||
});
|
});
|
||||||
|
@ -165,7 +170,7 @@ export class CalendarComponent implements OnInit {
|
||||||
this.joyrideService.startTour({
|
this.joyrideService.startTour({
|
||||||
steps: ['titleStep', 'addButtonStep', 'searchStep', 'tableStep', 'actionsStep'],
|
steps: ['titleStep', 'addButtonStep', 'searchStep', 'tableStep', 'actionsStep'],
|
||||||
showPrevButton: true,
|
showPrevButton: true,
|
||||||
themeColor: '#3f51b5'
|
themeColor: '#3f51b5'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,3 +28,10 @@
|
||||||
.time-field {
|
.time-field {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.action-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 1em;
|
||||||
|
padding: 1.5em;
|
||||||
|
}
|
|
@ -56,10 +56,10 @@
|
||||||
</div>
|
</div>
|
||||||
</mat-dialog-content>
|
</mat-dialog-content>
|
||||||
|
|
||||||
<mat-dialog-actions align="end">
|
<mat-dialog-actions class="action-container">
|
||||||
<button mat-button (click)="onNoClick()">{{ 'buttonCancel' | translate }}</button>
|
<button class="ordinary-button" (click)="onNoClick()">{{ 'buttonCancel' | translate }}</button>
|
||||||
<button
|
<button
|
||||||
mat-button
|
class="submit-button"
|
||||||
(click)="submitRule()"
|
(click)="submitRule()"
|
||||||
cdkFocusInitial
|
cdkFocusInitial
|
||||||
[disabled]="(!isRemoteAvailable && (!busyFromHour || !busyToHour)) || (isRemoteAvailable && (!availableReason || !availableFromDate || !availableToDate))">
|
[disabled]="(!isRemoteAvailable && (!busyFromHour || !busyToHour)) || (isRemoteAvailable && (!availableReason || !availableFromDate || !availableToDate))">
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import {Component, Inject} from '@angular/core';
|
import { Component, Inject } from '@angular/core';
|
||||||
import {ToastrService} from "ngx-toastr";
|
import { ToastrService } from "ngx-toastr";
|
||||||
import {HttpClient} from "@angular/common/http";
|
import { HttpClient } from "@angular/common/http";
|
||||||
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
|
import { MAT_DIALOG_DATA, MatDialogRef } from "@angular/material/dialog";
|
||||||
|
import { ConfigService } from '@services/config.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-create-calendar-rule',
|
selector: 'app-create-calendar-rule',
|
||||||
|
@ -9,7 +10,7 @@ import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
|
||||||
styleUrl: './create-calendar-rule.component.css'
|
styleUrl: './create-calendar-rule.component.css'
|
||||||
})
|
})
|
||||||
export class CreateCalendarRuleComponent {
|
export class CreateCalendarRuleComponent {
|
||||||
baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
|
baseUrl: string;
|
||||||
name: string = '';
|
name: string = '';
|
||||||
remoteCalendarRules: any[] = [];
|
remoteCalendarRules: any[] = [];
|
||||||
weekDays: string[] = ['Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado', 'Domingo'];
|
weekDays: string[] = ['Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado', 'Domingo'];
|
||||||
|
@ -29,20 +30,23 @@ export class CreateCalendarRuleComponent {
|
||||||
constructor(
|
constructor(
|
||||||
private toastService: ToastrService,
|
private toastService: ToastrService,
|
||||||
private http: HttpClient,
|
private http: HttpClient,
|
||||||
|
private configService: ConfigService,
|
||||||
public dialogRef: MatDialogRef<CreateCalendarRuleComponent>,
|
public dialogRef: MatDialogRef<CreateCalendarRuleComponent>,
|
||||||
@Inject(MAT_DIALOG_DATA) public data: any,
|
@Inject(MAT_DIALOG_DATA) public data: any,
|
||||||
) { }
|
) {
|
||||||
|
this.baseUrl = this.configService.apiUrl;
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.calendarId = this.data.calendar
|
this.calendarId = this.data.calendar
|
||||||
if (this.data) {
|
if (this.data) {
|
||||||
this.isEditMode = true;
|
this.isEditMode = true;
|
||||||
this.availableFromDate = this.data.rule? this.data.rule.availableFromDate : null;
|
this.availableFromDate = this.data.rule ? this.data.rule.availableFromDate : null;
|
||||||
this.availableToDate = this.data.rule? this.data.rule.availableToDate : null;
|
this.availableToDate = this.data.rule ? this.data.rule.availableToDate : null;
|
||||||
this.isRemoteAvailable = this.data.rule? this.data.rule.isRemoteAvailable : false;
|
this.isRemoteAvailable = this.data.rule ? this.data.rule.isRemoteAvailable : false;
|
||||||
this.availableReason = this.data.rule? this.data.rule.availableReason : null;
|
this.availableReason = this.data.rule ? this.data.rule.availableReason : null;
|
||||||
this.busyFromHour = this.data.rule? this.data.rule.busyFromHour : null;
|
this.busyFromHour = this.data.rule ? this.data.rule.busyFromHour : null;
|
||||||
this.busyToHour = this.data.rule? this.data.rule.busyToHour : null;
|
this.busyToHour = this.data.rule ? this.data.rule.busyToHour : null;
|
||||||
if (this.data.rule && this.data.rule.busyWeekDays) {
|
if (this.data.rule && this.data.rule.busyWeekDays) {
|
||||||
this.busyWeekDays = this.data.rule.busyWeekDays.reduce((acc: {
|
this.busyWeekDays = this.data.rule.busyWeekDays.reduce((acc: {
|
||||||
[x: string]: boolean;
|
[x: string]: boolean;
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
.full-width {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
.form-container {
|
.form-container {
|
||||||
padding: 40px;
|
padding: 40px;
|
||||||
}
|
}
|
||||||
|
@ -12,7 +9,7 @@
|
||||||
|
|
||||||
.full-width {
|
.full-width {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-bottom: 16px;
|
margin-top: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.additional-form {
|
.additional-form {
|
||||||
|
@ -58,3 +55,9 @@
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.action-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 1em;
|
||||||
|
padding: 1.5em;
|
||||||
|
}
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
<div style="display: flex; justify-content: space-between; align-items: center;">
|
<div style="display: flex; justify-content: space-between; align-items: center;">
|
||||||
<div *ngIf="isEditMode" mat-subheader>{{ 'rulesHeader' | translate }}</div>
|
<div *ngIf="isEditMode" mat-subheader>{{ 'rulesHeader' | translate }}</div>
|
||||||
<button mat-flat-button color="primary" *ngIf="isEditMode" (click)="createRule()" style="padding: 10px;">
|
<button class="action-button" *ngIf="isEditMode" (click)="createRule()" style="padding: 10px;">
|
||||||
{{ 'addRule' | translate }}
|
{{ 'addRule' | translate }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -41,9 +41,9 @@
|
||||||
</mat-list>
|
</mat-list>
|
||||||
</mat-dialog-content>
|
</mat-dialog-content>
|
||||||
|
|
||||||
<mat-dialog-actions align="end">
|
<mat-dialog-actions class="action-container">
|
||||||
<button mat-button (click)="onNoClick()">{{ 'buttonCancel' | translate }}</button>
|
<button class="ordinary-button" (click)="onNoClick()">{{ 'buttonCancel' | translate }}</button>
|
||||||
<button mat-button (click)="submitForm()" [disabled]="!name || name === ''" cdkFocusInitial>
|
<button class="submit-button" (click)="submitForm()" [disabled]="!name || name === ''" cdkFocusInitial>
|
||||||
{{ 'buttonSave' | translate }}
|
{{ 'buttonSave' | translate }}
|
||||||
</button>
|
</button>
|
||||||
</mat-dialog-actions>
|
</mat-dialog-actions>
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import {Component, Inject, OnInit} from '@angular/core';
|
import { Component, Inject, OnInit } from '@angular/core';
|
||||||
import {ToastrService} from "ngx-toastr";
|
import { ToastrService } from "ngx-toastr";
|
||||||
import {HttpClient} from "@angular/common/http";
|
import { HttpClient } from "@angular/common/http";
|
||||||
import {MAT_DIALOG_DATA, MatDialog, MatDialogRef} from "@angular/material/dialog";
|
import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from "@angular/material/dialog";
|
||||||
import {CreateCalendarRuleComponent} from "../create-calendar-rule/create-calendar-rule.component";
|
import { CreateCalendarRuleComponent } from "../create-calendar-rule/create-calendar-rule.component";
|
||||||
import {DataService} from "../data.service";
|
import { DataService } from "../data.service";
|
||||||
import {DeleteModalComponent} from "../../../shared/delete_modal/delete-modal/delete-modal.component";
|
import { DeleteModalComponent } from "../../../shared/delete_modal/delete-modal/delete-modal.component";
|
||||||
|
import { ConfigService } from '@services/config.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-create-calendar',
|
selector: 'app-create-calendar',
|
||||||
|
@ -12,7 +13,7 @@ import {DeleteModalComponent} from "../../../shared/delete_modal/delete-modal/de
|
||||||
styleUrl: './create-calendar.component.css'
|
styleUrl: './create-calendar.component.css'
|
||||||
})
|
})
|
||||||
export class CreateCalendarComponent implements OnInit {
|
export class CreateCalendarComponent implements OnInit {
|
||||||
baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
|
baseUrl: string;
|
||||||
name: string = '';
|
name: string = '';
|
||||||
remoteCalendarRules: any[] = [];
|
remoteCalendarRules: any[] = [];
|
||||||
isEditMode: boolean = false;
|
isEditMode: boolean = false;
|
||||||
|
@ -22,11 +23,14 @@ export class CreateCalendarComponent implements OnInit {
|
||||||
constructor(
|
constructor(
|
||||||
private toastService: ToastrService,
|
private toastService: ToastrService,
|
||||||
private http: HttpClient,
|
private http: HttpClient,
|
||||||
|
private configService: ConfigService,
|
||||||
public dialogRef: MatDialogRef<CreateCalendarComponent>,
|
public dialogRef: MatDialogRef<CreateCalendarComponent>,
|
||||||
@Inject(MAT_DIALOG_DATA) public data: any,
|
@Inject(MAT_DIALOG_DATA) public data: any,
|
||||||
public dialog: MatDialog,
|
public dialog: MatDialog,
|
||||||
private dataService: DataService,
|
private dataService: DataService,
|
||||||
) { }
|
) {
|
||||||
|
this.baseUrl = this.configService.apiUrl;
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
if (this.data) {
|
if (this.data) {
|
||||||
|
|
|
@ -1,16 +1,20 @@
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import {HttpClient, HttpParams} from '@angular/common/http';
|
import { HttpClient, HttpParams } from '@angular/common/http';
|
||||||
import { Observable, throwError } from 'rxjs';
|
import { Observable, throwError } from 'rxjs';
|
||||||
import { catchError, map } from 'rxjs/operators';
|
import { catchError, map } from 'rxjs/operators';
|
||||||
|
import { ConfigService } from '@services/config.service';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class DataService {
|
export class DataService {
|
||||||
baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
|
baseUrl: string;
|
||||||
private apiUrl = `${this.baseUrl}/remote-calendars?page=1&itemsPerPage=1000`;
|
private apiUrl: string;
|
||||||
|
|
||||||
constructor(private http: HttpClient) {}
|
constructor(private http: HttpClient, private configService: ConfigService) {
|
||||||
|
this.baseUrl = this.configService.apiUrl;
|
||||||
|
this.apiUrl = `${this.baseUrl}/remote-calendars?page=1&itemsPerPage=1000`;
|
||||||
|
}
|
||||||
|
|
||||||
getRemoteCalendars(filters: { [key: string]: string }): Observable<any[]> {
|
getRemoteCalendars(filters: { [key: string]: string }): Observable<any[]> {
|
||||||
const params = new HttpParams({ fromObject: filters });
|
const params = new HttpParams({ fromObject: filters });
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
.title {
|
.header-container-title {
|
||||||
font-size: 24px;
|
flex-grow: 1;
|
||||||
|
text-align: left;
|
||||||
|
margin-left: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.calendar-button-row {
|
.command-groups-button-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-start;
|
gap: 15px;
|
||||||
margin-top: 16px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.divider {
|
.divider {
|
||||||
|
@ -27,16 +28,15 @@
|
||||||
|
|
||||||
table {
|
table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-top: 50px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-container {
|
.search-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 0 5px;
|
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
margin: 1.5rem 0rem 1.5rem 0rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-string {
|
.search-string {
|
||||||
|
@ -53,11 +53,12 @@ table {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 10px;
|
padding: 10px 10px;
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mat-elevation-z8 {
|
.mat-elevation-z8 {
|
||||||
box-shadow: 0px 0px 0px rgba(0,0,0,0.2);
|
box-shadow: 0px 0px 0px rgba(0, 0, 0, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.paginator-container {
|
.paginator-container {
|
||||||
|
@ -74,5 +75,4 @@ table {
|
||||||
.mat-chip-readonly-false {
|
.mat-chip-readonly-false {
|
||||||
background-color: #F44336 !important;
|
background-color: #F44336 !important;
|
||||||
color: white !important;
|
color: white !important;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,16 +2,16 @@
|
||||||
<button mat-icon-button color="primary" (click)="iniciarTour()">
|
<button mat-icon-button color="primary" (click)="iniciarTour()">
|
||||||
<mat-icon>help</mat-icon>
|
<mat-icon>help</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
<h2 class="title" joyrideStep="titleStep" text="{{ 'titleStepText' | translate }}">{{ 'adminCommandGroupsTitle' | translate }}</h2>
|
<div class="header-container-title">
|
||||||
|
<h2 joyrideStep="titleStep" text="{{ 'titleStepText' | translate }}">{{ 'adminCommandGroupsTitle' | translate }}</h2>
|
||||||
|
</div>
|
||||||
<div class="command-groups-button-row">
|
<div class="command-groups-button-row">
|
||||||
<button mat-flat-button color="primary" (click)="openCreateCommandGroupModal()" joyrideStep="addCommandGroupStep" text="{{ 'addCommandGroupStepText' | translate }}">
|
<button class="action-button" (click)="openCreateCommandGroupModal()" joyrideStep="addCommandGroupStep" text="{{ 'addCommandGroupStepText' | translate }}">
|
||||||
{{ 'addCommandGroup' | translate }}
|
{{ 'addCommandGroup' | translate }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<mat-divider class="divider"></mat-divider>
|
|
||||||
|
|
||||||
<div class="search-container" joyrideStep="searchStep" text="{{ 'searchStepText' | translate }}">
|
<div class="search-container" joyrideStep="searchStep" text="{{ 'searchStepText' | translate }}">
|
||||||
<mat-form-field appearance="fill" class="search-string">
|
<mat-form-field appearance="fill" class="search-string">
|
||||||
<mat-label>{{ 'searchGroupNameLabel' | translate }}</mat-label>
|
<mat-label>{{ 'searchGroupNameLabel' | translate }}</mat-label>
|
||||||
|
@ -22,7 +22,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="loading" class="loading-container" joyrideStep="loadingStep" text="{{ 'loadingStepText' | translate }}">
|
<div *ngIf="loading" class="loading-container" joyrideStep="loadingStep" text="{{ 'loadingStepText' | translate }}">
|
||||||
<mat-spinner></mat-spinner>
|
<app-loading [isLoading]="loading"></app-loading>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="!loading">
|
<div *ngIf="!loading">
|
||||||
|
@ -35,7 +35,7 @@
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container *ngIf="column.columnDef === 'commands'" joyrideStep="viewCommandsStep" text="{{ 'viewCommandsStepText' | translate }}">
|
<ng-container *ngIf="column.columnDef === 'commands'" joyrideStep="viewCommandsStep" text="{{ 'viewCommandsStepText' | translate }}">
|
||||||
<button mat-button [matMenuTriggerFor]="menu">{{ 'viewCommands' | translate }}</button>
|
<button class="action-button" [matMenuTriggerFor]="menu">{{ 'viewCommands' | translate }}</button>
|
||||||
<mat-menu #menu="matMenu">
|
<mat-menu #menu="matMenu">
|
||||||
<button mat-menu-item *ngFor="let command of commandGroup.commands">
|
<button mat-menu-item *ngFor="let command of commandGroup.commands">
|
||||||
{{ command.name }}
|
{{ command.name }}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import { DeleteModalComponent } from '../../../shared/delete_modal/delete-modal/
|
||||||
import { MatTableDataSource } from "@angular/material/table";
|
import { MatTableDataSource } from "@angular/material/table";
|
||||||
import { DatePipe } from "@angular/common";
|
import { DatePipe } from "@angular/common";
|
||||||
import { JoyrideService } from 'ngx-joyride';
|
import { JoyrideService } from 'ngx-joyride';
|
||||||
|
import { ConfigService } from '@services/config.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-commands-groups',
|
selector: 'app-commands-groups',
|
||||||
|
@ -15,7 +16,8 @@ import { JoyrideService } from 'ngx-joyride';
|
||||||
styleUrls: ['./commands-groups.component.css']
|
styleUrls: ['./commands-groups.component.css']
|
||||||
})
|
})
|
||||||
export class CommandsGroupsComponent implements OnInit {
|
export class CommandsGroupsComponent implements OnInit {
|
||||||
baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
|
baseUrl: string;
|
||||||
|
private apiUrl: string;
|
||||||
dataSource = new MatTableDataSource<any>();
|
dataSource = new MatTableDataSource<any>();
|
||||||
filters: { [key: string]: string | boolean } = {};
|
filters: { [key: string]: string | boolean } = {};
|
||||||
length: number = 0;
|
length: number = 0;
|
||||||
|
@ -47,10 +49,12 @@ export class CommandsGroupsComponent implements OnInit {
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
displayedColumns = [...this.columns.map(column => column.columnDef), 'actions'];
|
displayedColumns = [...this.columns.map(column => column.columnDef), 'actions'];
|
||||||
private apiUrl = `${this.baseUrl}/command-groups`;
|
|
||||||
|
|
||||||
constructor(private http: HttpClient, private dialog: MatDialog, private toastService: ToastrService,
|
constructor(private http: HttpClient, private dialog: MatDialog, private toastService: ToastrService,
|
||||||
private joyrideService: JoyrideService) {}
|
private configService: ConfigService, private joyrideService: JoyrideService) {
|
||||||
|
this.baseUrl = this.configService.apiUrl;
|
||||||
|
this.apiUrl = `${this.baseUrl}/command-groups`;
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.search();
|
this.search();
|
||||||
|
@ -120,17 +124,17 @@ export class CommandsGroupsComponent implements OnInit {
|
||||||
iniciarTour(): void {
|
iniciarTour(): void {
|
||||||
this.joyrideService.startTour({
|
this.joyrideService.startTour({
|
||||||
steps: [
|
steps: [
|
||||||
'titleStep',
|
'titleStep',
|
||||||
'addCommandGroupStep',
|
'addCommandGroupStep',
|
||||||
'searchStep',
|
'searchStep',
|
||||||
'tableStep',
|
'tableStep',
|
||||||
'viewCommandsStep',
|
'viewCommandsStep',
|
||||||
'actionsStep',
|
'actionsStep',
|
||||||
'paginationStep'
|
'paginationStep'
|
||||||
],
|
],
|
||||||
showPrevButton: true,
|
showPrevButton: true,
|
||||||
themeColor: '#3f51b5'
|
themeColor: '#3f51b5'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,3 +98,10 @@
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.action-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 1em;
|
||||||
|
padding: 1.5em;
|
||||||
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
<h2 mat-dialog-title>{{ editing ? ('editCommandGroup' | translate) : ('createCommandGroup' | translate) }}</h2>
|
<h2 mat-dialog-title>{{ editing ? ('editCommandGroup' | translate) : ('createCommandGroup' | translate) }}</h2>
|
||||||
<mat-dialog-content class="form-container">
|
<mat-dialog-content class="form-container">
|
||||||
<div *ngIf="loading" class="loading-container">
|
<app-loading [isLoading]="loading"></app-loading>
|
||||||
<mat-spinner></mat-spinner>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<form *ngIf="!loading" class="command-group-form" (ngSubmit)="onSubmit()">
|
<form *ngIf="!loading" class="command-group-form" (ngSubmit)="onSubmit()">
|
||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
|
@ -55,7 +53,7 @@
|
||||||
</form>
|
</form>
|
||||||
</mat-dialog-content>
|
</mat-dialog-content>
|
||||||
|
|
||||||
<mat-dialog-actions align="end">
|
<mat-dialog-actions class="action-container">
|
||||||
<button mat-button (click)="close()">{{ 'buttonCancel' | translate }}</button>
|
<button class="ordinary-button" (click)="close()">{{ 'buttonCancel' | translate }}</button>
|
||||||
<button mat-button (click)="onSubmit()" cdkFocusInitial>{{ 'buttonSave' | translate }}</button>
|
<button class="submit-button" (click)="onSubmit()" cdkFocusInitial>{{ 'buttonSave' | translate }}</button>
|
||||||
</mat-dialog-actions>
|
</mat-dialog-actions>
|
|
@ -3,6 +3,7 @@ import { HttpClient } from '@angular/common/http';
|
||||||
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
|
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||||
import { ToastrService } from 'ngx-toastr';
|
import { ToastrService } from 'ngx-toastr';
|
||||||
import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';
|
import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';
|
||||||
|
import { ConfigService } from '@services/config.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-create-command-group',
|
selector: 'app-create-command-group',
|
||||||
|
@ -10,21 +11,25 @@ import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';
|
||||||
styleUrls: ['./create-command-group.component.css']
|
styleUrls: ['./create-command-group.component.css']
|
||||||
})
|
})
|
||||||
export class CreateCommandGroupComponent implements OnInit {
|
export class CreateCommandGroupComponent implements OnInit {
|
||||||
baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
|
baseUrl: string;
|
||||||
availableCommands: any[] = [];
|
availableCommands: any[] = [];
|
||||||
selectedCommands: any[] = [];
|
selectedCommands: any[] = [];
|
||||||
groupName: string = '';
|
groupName: string = '';
|
||||||
enabled: boolean = true;
|
enabled: boolean = true;
|
||||||
editing: boolean = false;
|
editing: boolean = false;
|
||||||
loading: boolean = false;
|
loading: boolean = false;
|
||||||
private apiUrl = `${this.baseUrl}/commands`;
|
private apiUrl: string;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private http: HttpClient,
|
private http: HttpClient,
|
||||||
private dialogRef: MatDialogRef<CreateCommandGroupComponent>,
|
private dialogRef: MatDialogRef<CreateCommandGroupComponent>,
|
||||||
private toastService: ToastrService,
|
private toastService: ToastrService,
|
||||||
|
private configService: ConfigService,
|
||||||
@Inject(MAT_DIALOG_DATA) public data: any
|
@Inject(MAT_DIALOG_DATA) public data: any
|
||||||
) {}
|
) {
|
||||||
|
this.baseUrl = this.configService.apiUrl;
|
||||||
|
this.apiUrl = `${this.baseUrl}/commands`;
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.loadAvailableCommands();
|
this.loadAvailableCommands();
|
||||||
|
|
|
@ -58,19 +58,6 @@
|
||||||
padding: 10px 20px;
|
padding: 10px 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
|
||||||
background-color: #3f51b5; /* Color primario */
|
|
||||||
color: white;
|
|
||||||
padding: 10px 15px;
|
|
||||||
border: none;
|
|
||||||
border-radius: 4px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
button:hover {
|
|
||||||
background-color: #2c387e; /* Color primario oscuro */
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 600px) {
|
@media (max-width: 600px) {
|
||||||
.mat-card {
|
.mat-card {
|
||||||
margin: 10px 0;
|
margin: 10px 0;
|
||||||
|
@ -86,15 +73,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.cancel-button {
|
|
||||||
background-color: #dc3545;
|
|
||||||
color: white;
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
||||||
.cancel-button:hover {
|
|
||||||
opacity: 0.9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.create-command-group-container {
|
.create-command-group-container {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
|
@ -146,8 +124,9 @@
|
||||||
color: #666;
|
color: #666;
|
||||||
}
|
}
|
||||||
|
|
||||||
.command-group-actions {
|
.action-container {
|
||||||
margin-top: 20px;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: flex-end;
|
||||||
|
gap: 1em;
|
||||||
|
padding: 1.5em;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
<div class="detail-command-group-container">
|
<div class="detail-command-group-container">
|
||||||
<h2>{{ 'commandGroupDetailsTitle' | translate }}</h2>
|
<h2>{{ 'commandGroupDetailsTitle' | translate }}</h2>
|
||||||
|
|
||||||
<!-- Indicador de carga -->
|
<app-loading [isLoading]="loading"></app-loading>
|
||||||
<div *ngIf="loading" class="loading-container">
|
|
||||||
<mat-spinner></mat-spinner>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<mat-card *ngIf="!loading">
|
<mat-card *ngIf="!loading">
|
||||||
<mat-card-header>
|
<mat-card-header>
|
||||||
|
@ -52,10 +49,10 @@
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="command-group-actions" *ngIf="!loading">
|
<div class="action-container" *ngIf="!loading">
|
||||||
<button mat-flat-button color="primary" (click)="toggleClientSelect()">
|
<button class="ordinary-button" (click)="close()">{{ 'buttonCancel' | translate }}</button>
|
||||||
|
<button [ngClass]="showClientSelect ? 'submit-button' : 'action-button'" (click)="toggleClientSelect()">
|
||||||
{{ showClientSelect ? ('execute' | translate) : ('scheduleExecution' | translate) }}
|
{{ showClientSelect ? ('execute' | translate) : ('scheduleExecution' | translate) }}
|
||||||
</button>
|
</button>
|
||||||
<button mat-flat-button color="warn" (click)="close()">{{ 'buttonCancel' | translate }}</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
|
@ -3,6 +3,7 @@ import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { ToastrService } from 'ngx-toastr';
|
import { ToastrService } from 'ngx-toastr';
|
||||||
|
import { ConfigService } from '@services/config.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-detail-command-group',
|
selector: 'app-detail-command-group',
|
||||||
|
@ -10,7 +11,7 @@ import { ToastrService } from 'ngx-toastr';
|
||||||
styleUrls: ['./detail-command-group.component.css']
|
styleUrls: ['./detail-command-group.component.css']
|
||||||
})
|
})
|
||||||
export class DetailCommandGroupComponent implements OnInit {
|
export class DetailCommandGroupComponent implements OnInit {
|
||||||
baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
|
baseUrl: string;
|
||||||
form!: FormGroup;
|
form!: FormGroup;
|
||||||
clients: any[] = [];
|
clients: any[] = [];
|
||||||
showClientSelect = false;
|
showClientSelect = false;
|
||||||
|
@ -21,9 +22,12 @@ export class DetailCommandGroupComponent implements OnInit {
|
||||||
@Inject(MAT_DIALOG_DATA) public data: any,
|
@Inject(MAT_DIALOG_DATA) public data: any,
|
||||||
private dialogRef: MatDialogRef<DetailCommandGroupComponent>,
|
private dialogRef: MatDialogRef<DetailCommandGroupComponent>,
|
||||||
private fb: FormBuilder,
|
private fb: FormBuilder,
|
||||||
|
private configService: ConfigService,
|
||||||
private http: HttpClient,
|
private http: HttpClient,
|
||||||
private toastService: ToastrService
|
private toastService: ToastrService
|
||||||
) { }
|
) {
|
||||||
|
this.baseUrl = this.configService.apiUrl;
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.form = this.fb.group({
|
this.form = this.fb.group({
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
.title {
|
.header-container-title {
|
||||||
font-size: 24px;
|
flex-grow: 1;
|
||||||
|
text-align: left;
|
||||||
|
margin-left: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.calendar-button-row {
|
.task-button-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-start;
|
gap: 15px;
|
||||||
margin-top: 16px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.divider {
|
.divider {
|
||||||
|
@ -27,7 +28,6 @@
|
||||||
|
|
||||||
table {
|
table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-top: 50px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-container {
|
.search-container {
|
||||||
|
@ -35,7 +35,7 @@ table {
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 0 5px;
|
margin: 1.5rem 0rem 1.5rem 0rem;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,7 +53,8 @@ table {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 10px;
|
padding: 10px 10px;
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mat-elevation-z8 {
|
.mat-elevation-z8 {
|
||||||
|
|
|
@ -2,16 +2,16 @@
|
||||||
<button mat-icon-button color="primary" (click)="iniciarTour()">
|
<button mat-icon-button color="primary" (click)="iniciarTour()">
|
||||||
<mat-icon>help</mat-icon>
|
<mat-icon>help</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
<h2 class="title" joyrideStep="titleStep" text="{{ 'titleStepText' | translate }}">{{ 'manageTasksTitle' | translate }}</h2>
|
<div class="header-container-title">
|
||||||
|
<h2 class="title" joyrideStep="titleStep" text="{{ 'titleStepText' | translate }}">{{ 'manageTasksTitle' | translate }}</h2>
|
||||||
|
</div>
|
||||||
<div class="task-button-row">
|
<div class="task-button-row">
|
||||||
<button mat-flat-button color="primary" (click)="openCreateTaskModal()" joyrideStep="addTaskStep" text="{{ 'addTaskStepText' | translate }}">
|
<button class="action-button" (click)="openCreateTaskModal()" joyrideStep="addTaskStep" text="{{ 'addTaskStepText' | translate }}">
|
||||||
{{ 'addTask' | translate }}
|
{{ 'addTask' | translate }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<mat-divider class="divider"></mat-divider>
|
|
||||||
|
|
||||||
<div class="search-container" joyrideStep="searchStep" text="{{ 'searchStepText' | translate }}">
|
<div class="search-container" joyrideStep="searchStep" text="{{ 'searchStepText' | translate }}">
|
||||||
<mat-form-field appearance="fill" class="search-string">
|
<mat-form-field appearance="fill" class="search-string">
|
||||||
<mat-label>{{ 'searchTaskLabel' | translate }}</mat-label>
|
<mat-label>{{ 'searchTaskLabel' | translate }}</mat-label>
|
||||||
|
|
|
@ -7,16 +7,22 @@ import { MatDividerModule } from '@angular/material/divider';
|
||||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
import { MatPaginatorModule } from '@angular/material/paginator';
|
import { MatPaginatorModule } from '@angular/material/paginator';
|
||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
import { MatInputModule } from '@angular/material/input';
|
import { MatInputModule } from '@angular/material/input';
|
||||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { LoadingComponent } from '../../../shared/loading/loading.component';
|
||||||
import { JoyrideModule, JoyrideService, JoyrideStepService } from 'ngx-joyride';
|
import { JoyrideModule, JoyrideService, JoyrideStepService } from 'ngx-joyride';
|
||||||
|
import { ConfigService } from '@services/config.service';
|
||||||
|
|
||||||
describe('CommandsTaskComponent', () => {
|
describe('CommandsTaskComponent', () => {
|
||||||
let component: CommandsTaskComponent;
|
let component: CommandsTaskComponent;
|
||||||
let fixture: ComponentFixture<CommandsTaskComponent>;
|
let fixture: ComponentFixture<CommandsTaskComponent>;
|
||||||
|
let mockConfigService: jasmine.SpyObj<ConfigService>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
|
const configServiceSpy = jasmine.createSpyObj('ConfigService', [], { apiUrl: 'http://mock-api-url' });
|
||||||
|
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
imports: [
|
imports: [
|
||||||
HttpClientTestingModule,
|
HttpClientTestingModule,
|
||||||
|
@ -31,9 +37,14 @@ describe('CommandsTaskComponent', () => {
|
||||||
TranslateModule.forRoot(),
|
TranslateModule.forRoot(),
|
||||||
JoyrideModule.forRoot(),
|
JoyrideModule.forRoot(),
|
||||||
],
|
],
|
||||||
declarations: [CommandsTaskComponent],
|
declarations: [CommandsTaskComponent, LoadingComponent],
|
||||||
|
providers: [
|
||||||
|
{ provide: ConfigService, useValue: configServiceSpy }
|
||||||
|
],
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
|
|
||||||
|
mockConfigService = TestBed.inject(ConfigService) as jasmine.SpyObj<ConfigService>;
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { CreateTaskComponent } from './create-task/create-task.component';
|
||||||
import { DetailTaskComponent } from './detail-task/detail-task.component';
|
import { DetailTaskComponent } from './detail-task/detail-task.component';
|
||||||
import { DeleteModalComponent } from '../../../shared/delete_modal/delete-modal/delete-modal.component';
|
import { DeleteModalComponent } from '../../../shared/delete_modal/delete-modal/delete-modal.component';
|
||||||
import { JoyrideService } from 'ngx-joyride';
|
import { JoyrideService } from 'ngx-joyride';
|
||||||
|
import { ConfigService } from '@services/config.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-commands-task',
|
selector: 'app-commands-task',
|
||||||
|
@ -13,7 +14,7 @@ import { JoyrideService } from 'ngx-joyride';
|
||||||
styleUrls: ['./commands-task.component.css']
|
styleUrls: ['./commands-task.component.css']
|
||||||
})
|
})
|
||||||
export class CommandsTaskComponent implements OnInit {
|
export class CommandsTaskComponent implements OnInit {
|
||||||
baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
|
baseUrl: string;
|
||||||
tasks: any[] = [];
|
tasks: any[] = [];
|
||||||
filters: { [key: string]: string | boolean } = {};
|
filters: { [key: string]: string | boolean } = {};
|
||||||
length: number = 0;
|
length: number = 0;
|
||||||
|
@ -22,10 +23,14 @@ export class CommandsTaskComponent implements OnInit {
|
||||||
pageSizeOptions: number[] = [5, 10, 20, 40, 100];
|
pageSizeOptions: number[] = [5, 10, 20, 40, 100];
|
||||||
displayedColumns: string[] = ['taskid', 'notes', 'name', 'scheduledDate', 'enabled', 'actions'];
|
displayedColumns: string[] = ['taskid', 'notes', 'name', 'scheduledDate', 'enabled', 'actions'];
|
||||||
loading: boolean = false;
|
loading: boolean = false;
|
||||||
private apiUrl = `${this.baseUrl}/command-tasks`;
|
private apiUrl: string;
|
||||||
|
|
||||||
constructor(private http: HttpClient, private dialog: MatDialog, private toastService: ToastrService,
|
constructor(private http: HttpClient, private dialog: MatDialog, private toastService: ToastrService,
|
||||||
private joyrideService: JoyrideService) {}
|
private configService: ConfigService,
|
||||||
|
private joyrideService: JoyrideService) {
|
||||||
|
this.baseUrl = this.configService.apiUrl;
|
||||||
|
this.apiUrl = `${this.baseUrl}/command-tasks`;
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.loadTasks();
|
this.loadTasks();
|
||||||
|
@ -110,5 +115,5 @@ export class CommandsTaskComponent implements OnInit {
|
||||||
themeColor: '#3f51b5'
|
themeColor: '#3f51b5'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
|
padding: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
mat-form-field {
|
mat-form-field {
|
||||||
|
|
|
@ -87,10 +87,6 @@
|
||||||
</mat-select>
|
</mat-select>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
<div class="button-container">
|
|
||||||
<button mat-raised-button color="primary" (click)="saveTask()">{{ 'buttonSave' | translate }}</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<mat-form-field appearance="fill" class="full-width">
|
<mat-form-field appearance="fill" class="full-width">
|
||||||
<mat-label>Selecciona Clientes</mat-label>
|
<mat-label>Selecciona Clientes</mat-label>
|
||||||
<mat-select formControlName="selectedClients" multiple>
|
<mat-select formControlName="selectedClients" multiple>
|
||||||
|
@ -102,10 +98,9 @@
|
||||||
</mat-option>
|
</mat-option>
|
||||||
</mat-select>
|
</mat-select>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
<div class="button-container">
|
|
||||||
<button mat-raised-button color="primary" (click)="saveTask()">Guardar</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</mat-dialog-content>
|
</mat-dialog-content>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<div class="button-container">
|
||||||
|
<button class="submit-button" (click)="saveTask()">{{ 'buttonSave' | translate }}</button>
|
||||||
|
</div>
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { HttpClient } from '@angular/common/http';
|
||||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
import { ToastrService } from 'ngx-toastr';
|
import { ToastrService } from 'ngx-toastr';
|
||||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||||
|
import { ConfigService } from '@services/config.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-create-task',
|
selector: 'app-create-task',
|
||||||
|
@ -10,12 +11,12 @@ import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||||
styleUrls: ['./create-task.component.css']
|
styleUrls: ['./create-task.component.css']
|
||||||
})
|
})
|
||||||
export class CreateTaskComponent implements OnInit {
|
export class CreateTaskComponent implements OnInit {
|
||||||
baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
|
baseUrl: string;
|
||||||
taskForm: FormGroup;
|
taskForm: FormGroup;
|
||||||
availableCommandGroups: any[] = [];
|
availableCommandGroups: any[] = [];
|
||||||
selectedGroupCommands: any[] = [];
|
selectedGroupCommands: any[] = [];
|
||||||
availableIndividualCommands: any[] = [];
|
availableIndividualCommands: any[] = [];
|
||||||
apiUrl = `${this.baseUrl}/command-tasks`;
|
apiUrl: string;
|
||||||
editing: boolean = false;
|
editing: boolean = false;
|
||||||
availableOrganizationalUnits: any[] = [];
|
availableOrganizationalUnits: any[] = [];
|
||||||
selectedUnitChildren: any[] = [];
|
selectedUnitChildren: any[] = [];
|
||||||
|
@ -25,10 +26,13 @@ export class CreateTaskComponent implements OnInit {
|
||||||
constructor(
|
constructor(
|
||||||
private fb: FormBuilder,
|
private fb: FormBuilder,
|
||||||
private http: HttpClient,
|
private http: HttpClient,
|
||||||
|
private configService: ConfigService,
|
||||||
private toastr: ToastrService,
|
private toastr: ToastrService,
|
||||||
public dialogRef: MatDialogRef<CreateTaskComponent>,
|
public dialogRef: MatDialogRef<CreateTaskComponent>,
|
||||||
@Inject(MAT_DIALOG_DATA) public data: any
|
@Inject(MAT_DIALOG_DATA) public data: any
|
||||||
) {
|
) {
|
||||||
|
this.baseUrl = this.configService.apiUrl;
|
||||||
|
this.apiUrl = `${this.baseUrl}/command-tasks`;
|
||||||
this.taskForm = this.fb.group({
|
this.taskForm = this.fb.group({
|
||||||
commandGroup: ['', Validators.required],
|
commandGroup: ['', Validators.required],
|
||||||
extraCommands: [[]],
|
extraCommands: [[]],
|
||||||
|
|
|
@ -57,26 +57,4 @@
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
padding: 10px 20px;
|
padding: 10px 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
|
||||||
background-color: #3f51b5;
|
|
||||||
color: white;
|
|
||||||
padding: 10px 15px;
|
|
||||||
border: none;
|
|
||||||
border-radius: 4px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
button:hover {
|
|
||||||
background-color: #2c387e;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cancel-button {
|
|
||||||
background-color: #dc3545;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cancel-button:hover {
|
|
||||||
opacity: 0.9;
|
|
||||||
}
|
|
||||||
|
|
|
@ -50,6 +50,6 @@
|
||||||
</mat-card>
|
</mat-card>
|
||||||
|
|
||||||
<div class="task-actions">
|
<div class="task-actions">
|
||||||
<button mat-flat-button class="cancel-button" (click)="closeDialog()">{{ 'buttonClose' | translate }}</button>
|
<button class="ordinary-button" (click)="closeDialog()">Cancel</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
<h1 mat-dialog-title>{{ 'inputDetails' | translate }}</h1>
|
||||||
|
<div mat-dialog-content>
|
||||||
|
<pre>{{ data.input | json }}</pre>
|
||||||
|
</div>
|
||||||
|
<div mat-dialog-actions align="end">
|
||||||
|
<button class="ordinary-button" (click)="close()">{{ 'closeButton' | translate }}</button>
|
||||||
|
</div>
|
|
@ -0,0 +1,47 @@
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { InputDialogComponent } from './input-dialog.component';
|
||||||
|
import {MAT_DIALOG_DATA, MatDialogModule, MatDialogRef} from "@angular/material/dialog";
|
||||||
|
import {FormBuilder} from "@angular/forms";
|
||||||
|
import {ToastrService} from "ngx-toastr";
|
||||||
|
import {provideHttpClient} from "@angular/common/http";
|
||||||
|
import {provideHttpClientTesting} from "@angular/common/http/testing";
|
||||||
|
import {TranslateModule} from "@ngx-translate/core";
|
||||||
|
|
||||||
|
describe('InputDialogComponent', () => {
|
||||||
|
let component: InputDialogComponent;
|
||||||
|
let fixture: ComponentFixture<InputDialogComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [InputDialogComponent],
|
||||||
|
imports: [
|
||||||
|
MatDialogModule,
|
||||||
|
TranslateModule.forRoot(),
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
FormBuilder,
|
||||||
|
ToastrService,
|
||||||
|
provideHttpClient(),
|
||||||
|
provideHttpClientTesting(),
|
||||||
|
{
|
||||||
|
provide: MatDialogRef,
|
||||||
|
useValue: {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: MAT_DIALOG_DATA,
|
||||||
|
useValue: {}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(InputDialogComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { Component, Inject } from '@angular/core';
|
||||||
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-input-dialog',
|
||||||
|
templateUrl: './input-dialog.component.html',
|
||||||
|
styleUrl: './input-dialog.component.css'
|
||||||
|
})
|
||||||
|
export class InputDialogComponent {
|
||||||
|
constructor(
|
||||||
|
public dialogRef: MatDialogRef<InputDialogComponent>,
|
||||||
|
@Inject(MAT_DIALOG_DATA) public data: { input: any }
|
||||||
|
) {}
|
||||||
|
|
||||||
|
close(): void {
|
||||||
|
this.dialogRef.close();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,15 +1,20 @@
|
||||||
.title {
|
.header-container {
|
||||||
font-size: 24px;
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 10px 10px;
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-container-title {
|
||||||
|
flex-grow: 1;
|
||||||
|
text-align: left;
|
||||||
|
margin-left: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.calendar-button-row {
|
.calendar-button-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-start;
|
gap: 15px;
|
||||||
margin-top: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.divider {
|
|
||||||
margin: 20px 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.lists-container {
|
.lists-container {
|
||||||
|
@ -27,14 +32,13 @@
|
||||||
|
|
||||||
table {
|
table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-top: 50px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-container {
|
.search-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 0 5px;
|
margin: 1.5rem 0rem 1.5rem 0rem;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,15 +57,14 @@ table {
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-container {
|
.mat-elevation-z8 {
|
||||||
display: flex;
|
box-shadow: 0px 0px 0px rgba(0, 0, 0, 0.2);
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
padding: 10px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.mat-elevation-z8 {
|
.progress-container {
|
||||||
box-shadow: 0px 0px 0px rgba(0,0,0,0.2);
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.paginator-container {
|
.paginator-container {
|
||||||
|
@ -71,12 +74,12 @@ table {
|
||||||
}
|
}
|
||||||
|
|
||||||
.chip-failed {
|
.chip-failed {
|
||||||
background-color: #f15d5d !important;
|
background-color: #e87979 !important;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chip-success {
|
.chip-success {
|
||||||
background-color: #32c532 !important;
|
background-color: #46c446 !important;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,3 +93,26 @@ table {
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.status-progress-flex {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.cancel-button {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cancel-button {
|
||||||
|
color: red;
|
||||||
|
background-color: transparent;
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cancel-button mat-icon {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
|
@ -2,29 +2,39 @@
|
||||||
<button mat-icon-button color="primary" (click)="iniciarTour()">
|
<button mat-icon-button color="primary" (click)="iniciarTour()">
|
||||||
<mat-icon>help</mat-icon>
|
<mat-icon>help</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
<h2 class="title" joyrideStep="titleStep" text="{{ 'titleStepText' | translate }}">{{ 'adminCommandsTitle' | translate }}</h2>
|
|
||||||
|
<div class="header-container-title">
|
||||||
|
<h2 joyrideStep="titleStep" text="{{ 'titleStepText' | translate }}">{{ 'adminCommandsTitle' |
|
||||||
|
translate }}</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="images-button-row">
|
<div class="images-button-row">
|
||||||
<button mat-flat-button color="primary" (click)="resetFilters()" joyrideStep="resetFiltersStep" text="{{ 'resetFiltersStepText' | translate }}">
|
<button class="action-button" (click)="resetFilters()" joyrideStep="resetFiltersStep"
|
||||||
|
text="{{ 'resetFiltersStepText' | translate }}">
|
||||||
{{ 'resetFilters' | translate }}
|
{{ 'resetFilters' | translate }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<mat-divider class="divider"></mat-divider>
|
|
||||||
|
|
||||||
<div class="search-container">
|
<div class="search-container">
|
||||||
<mat-form-field appearance="fill" class="search-select" joyrideStep="clientSelectStep" text="{{ 'clientSelectStepText' | translate }}">
|
<mat-form-field appearance="fill" class="search-select" joyrideStep="clientSelectStep"
|
||||||
<input type="text" matInput [formControl]="clientControl" [matAutocomplete]="clientAuto" placeholder="{{ 'selectClientPlaceholder' | translate }}">
|
text="{{ 'clientSelectStepText' | translate }}">
|
||||||
<mat-autocomplete #clientAuto="matAutocomplete" [displayWith]="displayFnClient" (optionSelected)="onOptionClientSelected($event.option.value)">
|
<input type="text" matInput [formControl]="clientControl" [matAutocomplete]="clientAuto"
|
||||||
|
placeholder="{{ 'filterClientPlaceholder' | translate }}">
|
||||||
|
<mat-autocomplete #clientAuto="matAutocomplete" [displayWith]="displayFnClient"
|
||||||
|
(optionSelected)="onOptionClientSelected($event.option.value)">
|
||||||
<mat-option *ngFor="let client of filteredClients | async" [value]="client">
|
<mat-option *ngFor="let client of filteredClients | async" [value]="client">
|
||||||
{{ client.name }}
|
{{ client.name }}
|
||||||
</mat-option>
|
</mat-option>
|
||||||
</mat-autocomplete>
|
</mat-autocomplete>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
<mat-form-field appearance="fill" class="search-select" joyrideStep="commandSelectStep" text="{{ 'commandSelectStepText' | translate }}">
|
<mat-form-field appearance="fill" class="search-select" joyrideStep="commandSelectStep"
|
||||||
<input type="text" matInput [formControl]="commandControl" [matAutocomplete]="commandAuto" placeholder="{{ 'selectCommandPlaceholder' | translate }}">
|
text="{{ 'commandSelectStepText' | translate }}">
|
||||||
<mat-autocomplete #commandAuto="matAutocomplete" [displayWith]="displayFnCommand" (optionSelected)="onOptionCommandSelected($event.option.value)">
|
<input type="text" matInput [formControl]="commandControl" [matAutocomplete]="commandAuto"
|
||||||
|
placeholder="{{ 'filterCommandPlaceholder' | translate }}">
|
||||||
|
<mat-autocomplete #commandAuto="matAutocomplete" [displayWith]="displayFnCommand"
|
||||||
|
(optionSelected)="onOptionCommandSelected($event.option.value)">
|
||||||
<mat-option *ngFor="let command of filteredCommands | async" [value]="command">
|
<mat-option *ngFor="let command of filteredCommands | async" [value]="command">
|
||||||
{{ command.name }}
|
{{ command.name }}
|
||||||
</mat-option>
|
</mat-option>
|
||||||
|
@ -33,59 +43,86 @@
|
||||||
|
|
||||||
<mat-form-field appearance="fill" class="search-boolean">
|
<mat-form-field appearance="fill" class="search-boolean">
|
||||||
<mat-label i18n="@@searchLabel">Estado</mat-label>
|
<mat-label i18n="@@searchLabel">Estado</mat-label>
|
||||||
<mat-select [(ngModel)]="filters['status']" (selectionChange)="loadTraces()" placeholder="Seleccionar opción" >
|
<mat-select [(ngModel)]="filters['status']" (selectionChange)="loadTraces()" placeholder="Seleccionar opción">
|
||||||
|
<mat-option [value]="undefined">Todos</mat-option>
|
||||||
<mat-option [value]="'failed'">Fallido</mat-option>
|
<mat-option [value]="'failed'">Fallido</mat-option>
|
||||||
<mat-option [value]="'pending'">Pendiente de ejecutar</mat-option>
|
<mat-option [value]="'pending'">Pendiente de ejecutar</mat-option>
|
||||||
<mat-option [value]="'in-progress'">Ejecutando</mat-option>
|
<mat-option [value]="'in-progress'">Ejecutando</mat-option>
|
||||||
<mat-option [value]="'success'">Completado con éxito</mat-option>
|
<mat-option [value]="'success'">Completado con éxito</mat-option>
|
||||||
|
<mat-option [value]="'cancelled'">Cancelado</mat-option>
|
||||||
</mat-select>
|
</mat-select>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="loading" class="loading-container">
|
<app-loading [isLoading]="loading"></app-loading>
|
||||||
<mat-spinner></mat-spinner>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div *ngIf="!loading">
|
<div *ngIf="!loading">
|
||||||
<table mat-table [dataSource]="traces" class="mat-elevation-z8" joyrideStep="tableStep" text="{{ 'tableStepText' | translate }}">
|
<table mat-table [dataSource]="traces" class="mat-elevation-z8" joyrideStep="tableStep"
|
||||||
|
text="{{ 'tableStepText' | translate }}">
|
||||||
<ng-container *ngFor="let column of columns" [matColumnDef]="column.columnDef">
|
<ng-container *ngFor="let column of columns" [matColumnDef]="column.columnDef">
|
||||||
<th mat-header-cell *matHeaderCellDef> {{ column.header }} </th>
|
<th mat-header-cell *matHeaderCellDef> {{ column.header }} </th>
|
||||||
<td mat-cell *matCellDef="let trace">
|
<td mat-cell *matCellDef="let trace">
|
||||||
|
|
||||||
<ng-container *ngIf="column.columnDef === 'status'; else defaultCell">
|
<ng-container [ngSwitch]="column.columnDef">
|
||||||
<mat-chip [ngClass]="{
|
<ng-container *ngSwitchCase="'status'">
|
||||||
'chip-failed': trace.status === 'failed',
|
<ng-container *ngIf="trace.status === 'in-progress' && trace.progress; else statusChip">
|
||||||
'chip-success': trace.status === 'success',
|
<div class="progress-container">
|
||||||
'chip-pending': trace.status === 'pending',
|
<mat-progress-bar class="example-margin" [mode]="mode" [value]="trace.progress" [bufferValue]="bufferValue">
|
||||||
'chip-in-progress': trace.status === 'in-progress'
|
</mat-progress-bar>
|
||||||
}">
|
<span>{{trace.progress}}%</span>
|
||||||
{{
|
</div>
|
||||||
trace.status === 'failed' ? 'Fallido' :
|
</ng-container>
|
||||||
trace.status === 'success' ? 'Finalizado con éxito' :
|
<ng-template #statusChip>
|
||||||
trace.status === 'pending' ? 'Pendiente de ejecutar' :
|
<div class="status-progress-flex">
|
||||||
trace.status === 'in-progress' ? 'Ejecutando' :
|
<mat-chip [ngClass]="{
|
||||||
trace.status
|
'chip-failed': trace.status === 'failed',
|
||||||
}}
|
'chip-success': trace.status === 'success',
|
||||||
</mat-chip>
|
'chip-pending': trace.status === 'pending',
|
||||||
</ng-container>
|
'chip-in-progress': trace.status === 'in-progress',
|
||||||
|
'chip-cancelled': trace.status === 'cancelled'
|
||||||
|
}">
|
||||||
|
{{
|
||||||
|
trace.status === 'failed' ? 'Fallido' :
|
||||||
|
trace.status === 'in-progress' ? 'En ejecución' :
|
||||||
|
trace.status === 'success' ? 'Finalizado con éxito' :
|
||||||
|
trace.status === 'pending' ? 'Pendiente de ejecutar' :
|
||||||
|
trace.status === 'cancelled' ? 'Cancelado' :
|
||||||
|
trace.status
|
||||||
|
}}
|
||||||
|
</mat-chip>
|
||||||
|
<button *ngIf="trace.status === 'in-progress' && trace.command === 'deploy-image'"
|
||||||
|
mat-icon-button
|
||||||
|
(click)="cancelTrace(trace)"
|
||||||
|
class="cancel-button"
|
||||||
|
matTooltip="Cancelar transmisión de imagen">
|
||||||
|
<mat-icon>cancel</mat-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
<ng-template #defaultCell>
|
<ng-container *ngSwitchCase="'input'">
|
||||||
{{ column.cell(trace) }}
|
<button mat-icon-button (click)="openInputModal(trace.input)">
|
||||||
</ng-template>
|
<mat-icon>info</mat-icon>
|
||||||
|
</button>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container *ngSwitchDefault>
|
||||||
|
{{ column.cell(trace) }}
|
||||||
|
</ng-container>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
|
||||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="paginator-container" joyrideStep="paginationStep" text="{{ 'paginationStepText' | translate }}">
|
<div class="paginator-container" joyrideStep="paginationStep" text="{{ 'paginationStepText' | translate }}">
|
||||||
<mat-paginator [length]="length"
|
<mat-paginator [length]="length" [pageSize]="itemsPerPage" [pageIndex]="page" [pageSizeOptions]="pageSizeOptions"
|
||||||
[pageSize]="itemsPerPage"
|
(page)="onPageChange($event)">
|
||||||
[pageIndex]="page"
|
|
||||||
[pageSizeOptions]="pageSizeOptions"
|
|
||||||
(page)="onPageChange($event)">
|
|
||||||
</mat-paginator>
|
</mat-paginator>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,10 +1,16 @@
|
||||||
import { Component, OnInit } from '@angular/core';
|
import { ChangeDetectorRef, Component, OnInit } from '@angular/core';
|
||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable, forkJoin } from 'rxjs';
|
||||||
import { FormControl } from '@angular/forms';
|
import { FormControl } from '@angular/forms';
|
||||||
import { map, startWith } from 'rxjs/operators';
|
import { map, startWith } from 'rxjs/operators';
|
||||||
import { DatePipe } from '@angular/common';
|
import { DatePipe } from '@angular/common';
|
||||||
import { JoyrideService } from 'ngx-joyride';
|
import { JoyrideService } from 'ngx-joyride';
|
||||||
|
import { MatDialog } from "@angular/material/dialog";
|
||||||
|
import { InputDialogComponent } from "./input-dialog/input-dialog.component";
|
||||||
|
import { ProgressBarMode } from '@angular/material/progress-bar';
|
||||||
|
import { DeleteModalComponent } from "../../../../shared/delete_modal/delete-modal/delete-modal.component";
|
||||||
|
import { ToastrService } from "ngx-toastr";
|
||||||
|
import { ConfigService } from '@services/config.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-task-logs',
|
selector: 'app-task-logs',
|
||||||
|
@ -12,7 +18,8 @@ import { JoyrideService } from 'ngx-joyride';
|
||||||
styleUrls: ['./task-logs.component.css']
|
styleUrls: ['./task-logs.component.css']
|
||||||
})
|
})
|
||||||
export class TaskLogsComponent implements OnInit {
|
export class TaskLogsComponent implements OnInit {
|
||||||
baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
|
baseUrl: string;
|
||||||
|
mercureUrl: string;
|
||||||
traces: any[] = [];
|
traces: any[] = [];
|
||||||
groupedTraces: any[] = [];
|
groupedTraces: any[] = [];
|
||||||
commands: any[] = [];
|
commands: any[] = [];
|
||||||
|
@ -23,6 +30,9 @@ export class TaskLogsComponent implements OnInit {
|
||||||
loading: boolean = true;
|
loading: boolean = true;
|
||||||
pageSizeOptions: number[] = [10, 20, 30, 50];
|
pageSizeOptions: number[] = [10, 20, 30, 50];
|
||||||
datePipe: DatePipe = new DatePipe('es-ES');
|
datePipe: DatePipe = new DatePipe('es-ES');
|
||||||
|
mode: ProgressBarMode = 'buffer';
|
||||||
|
progress = 0;
|
||||||
|
bufferValue = 0;
|
||||||
|
|
||||||
columns = [
|
columns = [
|
||||||
{
|
{
|
||||||
|
@ -50,6 +60,16 @@ export class TaskLogsComponent implements OnInit {
|
||||||
header: 'Hilo de trabajo',
|
header: 'Hilo de trabajo',
|
||||||
cell: (trace: any) => `${trace.jobId}`
|
cell: (trace: any) => `${trace.jobId}`
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
columnDef: 'input',
|
||||||
|
header: 'Input',
|
||||||
|
cell: (trace: any) => `${trace.input}`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
columnDef: 'output',
|
||||||
|
header: 'Logs',
|
||||||
|
cell: (trace: any) => `${trace.output}`
|
||||||
|
},
|
||||||
{
|
{
|
||||||
columnDef: 'executedAt',
|
columnDef: 'executedAt',
|
||||||
header: 'Programación de ejecución',
|
header: 'Programación de ejecución',
|
||||||
|
@ -70,12 +90,20 @@ export class TaskLogsComponent implements OnInit {
|
||||||
commandControl = new FormControl();
|
commandControl = new FormControl();
|
||||||
|
|
||||||
constructor(private http: HttpClient,
|
constructor(private http: HttpClient,
|
||||||
private joyrideService: JoyrideService) {}
|
private joyrideService: JoyrideService,
|
||||||
|
private dialog: MatDialog,
|
||||||
|
private cdr: ChangeDetectorRef,
|
||||||
|
private configService: ConfigService,
|
||||||
|
private toastService: ToastrService
|
||||||
|
) {
|
||||||
|
this.baseUrl = this.configService.apiUrl;
|
||||||
|
this.mercureUrl = this.configService.mercureUrl;
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.loadTraces();
|
this.loadTraces();
|
||||||
this.loadCommands();
|
this.loadCommands();
|
||||||
this.loadClients();
|
//this.loadClients();
|
||||||
this.filteredCommands = this.commandControl.valueChanges.pipe(
|
this.filteredCommands = this.commandControl.valueChanges.pipe(
|
||||||
startWith(''),
|
startWith(''),
|
||||||
map(value => (typeof value === 'string' ? value : value?.name)),
|
map(value => (typeof value === 'string' ? value : value?.name)),
|
||||||
|
@ -86,8 +114,39 @@ export class TaskLogsComponent implements OnInit {
|
||||||
map(value => (typeof value === 'string' ? value : value?.name)),
|
map(value => (typeof value === 'string' ? value : value?.name)),
|
||||||
map(name => (name ? this._filterClients(name) : this.clients.slice()))
|
map(name => (name ? this._filterClients(name) : this.clients.slice()))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const eventSource = new EventSource(`${this.mercureUrl}?topic=`
|
||||||
|
+ encodeURIComponent(`traces`));
|
||||||
|
|
||||||
|
eventSource.onmessage = (event) => {
|
||||||
|
const data = JSON.parse(event.data);
|
||||||
|
if (data && data['@id']) {
|
||||||
|
this.updateTracesStatus(data['@id'], data.status, data.progress);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private updateTracesStatus(clientUuid: string, newStatus: string, progress: Number): void {
|
||||||
|
const traceIndex = this.traces.findIndex(trace => trace['@id'] === clientUuid);
|
||||||
|
if (traceIndex !== -1) {
|
||||||
|
const updatedTraces = [...this.traces];
|
||||||
|
|
||||||
|
updatedTraces[traceIndex] = {
|
||||||
|
...updatedTraces[traceIndex],
|
||||||
|
status: newStatus,
|
||||||
|
progress: progress
|
||||||
|
};
|
||||||
|
|
||||||
|
this.traces = updatedTraces;
|
||||||
|
this.cdr.detectChanges();
|
||||||
|
|
||||||
|
console.log(`Estado actualizado para la traza ${clientUuid}: ${newStatus}`);
|
||||||
|
} else {
|
||||||
|
console.warn(`Traza con UUID ${clientUuid} no encontrado en la lista.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private _filterClients(name: string): any[] {
|
private _filterClients(name: string): any[] {
|
||||||
const filterValue = name.toLowerCase();
|
const filterValue = name.toLowerCase();
|
||||||
return this.clients.filter(client => client.name.toLowerCase().includes(filterValue));
|
return this.clients.filter(client => client.name.toLowerCase().includes(filterValue));
|
||||||
|
@ -116,10 +175,41 @@ export class TaskLogsComponent implements OnInit {
|
||||||
this.loadTraces();
|
this.loadTraces();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
openInputModal(inputData: any): void {
|
||||||
|
this.dialog.open(InputDialogComponent, {
|
||||||
|
width: '700px',
|
||||||
|
data: { input: inputData }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
cancelTrace(trace: any): void {
|
||||||
|
this.dialog.open(DeleteModalComponent, {
|
||||||
|
width: '300px',
|
||||||
|
data: { name: trace.jobId },
|
||||||
|
}).afterClosed().subscribe((result) => {
|
||||||
|
if (result) {
|
||||||
|
this.http.post(`${this.baseUrl}/traces/server/${trace.uuid}/cancel`, {}).subscribe({
|
||||||
|
next: () => {
|
||||||
|
this.toastService.success('Transmision de imagen cancelada');
|
||||||
|
this.loadTraces();
|
||||||
|
},
|
||||||
|
error: (error) => {
|
||||||
|
this.toastService.error(error.error['hydra:description']);
|
||||||
|
console.error(error.error['hydra:description']);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
loadTraces(): void {
|
loadTraces(): void {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
const url = `${this.baseUrl}/traces?page=${this.page + 1}&itemsPerPage=${this.itemsPerPage}`;
|
const url = `${this.baseUrl}/traces?page=${this.page + 1}&itemsPerPage=${this.itemsPerPage}`;
|
||||||
this.http.get<any>(url, { params: this.filters }).subscribe(
|
const params = { ...this.filters };
|
||||||
|
if (params['status'] === undefined) {
|
||||||
|
delete params['status'];
|
||||||
|
}
|
||||||
|
this.http.get<any>(url, { params }).subscribe(
|
||||||
(data) => {
|
(data) => {
|
||||||
this.traces = data['hydra:member'];
|
this.traces = data['hydra:member'];
|
||||||
this.length = data['hydra:totalItems'];
|
this.length = data['hydra:totalItems'];
|
||||||
|
@ -151,10 +241,20 @@ export class TaskLogsComponent implements OnInit {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
this.http.get<any>(`${this.baseUrl}/clients?&page=1&itemsPerPage=10000`).subscribe(
|
this.http.get<any>(`${this.baseUrl}/clients?&page=1&itemsPerPage=10000`).subscribe(
|
||||||
response => {
|
response => {
|
||||||
this.clients = response['hydra:member'];
|
const clientIds = response['hydra:member'].map((client: any) => client['@id']);
|
||||||
this.loading = false;
|
const clientDetailsRequests: Observable<any>[] = clientIds.map((id: string) => this.http.get<any>(`${this.baseUrl}${id}`));
|
||||||
|
forkJoin(clientDetailsRequests).subscribe(
|
||||||
|
(clients: any[]) => {
|
||||||
|
this.clients = clients;
|
||||||
|
this.loading = false;
|
||||||
|
},
|
||||||
|
(error: any) => {
|
||||||
|
console.error('Error fetching client details:', error);
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
);
|
||||||
},
|
},
|
||||||
error => {
|
(error: any) => {
|
||||||
console.error('Error fetching clients:', error);
|
console.error('Error fetching clients:', error);
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
}
|
}
|
||||||
|
@ -194,12 +294,12 @@ export class TaskLogsComponent implements OnInit {
|
||||||
iniciarTour(): void {
|
iniciarTour(): void {
|
||||||
this.joyrideService.startTour({
|
this.joyrideService.startTour({
|
||||||
steps: [
|
steps: [
|
||||||
'titleStep',
|
'titleStep',
|
||||||
'resetFiltersStep',
|
'resetFiltersStep',
|
||||||
'clientSelectStep',
|
'clientSelectStep',
|
||||||
'commandSelectStep',
|
'commandSelectStep',
|
||||||
'tableStep',
|
'tableStep',
|
||||||
'paginationStep'
|
'paginationStep'
|
||||||
],
|
],
|
||||||
showPrevButton: true,
|
showPrevButton: true,
|
||||||
themeColor: '#3f51b5'
|
themeColor: '#3f51b5'
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
.title {
|
.header-container-title {
|
||||||
font-size: 24px;
|
flex-grow: 1;
|
||||||
|
text-align: left;
|
||||||
|
margin-left: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.calendar-button-row {
|
.command-button-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-start;
|
gap: 15px;
|
||||||
margin-top: 16px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.divider {
|
.divider {
|
||||||
|
@ -27,7 +28,6 @@
|
||||||
|
|
||||||
table {
|
table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-top: 50px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-container {
|
.search-container {
|
||||||
|
@ -35,8 +35,8 @@ table {
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 0 5px;
|
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
margin: 1.5rem 0rem 1.5rem 0rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-string {
|
.search-string {
|
||||||
|
@ -53,7 +53,8 @@ table {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 10px;
|
padding: 10px 10px;
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mat-elevation-z8 {
|
.mat-elevation-z8 {
|
||||||
|
|
|
@ -2,14 +2,14 @@
|
||||||
<button mat-icon-button color="primary" (click)="iniciarTour()">
|
<button mat-icon-button color="primary" (click)="iniciarTour()">
|
||||||
<mat-icon>help</mat-icon>
|
<mat-icon>help</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
<h2 class="title" joyrideStep="titleStep" text="{{ 'titleStepText' | translate }}">{{ 'CommandsTitle' | translate }}</h2>
|
<div class="header-container-title">
|
||||||
|
<h2 joyrideStep="titleStep" text="{{ 'titleStepText' | translate }}">{{ 'CommandsTitle' | translate }}</h2>
|
||||||
|
</div>
|
||||||
<div class="command-button-row" joyrideStep="addCommandStep" text="{{ 'addCommandStepText' | translate }}">
|
<div class="command-button-row" joyrideStep="addCommandStep" text="{{ 'addCommandStepText' | translate }}">
|
||||||
<button mat-flat-button color="primary" (click)="openCreateCommandModal()">{{ 'addCommand' | translate }}</button>
|
<button class="action-button" (click)="openCreateCommandModal()">{{ 'addCommand' | translate }}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<mat-divider class="divider"></mat-divider>
|
|
||||||
|
|
||||||
<div class="search-container" joyrideStep="searchStep" text="{{ 'searchStepText' | translate }}">
|
<div class="search-container" joyrideStep="searchStep" text="{{ 'searchStepText' | translate }}">
|
||||||
<mat-form-field appearance="fill" class="search-string">
|
<mat-form-field appearance="fill" class="search-string">
|
||||||
<mat-label>{{ 'searchCommandLabel' | translate }}</mat-label>
|
<mat-label>{{ 'searchCommandLabel' | translate }}</mat-label>
|
||||||
|
@ -20,7 +20,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="loading" class="loading-container" joyrideStep="loadingStep" text="{{ 'loadingStepText' | translate }}">
|
<div *ngIf="loading" class="loading-container" joyrideStep="loadingStep" text="{{ 'loadingStepText' | translate }}">
|
||||||
<mat-spinner></mat-spinner>
|
<app-loading [isLoading]="loading"></app-loading>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="!loading">
|
<div *ngIf="!loading">
|
||||||
|
@ -31,9 +31,11 @@
|
||||||
<ng-container *ngIf="column.columnDef !== 'readOnly'">
|
<ng-container *ngIf="column.columnDef !== 'readOnly'">
|
||||||
{{ column.cell(command) }}
|
{{ column.cell(command) }}
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container *ngIf="column.columnDef === 'readOnly'">
|
<ng-container *ngIf="column.columnDef === 'readOnly'">
|
||||||
<mat-chip *ngIf="command.readOnly" class="mat-chip-readonly-true"><mat-icon style="color:white;">check</mat-icon></mat-chip>
|
<mat-icon [color]="command[column.columnDef] ? 'primary' : 'warn'">
|
||||||
<mat-chip *ngIf="!command.readOnly" class="mat-chip-readonly-false"><mat-icon style="color:white;">close</mat-icon></mat-chip>
|
{{ command[column.columnDef] ? 'check_circle' : 'cancel' }}
|
||||||
|
</mat-icon>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
@ -41,7 +43,6 @@
|
||||||
<ng-container matColumnDef="actions">
|
<ng-container matColumnDef="actions">
|
||||||
<th mat-header-cell *matHeaderCellDef style="text-align: center;">{{ 'columnActions' | translate }}</th>
|
<th mat-header-cell *matHeaderCellDef style="text-align: center;">{{ 'columnActions' | translate }}</th>
|
||||||
<td mat-cell *matCellDef="let command" style="text-align: center;" joyrideStep="actionsStep" text="{{ 'actionsStepText' | translate }}">
|
<td mat-cell *matCellDef="let command" style="text-align: center;" joyrideStep="actionsStep" text="{{ 'actionsStepText' | translate }}">
|
||||||
<button mat-icon-button color="info" (click)="executeCommand($event, command)"><mat-icon>play_arrow</mat-icon></button>
|
|
||||||
<button mat-icon-button color="info" (click)="viewDetails($event, command)"><mat-icon>visibility</mat-icon></button>
|
<button mat-icon-button color="info" (click)="viewDetails($event, command)"><mat-icon>visibility</mat-icon></button>
|
||||||
<button mat-icon-button color="primary" [disabled]="command.readOnly" (click)="editCommand($event, command)"><mat-icon>edit</mat-icon></button>
|
<button mat-icon-button color="primary" [disabled]="command.readOnly" (click)="editCommand($event, command)"><mat-icon>edit</mat-icon></button>
|
||||||
<button mat-icon-button color="warn" [disabled]="command.readOnly" (click)="deleteCommand($event, command)"><mat-icon>delete</mat-icon></button>
|
<button mat-icon-button color="warn" [disabled]="command.readOnly" (click)="deleteCommand($event, command)"><mat-icon>delete</mat-icon></button>
|
||||||
|
|
|
@ -19,14 +19,21 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
import { NgxChartsModule } from '@swimlane/ngx-charts';
|
import { NgxChartsModule } from '@swimlane/ngx-charts';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { JoyrideModule } from 'ngx-joyride';
|
import { JoyrideModule } from 'ngx-joyride';
|
||||||
|
import { LoadingComponent } from '../../../shared/loading/loading.component';
|
||||||
|
import { ConfigService } from '@services/config.service';
|
||||||
|
|
||||||
describe('CommandsComponent', () => {
|
describe('CommandsComponent', () => {
|
||||||
let component: CommandsComponent;
|
let component: CommandsComponent;
|
||||||
let fixture: ComponentFixture<CommandsComponent>;
|
let fixture: ComponentFixture<CommandsComponent>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
|
const mockConfigService = {
|
||||||
|
apiUrl: 'http://mock-api-url',
|
||||||
|
mercureUrl: 'http://mock-mercure-url'
|
||||||
|
};
|
||||||
|
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
declarations: [CommandsComponent],
|
declarations: [CommandsComponent, LoadingComponent],
|
||||||
imports: [
|
imports: [
|
||||||
HttpClientTestingModule,
|
HttpClientTestingModule,
|
||||||
ToastrModule.forRoot(),
|
ToastrModule.forRoot(),
|
||||||
|
@ -52,7 +59,8 @@ describe('CommandsComponent', () => {
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: MatDialogRef, useValue: {} },
|
{ provide: MatDialogRef, useValue: {} },
|
||||||
{ provide: MAT_DIALOG_DATA, useValue: {} }
|
{ provide: MAT_DIALOG_DATA, useValue: {} },
|
||||||
|
{ provide: ConfigService, useValue: mockConfigService }
|
||||||
]
|
]
|
||||||
|
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { CreateCommandComponent } from './create-command/create-command.componen
|
||||||
import { DeleteModalComponent } from '../../../shared/delete_modal/delete-modal/delete-modal.component';
|
import { DeleteModalComponent } from '../../../shared/delete_modal/delete-modal/delete-modal.component';
|
||||||
import { MatTableDataSource } from '@angular/material/table';
|
import { MatTableDataSource } from '@angular/material/table';
|
||||||
import { DatePipe } from '@angular/common';
|
import { DatePipe } from '@angular/common';
|
||||||
import { ExecuteCommandComponent } from './execute-command/execute-command.component';
|
import { ConfigService } from '@services/config.service';
|
||||||
import { JoyrideService } from 'ngx-joyride';
|
import { JoyrideService } from 'ngx-joyride';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
@ -16,7 +16,8 @@ import { JoyrideService } from 'ngx-joyride';
|
||||||
styleUrls: ['./commands.component.css']
|
styleUrls: ['./commands.component.css']
|
||||||
})
|
})
|
||||||
export class CommandsComponent implements OnInit {
|
export class CommandsComponent implements OnInit {
|
||||||
baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
|
baseUrl: string;
|
||||||
|
private apiUrl: string;
|
||||||
dataSource = new MatTableDataSource<any>();
|
dataSource = new MatTableDataSource<any>();
|
||||||
filters: { [key: string]: string | boolean } = {};
|
filters: { [key: string]: string | boolean } = {};
|
||||||
length: number = 0;
|
length: number = 0;
|
||||||
|
@ -48,10 +49,12 @@ export class CommandsComponent implements OnInit {
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
displayedColumns = [...this.columns.map(column => column.columnDef), 'actions'];
|
displayedColumns = [...this.columns.map(column => column.columnDef), 'actions'];
|
||||||
private apiUrl = `${this.baseUrl}/commands`;
|
|
||||||
|
|
||||||
constructor(private http: HttpClient, private dialog: MatDialog, private toastService: ToastrService,
|
constructor(private http: HttpClient, private dialog: MatDialog, private toastService: ToastrService,
|
||||||
private joyrideService: JoyrideService) {}
|
private joyrideService: JoyrideService, private configService: ConfigService) {
|
||||||
|
this.baseUrl = this.configService.apiUrl;
|
||||||
|
this.apiUrl = `${this.baseUrl}/commands`;
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.search();
|
this.search();
|
||||||
|
@ -82,14 +85,14 @@ export class CommandsComponent implements OnInit {
|
||||||
|
|
||||||
openCreateCommandModal(): void {
|
openCreateCommandModal(): void {
|
||||||
this.dialog.open(CreateCommandComponent, {
|
this.dialog.open(CreateCommandComponent, {
|
||||||
width: '600px',
|
width: '800px',
|
||||||
}).afterClosed().subscribe(() => this.search());
|
}).afterClosed().subscribe(() => this.search());
|
||||||
}
|
}
|
||||||
|
|
||||||
editCommand(event: MouseEvent, command: any): void {
|
editCommand(event: MouseEvent, command: any): void {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
this.dialog.open(CreateCommandComponent, {
|
this.dialog.open(CreateCommandComponent, {
|
||||||
width: '600px',
|
width: '800px',
|
||||||
data: command['@id']
|
data: command['@id']
|
||||||
}).afterClosed().subscribe(() => this.search());
|
}).afterClosed().subscribe(() => this.search());
|
||||||
}
|
}
|
||||||
|
@ -114,19 +117,6 @@ export class CommandsComponent implements OnInit {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
executeCommand(event: MouseEvent, command: any): void {
|
|
||||||
this.dialog.open(ExecuteCommandComponent, {
|
|
||||||
width: '50%',
|
|
||||||
data: { commandData: command }
|
|
||||||
}).afterClosed().subscribe((result) => {
|
|
||||||
if (result) {
|
|
||||||
console.log('Comando ejecutado con éxito');
|
|
||||||
} else {
|
|
||||||
console.log('Ejecución de comando cancelada');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onPageChange(event: any): void {
|
onPageChange(event: any): void {
|
||||||
this.page = event.pageIndex;
|
this.page = event.pageIndex;
|
||||||
this.itemsPerPage = event.pageSize;
|
this.itemsPerPage = event.pageSize;
|
||||||
|
@ -147,5 +137,5 @@ export class CommandsComponent implements OnInit {
|
||||||
themeColor: '#3f51b5'
|
themeColor: '#3f51b5'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,12 +7,6 @@
|
||||||
|
|
||||||
.form-group {
|
.form-group {
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
margin-bottom: 26px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.full-width {
|
|
||||||
width: 100%;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.additional-form {
|
.additional-form {
|
||||||
|
@ -58,3 +52,20 @@
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.action-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 1em;
|
||||||
|
padding: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-with-hint {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hint-text {
|
||||||
|
font-size: 12px;
|
||||||
|
color: gray;
|
||||||
|
margin-left: 40px;
|
||||||
|
}
|
||||||
|
|
|
@ -13,7 +13,13 @@
|
||||||
|
|
||||||
<div class="checkbox-group">
|
<div class="checkbox-group">
|
||||||
<mat-checkbox formControlName="readOnly">{{ 'readOnlyLabel' | translate }}</mat-checkbox>
|
<mat-checkbox formControlName="readOnly">{{ 'readOnlyLabel' | translate }}</mat-checkbox>
|
||||||
|
|
||||||
<mat-checkbox formControlName="enabled">{{ 'enabledLabel' | translate }}</mat-checkbox>
|
<mat-checkbox formControlName="enabled">{{ 'enabledLabel' | translate }}</mat-checkbox>
|
||||||
|
|
||||||
|
<div class="checkbox-with-hint">
|
||||||
|
<mat-checkbox formControlName="parameters">{{ 'parameters' | translate }}</mat-checkbox>
|
||||||
|
<span class="hint-text">Si se selecciona esta opción los parámetros deben indicarse en el script con el símbolo @.</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<mat-form-field appearance="fill" class="full-width">
|
<mat-form-field appearance="fill" class="full-width">
|
||||||
|
@ -23,7 +29,7 @@
|
||||||
</form>
|
</form>
|
||||||
</mat-dialog-content>
|
</mat-dialog-content>
|
||||||
|
|
||||||
<mat-dialog-actions align="end">
|
<mat-dialog-actions class="action-container">
|
||||||
<button mat-button (click)="onCancel($event)">{{ 'buttonCancel' | translate }}</button>
|
<button class="ordinary-button" (click)="onCancel($event)">{{ 'buttonCancel' | translate }}</button>
|
||||||
<button mat-button (click)="onSubmit()" cdkFocusInitial>{{ 'buttonSave' | translate }}</button>
|
<button class="submit-button" (click)="onSubmit()" cdkFocusInitial>{{ 'buttonSave' | translate }}</button>
|
||||||
</mat-dialog-actions>
|
</mat-dialog-actions>
|
||||||
|
|
|
@ -13,12 +13,18 @@ import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||||
import { MatButtonModule } from '@angular/material/button';
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { ConfigService } from '@services/config.service';
|
||||||
|
|
||||||
describe('CreateCommandComponent', () => {
|
describe('CreateCommandComponent', () => {
|
||||||
let component: CreateCommandComponent;
|
let component: CreateCommandComponent;
|
||||||
let fixture: ComponentFixture<CreateCommandComponent>;
|
let fixture: ComponentFixture<CreateCommandComponent>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
|
const mockConfigService = {
|
||||||
|
apiUrl: 'http://mock-api-url',
|
||||||
|
mercureUrl: 'http://mock-mercure-url'
|
||||||
|
};
|
||||||
|
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
declarations: [CreateCommandComponent],
|
declarations: [CreateCommandComponent],
|
||||||
imports: [
|
imports: [
|
||||||
|
@ -45,7 +51,8 @@ describe('CreateCommandComponent', () => {
|
||||||
{
|
{
|
||||||
provide: MAT_DIALOG_DATA,
|
provide: MAT_DIALOG_DATA,
|
||||||
useValue: {}
|
useValue: {}
|
||||||
}
|
},
|
||||||
|
{ provide: ConfigService, useValue: mockConfigService }
|
||||||
]
|
]
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
import { Component, Inject } from '@angular/core';
|
import {Component, Inject, OnInit} from '@angular/core';
|
||||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||||
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
|
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { ToastrService } from 'ngx-toastr';
|
import { ToastrService } from 'ngx-toastr';
|
||||||
import {DataService} from "../data.service";
|
import { DataService } from "../data.service";
|
||||||
|
import { ConfigService } from "@services/config.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-create-command',
|
selector: 'app-create-command',
|
||||||
templateUrl: './create-command.component.html',
|
templateUrl: './create-command.component.html',
|
||||||
styleUrls: ['./create-command.component.css']
|
styleUrls: ['./create-command.component.css']
|
||||||
})
|
})
|
||||||
export class CreateCommandComponent {
|
export class CreateCommandComponent implements OnInit{
|
||||||
baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
|
baseUrl: string;
|
||||||
createCommandForm: FormGroup<any>;
|
createCommandForm: FormGroup<any>;
|
||||||
private apiUrl = `${this.baseUrl}/commands`;
|
|
||||||
commandId: string | null = null;
|
commandId: string | null = null;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -21,13 +21,16 @@ export class CreateCommandComponent {
|
||||||
private http: HttpClient,
|
private http: HttpClient,
|
||||||
public dialogRef: MatDialogRef<CreateCommandComponent>,
|
public dialogRef: MatDialogRef<CreateCommandComponent>,
|
||||||
private toastService: ToastrService,
|
private toastService: ToastrService,
|
||||||
|
private configService: ConfigService,
|
||||||
private dataService: DataService,
|
private dataService: DataService,
|
||||||
@Inject(MAT_DIALOG_DATA) public data: any
|
@Inject(MAT_DIALOG_DATA) public data: any
|
||||||
) {
|
) {
|
||||||
|
this.baseUrl = this.configService.apiUrl;
|
||||||
this.createCommandForm = this.fb.group({
|
this.createCommandForm = this.fb.group({
|
||||||
name: ['', Validators.required],
|
name: ['', Validators.required],
|
||||||
script: [''],
|
script: [''],
|
||||||
readOnly: [false],
|
readOnly: [false],
|
||||||
|
parameters: [false],
|
||||||
enabled: [true],
|
enabled: [true],
|
||||||
comments: [''],
|
comments: [''],
|
||||||
});
|
});
|
||||||
|
@ -42,12 +45,12 @@ export class CreateCommandComponent {
|
||||||
load(): void {
|
load(): void {
|
||||||
this.dataService.getCommand(this.data).subscribe({
|
this.dataService.getCommand(this.data).subscribe({
|
||||||
next: (response) => {
|
next: (response) => {
|
||||||
console.log(response);
|
|
||||||
this.createCommandForm = this.fb.group({
|
this.createCommandForm = this.fb.group({
|
||||||
name: [response.name, Validators.required],
|
name: [response.name, Validators.required],
|
||||||
notes: [response.notes],
|
notes: [response.notes],
|
||||||
script: [response.script],
|
script: [response.script],
|
||||||
readOnly: [response.readOnly],
|
readOnly: [response.readOnly],
|
||||||
|
parameters: [response.parameters],
|
||||||
enabled: [response.enabled],
|
enabled: [response.enabled],
|
||||||
});
|
});
|
||||||
this.commandId = response['@id'];
|
this.commandId = response['@id'];
|
||||||
|
@ -82,7 +85,6 @@ export class CreateCommandComponent {
|
||||||
},
|
},
|
||||||
(error) => {
|
(error) => {
|
||||||
this.toastService.error(error['error']['hydra:description']);
|
this.toastService.error(error['error']['hydra:description']);
|
||||||
console.error('Error al editar el comando', error);
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
@ -93,7 +95,6 @@ export class CreateCommandComponent {
|
||||||
},
|
},
|
||||||
(error) => {
|
(error) => {
|
||||||
this.toastService.error(error['error']['hydra:description']);
|
this.toastService.error(error['error']['hydra:description']);
|
||||||
console.error('Error al añadir comando', error);
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
|
import { ConfigService } from '@services/config.service';
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import {HttpClient, HttpParams} from '@angular/common/http';
|
import { HttpClient, HttpParams } from '@angular/common/http';
|
||||||
import { Observable, throwError } from 'rxjs';
|
import { Observable, throwError } from 'rxjs';
|
||||||
import { catchError, map } from 'rxjs/operators';
|
import { catchError, map } from 'rxjs/operators';
|
||||||
|
|
||||||
|
@ -8,10 +8,16 @@ import { catchError, map } from 'rxjs/operators';
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class DataService {
|
export class DataService {
|
||||||
baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
|
baseUrl: string;
|
||||||
private apiUrl = `${this.baseUrl}/commands?page=1&itemsPerPage=1000`;
|
private apiUrl: string;
|
||||||
|
|
||||||
constructor(private http: HttpClient) {}
|
constructor(
|
||||||
|
private http: HttpClient,
|
||||||
|
private configService: ConfigService
|
||||||
|
) {
|
||||||
|
this.baseUrl = this.configService.apiUrl;
|
||||||
|
this.apiUrl = `${this.baseUrl}/commands?page=1&itemsPerPage=1000`;
|
||||||
|
}
|
||||||
|
|
||||||
getCommands(filters: { [key: string]: string }): Observable<{ totalItems: any; data: any }> {
|
getCommands(filters: { [key: string]: string }): Observable<{ totalItems: any; data: any }> {
|
||||||
const params = new HttpParams({ fromObject: filters });
|
const params = new HttpParams({ fromObject: filters });
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dial
|
||||||
import { CreateCommandComponent } from '../create-command/create-command.component';
|
import { CreateCommandComponent } from '../create-command/create-command.component';
|
||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { ToastrService } from 'ngx-toastr';
|
import { ToastrService } from 'ngx-toastr';
|
||||||
|
import { ConfigService } from '@services/config.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-command-detail',
|
selector: 'app-command-detail',
|
||||||
|
@ -11,7 +12,7 @@ import { ToastrService } from 'ngx-toastr';
|
||||||
styleUrls: ['./command-detail.component.css']
|
styleUrls: ['./command-detail.component.css']
|
||||||
})
|
})
|
||||||
export class CommandDetailComponent implements OnInit {
|
export class CommandDetailComponent implements OnInit {
|
||||||
baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
|
baseUrl: string;
|
||||||
form!: FormGroup;
|
form!: FormGroup;
|
||||||
clients: any[] = [];
|
clients: any[] = [];
|
||||||
showClientSelect = false;
|
showClientSelect = false;
|
||||||
|
@ -20,12 +21,15 @@ export class CommandDetailComponent implements OnInit {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private fb: FormBuilder,
|
private fb: FormBuilder,
|
||||||
|
private configService: ConfigService,
|
||||||
private http: HttpClient,
|
private http: HttpClient,
|
||||||
public dialogRef: MatDialogRef<CommandDetailComponent>,
|
public dialogRef: MatDialogRef<CommandDetailComponent>,
|
||||||
private dialog: MatDialog,
|
private dialog: MatDialog,
|
||||||
private toastService: ToastrService,
|
private toastService: ToastrService,
|
||||||
@Inject(MAT_DIALOG_DATA) public data: any
|
@Inject(MAT_DIALOG_DATA) public data: any
|
||||||
) { }
|
) {
|
||||||
|
this.baseUrl = this.configService.apiUrl;
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.form = this.fb.group({
|
this.form = this.fb.group({
|
||||||
|
@ -52,7 +56,7 @@ export class CommandDetailComponent implements OnInit {
|
||||||
|
|
||||||
dialogRef.afterClosed().subscribe(result => {
|
dialogRef.afterClosed().subscribe(result => {
|
||||||
if (result) {
|
if (result) {
|
||||||
this.toastService.success('Comando editado' );
|
this.toastService.success('Comando editado');
|
||||||
this.data.command = result;
|
this.data.command = result;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,40 +1,24 @@
|
||||||
<h2 mat-dialog-title>{{ 'executeCommandTitle' | translate }}</h2>
|
<ng-container [ngSwitch]="buttonType">
|
||||||
|
<button *ngSwitchCase="'icon'" mat-icon-button color="primary" [matMenuTriggerFor]="commandMenu"
|
||||||
|
[disabled]="disabled">
|
||||||
|
<mat-icon>{{ icon }}</mat-icon>
|
||||||
|
</button>
|
||||||
|
|
||||||
<mat-dialog-content class="form-container">
|
<button class="action-button" [disabled]="clientData.length === 0 || disabled" *ngSwitchCase="'text'"
|
||||||
<form [formGroup]="form" class="command-form">
|
[matMenuTriggerFor]="commandMenu">
|
||||||
|
{{ buttonText }}
|
||||||
<mat-form-field appearance="fill" class="full-width">
|
</button>
|
||||||
<mat-label>{{ 'organizationalUnitLabel' | translate }}</mat-label>
|
|
||||||
<mat-select formControlName="unit">
|
|
||||||
<mat-option *ngFor="let unit of units" [value]="unit.uuid">{{ unit.name }}</mat-option>
|
|
||||||
</mat-select>
|
|
||||||
</mat-form-field>
|
|
||||||
|
|
||||||
<mat-form-field appearance="fill" class="full-width">
|
<button mat-menu-item *ngSwitchCase="'menu-item'" [matMenuTriggerFor]="commandMenu" [disabled]="disabled">
|
||||||
<mat-label>{{ 'subOrganizationalUnitLabel' | translate }}</mat-label>
|
<mat-icon>{{ icon }}</mat-icon>
|
||||||
<mat-select formControlName="childUnit">
|
<span>{{ buttonText }}</span>
|
||||||
<mat-option *ngFor="let child of childUnits" [value]="child.uuid">{{ child.name }}</mat-option>
|
</button>
|
||||||
</mat-select>
|
</ng-container>
|
||||||
</mat-form-field>
|
|
||||||
|
|
||||||
<div class="checkbox-group">
|
<mat-menu #commandMenu="matMenu">
|
||||||
<label>{{ 'clientsLabel' | translate }}</label>
|
<button mat-menu-item [disabled]="command.disabled
|
||||||
<div *ngIf="clients.length > 0">
|
|| (command.slug === 'create-image' && clientData.length > 1)" *ngFor="let command of arrayCommands"
|
||||||
<mat-checkbox *ngFor="let client of clients"
|
(click)="onCommandSelect(command.slug)">
|
||||||
(change)="toggleClientSelection(client.uuid)"
|
{{ command.name }}
|
||||||
[checked]="form.get('clientSelection')?.value.includes(client.uuid)">
|
</button>
|
||||||
{{ client.name }}
|
</mat-menu>
|
||||||
</mat-checkbox>
|
|
||||||
</div>
|
|
||||||
<div *ngIf="clients.length === 0">
|
|
||||||
<p>{{ 'noClientsAvailable' | translate }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</form>
|
|
||||||
</mat-dialog-content>
|
|
||||||
|
|
||||||
<mat-dialog-actions align="end">
|
|
||||||
<button mat-button (click)="closeModal()">{{ 'buttonCancel' | translate }}</button>
|
|
||||||
<button mat-button (click)="executeCommand()" [disabled]="!form.get('clientSelection')?.value.length">{{ 'buttonExecute' | translate }}</button>
|
|
||||||
</mat-dialog-actions>
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { ExecuteCommandComponent } from './execute-command.component';
|
import { ExecuteCommandComponent } from './execute-command.component';
|
||||||
import { provideHttpClient } from '@angular/common/http';
|
import { provideHttpClient } from '@angular/common/http';
|
||||||
import { provideHttpClientTesting } from '@angular/common/http/testing';
|
import { provideHttpClientTesting } from '@angular/common/http/testing';
|
||||||
|
@ -10,17 +9,25 @@ import { MatDialogModule, MatDialogRef, MAT_DIALOG_DATA } from '@angular/materia
|
||||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
import { MatInputModule } from '@angular/material/input';
|
import { MatInputModule } from '@angular/material/input';
|
||||||
import { MatTableModule } from '@angular/material/table';
|
import { MatTableModule } from '@angular/material/table';
|
||||||
import { MatSelectModule } from '@angular/material/select';
|
import { MatSelectModule } from '@angular/material/select';
|
||||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { ToastrModule, ToastrService } from 'ngx-toastr';
|
import { ToastrModule, ToastrService } from 'ngx-toastr';
|
||||||
import { DataService } from '../data.service';
|
import { DataService } from '../data.service';
|
||||||
|
import {MatIconModule} from "@angular/material/icon";
|
||||||
|
import {MatMenu, MatMenuModule} from "@angular/material/menu";
|
||||||
|
import { ConfigService } from '@services/config.service';
|
||||||
|
|
||||||
describe('ExecuteCommandComponent', () => {
|
describe('ExecuteCommandComponent', () => {
|
||||||
let component: ExecuteCommandComponent;
|
let component: ExecuteCommandComponent;
|
||||||
let fixture: ComponentFixture<ExecuteCommandComponent>;
|
let fixture: ComponentFixture<ExecuteCommandComponent>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
|
const mockConfigService = {
|
||||||
|
apiUrl: 'http://mock-api-url',
|
||||||
|
mercureUrl: 'http://mock-mercure-url'
|
||||||
|
};
|
||||||
|
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
declarations: [ExecuteCommandComponent],
|
declarations: [ExecuteCommandComponent],
|
||||||
imports: [
|
imports: [
|
||||||
|
@ -31,9 +38,11 @@ describe('ExecuteCommandComponent', () => {
|
||||||
MatInputModule,
|
MatInputModule,
|
||||||
MatCheckboxModule,
|
MatCheckboxModule,
|
||||||
MatButtonModule,
|
MatButtonModule,
|
||||||
|
MatMenuModule,
|
||||||
BrowserAnimationsModule,
|
BrowserAnimationsModule,
|
||||||
MatTableModule,
|
MatTableModule,
|
||||||
MatSelectModule,
|
MatSelectModule,
|
||||||
|
MatIconModule,
|
||||||
ToastrModule.forRoot(),
|
ToastrModule.forRoot(),
|
||||||
TranslateModule.forRoot()
|
TranslateModule.forRoot()
|
||||||
],
|
],
|
||||||
|
@ -50,7 +59,8 @@ describe('ExecuteCommandComponent', () => {
|
||||||
{
|
{
|
||||||
provide: MAT_DIALOG_DATA,
|
provide: MAT_DIALOG_DATA,
|
||||||
useValue: {}
|
useValue: {}
|
||||||
}
|
},
|
||||||
|
{ provide: ConfigService, useValue: mockConfigService }
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue