Compare commits

...

185 Commits

Author SHA1 Message Date
Lampros Smyrnaios 17d0ac6c33 Merge pull request 'Resolve tickets 9114 and 9031' (#12) from develop into master
Reviewed-on: #12
2024-04-04 15:33:10 +02:00
Lampros Smyrnaios e4009b7903 Merge remote-tracking branch 'origin/master'
# Conflicts:
#	README.md
2024-04-04 16:16:31 +03:00
Lampros Smyrnaios 4f1e3a14d4 Fix conflicts in README. 2024-04-04 16:15:17 +03:00
Lampros Smyrnaios cb458553f0 - Update the "RepositoryServiceImpl.getCompatibilityClasses()" method to recognise additional "CRIS" modes.
- Catch exceptions thrown in "RepositoryServiceImpl.searchApi()".
- Code optimization.
- Update dependencies.
2024-04-03 14:59:43 +03:00
Lampros Smyrnaios c836de5761 Use the new DSM-2.0 API to obtain the "aggregation history" of a repository. 2024-03-22 11:51:22 +02:00
Lampros Smyrnaios 6dfe11484e - Add the Maven Central repository.
- Add/update/fix/cleanup dependencies.
- Add the "show-actuator"-config for springdoc.
- Remove obsolete configuration.
2024-03-15 13:58:38 +02:00
Lampros Smyrnaios 0deeb00ee5 - Replaced DSM's "AggregationInfo" with "CollectionInfoV2" in "eu.dnetlib.repo.manager.domain.AggregationInfo".
- Cleanup related dependencies.

Co-authored-by: Konstantinos Spyrou <spyroukon@gmail.com>
2024-03-13 18:20:45 +02:00
Lampros Smyrnaios b047caee26 Merge pull request 'Documentation cleanup.' (#11) from lsmyrnaios-patch-2 into master
Reviewed-on: #11
2024-03-11 12:27:21 +01:00
Lampros Smyrnaios 958e6b304e Documentation cleanup. 2024-03-11 12:26:30 +01:00
Antonis Lempesis 44610c172d Merge pull request 'Add/update documentation.' (#10) from lsmyrnaios-patch-1 into master
Reviewed-on: #10
2024-03-11 11:41:32 +01:00
Lampros Smyrnaios 547b3107a3 Add/update documentation. 2024-03-11 11:39:24 +01:00
Antonis Lempesis 4e7ba9d5cb Update 'README.md' 2024-03-11 10:51:15 +01:00
Antonis Lempesis 62aa87bd86 updated documentation 2024-03-11 10:50:21 +01:00
Lampros Smyrnaios 7853494a5b Remove obsolete paragraph. 2023-11-15 13:01:56 +02:00
Lampros Smyrnaios d1c336f4e7 Merge branch 'master' into develop 2023-11-14 18:50:30 +02:00
Lampros Smyrnaios 1c9ff0669a - Add information in the README, about the "Building", "Deployment", "Installation" and "Configuration" of the project.
- Eliminate a warning in the POM file, about a missing tag.
2023-11-14 18:45:45 +02:00
Antonis Lempesis cb8c68e8f9 Update 'README.md' 2023-11-14 15:25:37 +01:00
Antonis Lempesis 6f964e3d16 Update 'README.md' 2023-11-14 15:25:16 +01:00
Antonis Lempesis a673450f9c Update 'README.md' 2023-11-14 15:21:08 +01:00
Lampros Smyrnaios 224d058d89 Merge pull request 'merge master to develop' (#9) from master into develop
Reviewed-on: #9
2023-11-02 14:16:54 +01:00
Lampros Smyrnaios 91e0b66b28 Merge pull request 'Merge develop into master' (#6) from develop into master
Reviewed-on: #6
2023-11-02 14:05:14 +01:00
Lampros Smyrnaios c838f71227 merge from disable_compliance_failure_emails 2023-11-02 15:04:19 +02:00
Lampros Smyrnaios 0b8e4c4729 Update the Sushilite-R5 dependency from Nexus. 2023-11-02 14:39:10 +02:00
Lampros Smyrnaios a72c5b2fb8 - Update dependencies.
- Fix a file-extension in README.
2023-10-31 13:56:59 +02:00
Konstantinos Spyrou bd315848e0 fixed typo 2023-10-11 14:53:44 +03:00
Konstantinos Spyrou 4b5132358a created repository metrics calls and excluded them from authentication 2023-10-11 14:45:53 +03:00
Konstantinos Spyrou 39596cfcc1 added method to get if piwik is validated 2023-10-11 13:03:00 +03:00
Konstantinos Spyrou 5138605163 added method to get if piwik is validated 2023-10-11 13:00:48 +03:00
Lampros Smyrnaios 75e97ece26 Add info about the Swagger-UI-url in the README.md 2023-10-04 12:29:52 +03:00
TheQuaker 9da5fa5c1e Fix typo for DataType param 2023-09-29 15:36:55 +03:00
Lampros Smyrnaios 842468a175 Improve performance and error-handling in "InterfaceComplianceService.cleanUp()". 2023-09-27 12:24:22 +03:00
Lampros Smyrnaios 64fdff095d - Resolve a dependency-conflict.
- Update dependencies.
2023-09-25 15:01:31 +03:00
Lampros Smyrnaios 11460764a2 Merge pull request 'Disable sending emails about compliance failure.' (#5) from disable_compliance_failure_emails into master
Reviewed-on: #5
2023-07-24 12:47:31 +02:00
Lampros Smyrnaios 4e1f9cb965 Disable sending emails about compliance failure. 2023-07-24 13:45:24 +03:00
Lampros Smyrnaios 3cb8e7a5f6 - Update dependencies.
- Code polishing.
2023-07-05 13:16:48 +03:00
Lampros Smyrnaios eb42185991 - Update dependencies.
- Add notes for swagger-UI in README.
2023-06-06 19:27:02 +03:00
Lampros Smyrnaios a2a387be90 - Fix a security config for Swagger.
- Add the "displayRequestDuration" for "Try it out" requests in Swagger.
- Code polishing.
2023-06-02 14:23:54 +03:00
Lampros Smyrnaios dc469df9d6 Add JPA configuration. This fixes a missing Bean error. 2023-06-02 13:00:25 +03:00
Lampros Smyrnaios 8f6426eaf6 - Upgrade Spring and other dependencies.
- Migrate to SpringDoc, since SpringFox is dead.
2023-06-01 20:48:01 +03:00
Konstantinos Spyrou 6024e0c5b5 changed application type and replaced counters with gauges 2023-04-04 18:45:07 +03:00
Konstantinos Spyrou ab854a6624 changed application type and replaced counters with gauges 2023-04-04 18:43:48 +03:00
Lampros Smyrnaios 311442854a Add missing "sushiliteR5Endpoint" property. 2023-03-29 15:15:53 +03:00
Lampros Smyrnaios dfc4a4690e Update Json and Gson dependencies. 2023-03-29 12:11:19 +03:00
Lampros Smyrnaios 0ce67b6355 Avoid assigning an empty list, in case the "reportItems" is null, inside "SushiliteServiceImpl.getReportResults()". 2023-03-29 12:08:43 +03:00
Lampros Smyrnaios 54ebb35161 Detect the "504 Gateway Time-out" error from Sushilite-R5 and provide a mention to it, in the response-body. 2023-03-29 12:04:34 +03:00
Lampros Smyrnaios 97e85c2a75 Add suchilite-R5 support. 2023-03-28 15:08:20 +03:00
Lampros Smyrnaios 7cab17c133 Move Sushilite Service in its own package. Later, additional Sushilite Services will be added. 2023-03-22 13:35:20 +02:00
Lampros Smyrnaios d2973871a8 Inform the developer about the possibility of a "RestClientException" being thrown by various methods of "HttpUtils" and "RegistryCalls" classes. 2023-03-08 15:26:26 +02:00
Lampros Smyrnaios a0fd5f67a7 Fix the "NoSuchBeanDefinitionException: No bean named 'transactionManager' available", when trying to run the scheduled task -during initialization- for assigning pending user roles. 2023-03-08 13:57:29 +02:00
Lampros Smyrnaios 59fc344730 - Use the production AAI url, as the "beta" one has issues.
- Polish the application.yml
2023-03-02 16:15:54 +02:00
Antonis Lempesis 85f3d1c9dc Merge pull request 'reposByCountry' (#4) from reposByCountry into master
Reviewed-on: #4
2023-03-02 14:05:51 +01:00
Lampros Smyrnaios fe1a398773 - Show error-messages for Broker's errors.
- Code polishing.
2023-03-02 15:03:30 +02:00
Lampros Smyrnaios 3684f09970 Update application.yml 2023-03-02 15:03:06 +02:00
Lampros Smyrnaios 19fcda5ae1 Update the "getRepositoriesByCountry"-endpoint to return up to 10_000 repositories. 2023-03-02 15:02:52 +02:00
Konstantinos Spyrou e9fedc90d4 experimental: temp save role if assign fails 2023-02-17 20:40:51 +02:00
Konstantinos Spyrou 0ad86025b3 throw error when assigning role to a user who is not found in the aai registry 2023-02-17 17:35:18 +02:00
Konstantinos Spyrou bae9a132cb Merge branch 'develop' of code-repo.d4science.org:MaDgIK/uoa-repository-manager-service into develop 2023-02-17 16:17:56 +02:00
Konstantinos Spyrou b67e98976d Improved role functionality 2023-02-17 16:17:37 +02:00
Lampros Smyrnaios c006c7baa0 - Show error-messages for Broker's errors.
- Code polishing.
2023-02-15 18:31:13 +02:00
Lampros Smyrnaios 8f5aed3aab Update application.yml 2023-02-15 18:03:20 +02:00
Lampros Smyrnaios 0f0163dc2d Update the "getRepositoriesByCountry"-endpoint to return up to 10_000 repositories. 2023-02-15 17:14:02 +02:00
Antonis Lempesis 6041bb8f1f Add 'LICENSE.txt' 2023-02-14 20:39:36 +01:00
Konstantinos Spyrou 401de371b2 Merge branch 'develop' 2023-02-08 12:15:12 +02:00
Konstantinos Spyrou ccf826ee42 Merge branch 'master' of code-repo.d4science.org:MaDgIK/uoa-repository-manager-service 2023-02-08 12:14:57 +02:00
Konstantinos Spyrou 9ad28a2640 merge branch new_registry 2023-02-08 10:21:02 +02:00
Lampros Smyrnaios 06cea537bf Improve performance of "DashboardController.getCollectionMonitorSummary()"-endpoint.
Now, a single "getRepositoryAggregations"-call is made, which results in just one "DSM-aggregationhistory"-api-call. Previously, multiple api-calls where made, each requesting the whole "aggregationhistory"-data and then trimming it down to only a few results.
2023-02-03 14:19:43 +02:00
Lampros Smyrnaios c1abaad3e1 - Show a warning when the "indexedVersion" was not found in "DashboardServiceImpl.getRepositoriesSummaryInfo()".
- Code optimization and polishing.
2023-02-02 18:54:46 +02:00
Lampros Smyrnaios 3845a5103b - Fix a performance bug in "DashboardController.getCollectionMonitorSummary()", where the code for the "isIndexedVersion"-check, was re-checking 20 of the already-checked "aggregationInfo"-objects, in each new batch.
- Update some properties.
- Code polishing.
2023-02-02 18:47:04 +02:00
Lampros Smyrnaios 8bf0f76ad6 - Improve performance of "ValidatorController.getRuleSet()" endpoint.
- Improve exception-handling.
- Replace deprecated method-calls.
- Code polishing.
2023-01-31 16:57:37 +02:00
Lampros Smyrnaios a239164f97 - Ensure the "BufferedReader" and the "InputStream" get always closed in the end, in "Converter.readFile()".
- Code polishing.
2023-01-31 15:40:06 +02:00
Konstantinos Spyrou c2e5af87e8 changes in mail content 2023-01-31 15:26:19 +02:00
Konstantinos Spyrou 19923c19fc refactoring 2023-01-31 14:51:34 +02:00
Konstantinos Spyrou 1a3399f7d7 changed level in verbose logs 2023-01-31 14:35:48 +02:00
Konstantinos Spyrou fdd597cf02 changed 'validation set' to 'set' and 'guidelines' to 'selected guidelines' in message bodies 2023-01-31 12:25:41 +02:00
Konstantinos Spyrou 60f8c8cb82 use Compatibility Override field 2023-01-31 12:11:41 +02:00
Konstantinos Spyrou 699b45d62f Missed 3 methods in previous commit. Needs heavy refactoring.. 2023-01-30 20:04:11 +02:00
Konstantinos Spyrou 287f476bd2 messages sent to 'registeredBy' email are now sent to the currently authenticated user as well 2023-01-30 19:49:33 +02:00
Konstantinos Spyrou d3bcea79c0 Changes:
1. refactored method signatures
2. used desired compatibility level for 'Guidelines' field in emails
3. enabled async error logs
2023-01-30 18:56:34 +02:00
Lampros Smyrnaios de2ac5631f Fix return object of "getUrlsOfUserRepos" endpoint. 2023-01-30 16:25:21 +02:00
Lampros Smyrnaios 97d81ce478 - Add missing repositories in pom.xml.
- Update README.md
2023-01-30 15:31:06 +02:00
Lampros Smyrnaios b9cde0f1a9 Fix "SolrServerException: Server refused connection", during startup. 2023-01-30 13:44:15 +02:00
Lampros Smyrnaios 40aae5d750 Adjust logging-levels. 2023-01-25 14:05:30 +02:00
Konstantinos Spyrou 8c07950459 Merge pull request 'develop' (#3) from develop into master
Reviewed-on: #3
2023-01-23 17:36:04 +01:00
Konstantinos Spyrou 9e4817f2bd refactoring 2023-01-23 18:26:19 +02:00
Konstantinos Spyrou 4231480cbc disabled delete on interfaces 2023-01-23 17:57:44 +02:00
Konstantinos Spyrou 33694d17c1 disabled delete on interfaces 2023-01-23 17:57:18 +02:00
Konstantinos Spyrou 0aa604a192 refactoring 2023-01-23 17:54:51 +02:00
Konstantinos Spyrou caa1f1244a refactoring 2023-01-23 17:45:35 +02:00
Konstantinos Spyrou 63b99ea68e base url can be altered only if existing is null or empty 2023-01-20 19:19:02 +02:00
Konstantinos Spyrou ab03a5164d base url can be altered only if existing is null or empty 2023-01-20 19:17:06 +02:00
Konstantinos Spyrou 607c417e3f get all ids of user using email in registry 2023-01-20 19:15:59 +02:00
Konstantinos Spyrou aeca9ce38b Merge pull request 'check if CouId is null' (#2) from develop into master
Reviewed-on: #2
2023-01-19 14:40:37 +01:00
Antonis Lempesis 242014f2da Merge pull request 'develop' (#1) from develop into master
Reviewed-on: #1
2023-01-19 12:41:44 +01:00
Konstantinos Spyrou 7b7c6b0842 check if CouId is null 2023-01-19 13:41:37 +02:00
Konstantinos Spyrou 125b64ec3b check if CouId is null 2023-01-19 13:41:11 +02:00
Konstantinos Spyrou 415bb1e197 fixed conflict 2023-01-19 13:09:52 +02:00
Konstantinos Spyrou 95335461a2 getRolesWithStatus does not return null 2023-01-19 13:08:03 +02:00
Konstantinos Spyrou f88d9b8381 fixed deserialization issue caused from json to String 2023-01-19 12:29:12 +02:00
Konstantinos Spyrou cda2cd5557 fixed deserialization issue caused from json to String 2023-01-19 12:28:31 +02:00
Konstantinos Spyrou 0674a3c031 fixed null pointer exception 2023-01-19 12:16:42 +02:00
Konstantinos Spyrou 0ea91e909d Merge branch 'develop' of code-repo.d4science.org:MaDgIK/uoa-repository-manager-service into develop 2023-01-19 12:15:41 +02:00
Konstantinos Spyrou d5327aedea fixed null pointer exception 2023-01-19 12:15:30 +02:00
Konstantinos Spyrou 7c5020c205 update user roles using identifier instead of email 2023-01-18 16:54:18 +02:00
Lampros Smyrnaios df14f5eaab - Update a "URLEncoder.encode()" call, to reduce the ambiguity which comes with the platform's default encoding.
- Update a deprecated MediaType.
- Fix an ambiguous unicode character.
2023-01-17 20:21:00 +02:00
Konstantinos Spyrou e4d52c2323 refactoring project structure 2023-01-17 18:23:59 +02:00
Konstantinos Spyrou 139fb420d4 removed empty class 2023-01-17 18:21:20 +02:00
Konstantinos Spyrou 60ff3dce6e changes for new aai registry 2023-01-17 18:20:41 +02:00
Konstantinos Spyrou 40b20b1122 added LIMIT 1 when retrieving piwik site to avoid problems with existing duplicates in db 2023-01-17 17:56:18 +02:00
Konstantinos Spyrou 49cc3d74e8 fixed content type missmatch error when converting response from json to string in method getRepositoriesByCountry 2023-01-17 16:57:14 +02:00
Konstantinos Spyrou e551a7a783 replaced manual configuration with automatic 2023-01-17 16:53:37 +02:00
Konstantinos Spyrou 788dac69ad added tests for InterfaceComplianceRequest 2023-01-17 15:50:43 +02:00
Konstantinos Spyrou b1e1185765 fixed condition 2023-01-17 13:49:00 +02:00
Konstantinos Spyrou 817446df41 Created the functionality to alter an interface's compliance after its successful validation 2023-01-16 20:30:09 +02:00
Konstantinos Spyrou 87f610b06b merge conflict 2023-01-13 18:11:41 +02:00
Konstantinos Spyrou e947fe3fec 1. removed exceptions from emails
2. changed update repository interface
3. wip: after validation completes change interface compliance
2023-01-13 18:06:30 +02:00
Lampros Smyrnaios 96003f9f88 Optimize imports. 2023-01-13 12:55:15 +02:00
Lampros Smyrnaios d4e4624a3f Merge branch 'develop' of https://code-repo.d4science.org/MaDgIK/uoa-repository-manager-service into develop
# Conflicts:
#	src/main/java/eu/dnetlib/repo/manager/service/security/AuthoritiesMapper.java
#	src/main/java/eu/dnetlib/repo/manager/service/security/AuthoritiesUpdater.java
2023-01-13 12:22:28 +02:00
Lampros Smyrnaios eaa4083b04 - Code optimization and polishing.
- Improve error-checking and exception-handling.
2023-01-13 12:20:06 +02:00
Lampros Smyrnaios f136488b0e Improve performance by pre-compiling the regexes. 2023-01-12 19:23:42 +02:00
Konstantinos Spyrou 720ee5a9a4 fix: savePiwikInfo no longer creates duplicates 2023-01-12 16:38:29 +02:00
Konstantinos Spyrou 4dd0320255 removed script 2023-01-12 14:32:06 +02:00
Konstantinos Spyrou af0482a9fb Merge branch 'develop' of code-repo.d4science.org:MaDgIK/uoa-repository-manager-service into develop 2023-01-12 14:23:57 +02:00
Konstantinos Spyrou 830df7cb52 1. use sub for session instead of email 2. minor refactoring 2023-01-12 14:16:27 +02:00
Konstantinos Spyrou d8eac0ac10 replaced log4j2 with slf4j 2023-01-11 18:50:31 +02:00
Lampros Smyrnaios ceb53248ce Fix merge-commit, missing the improvement introduced in commit: 63c9caaf96 2022-12-21 13:44:58 +02:00
Konstantinos Spyrou 4850581c53 merge commit 2022-12-20 20:53:54 +02:00
Konstantinos Spyrou a5a4924c08 chenged methods retrieving AggregationInfo 2022-12-20 20:40:37 +02:00
Konstantinos Spyrou 6c2780927d changed objectMapper and restTemplate configuration 2022-12-20 20:27:13 +02:00
Lampros Smyrnaios 63c9caaf96 Improve performance of "AggregationServiceImpl.createYearMap()". 2022-12-14 17:12:27 +02:00
Lampros Smyrnaios a40c1b3e44 - Move all the Aggregation-related code from "RepositoryService" into the new "AggregationService".
- Add info about the thrown exceptions.
2022-12-14 17:09:38 +02:00
Lampros Smyrnaios 1a4df2b852 - Add "README.md"
- Code polishing.
2022-12-14 16:33:12 +02:00
Lampros Smyrnaios deae3b8b54 - Add error-checking and improve exceptions handling.
- Code polishing.
2022-12-07 13:37:55 +02:00
Lampros Smyrnaios f720446bdf Remove the obsolete "AggregationDetails" code and rename the related variables (which have already changed types). 2022-12-01 18:37:03 +02:00
Lampros Smyrnaios db88b2eecb Optimize imports. 2022-12-01 15:19:29 +02:00
Lampros Smyrnaios 4a32d0278b - Update the application.yml, which fixes runtime errors.
- Update the installAndRun.sh, to be able to only build the project (if desired to only produce the JAR), and also to handle errors better.
2022-12-01 14:56:52 +02:00
Konstantinos Spyrou b3758da868 replaced AggregationDetails with AggregationInfo class 2022-11-30 20:58:08 +02:00
Konstantinos Spyrou a792fd34c1 removed unnecessary operations 2022-11-30 17:33:09 +02:00
Lampros Smyrnaios 36b17e4ef1 Refactor the "Statistics" code:
- Merge the Aggregators' code inside "StatsServiceImpl.java".
 - Remove the Hystrix related code, since it wasn't really used.
- Add exceptions-handling and error-checking.
2022-11-30 13:03:17 +02:00
Konstantinos Spyrou 01ba340f47 created paging for searchRegisteredRepositories - temporarily inactive 2022-11-24 18:36:37 +02:00
Konstantinos Spyrou b533355c4f set log level to info 2022-11-24 18:33:40 +02:00
Konstantinos Spyrou 33d28cefc1 all registered users can perform validations 2022-11-24 18:33:21 +02:00
Konstantinos Spyrou c09cbbbcc0 created basic .gitignore 2022-11-24 15:58:30 +02:00
Konstantinos Spyrou 6a5c637f51 reformatted file 2022-11-24 15:55:09 +02:00
Lampros Smyrnaios 96d8ba8dde Fix url in application.yml 2022-11-23 18:49:35 +02:00
Lampros Smyrnaios dbe13a7ed1 - Fix memory leak.
- Add code's line-numbers in the logs.
2022-11-18 12:21:56 +02:00
Lampros Smyrnaios 83c77670ab Improve the "installAndRun.sh" script:
- Add the "java -jar" run command.
- Add more sanity checks for cmd-args.
- Code polishing.
2022-11-17 15:01:27 +02:00
Konstantinos Spyrou f41a5c8985 fixed prometheus test 2022-11-16 16:33:42 +02:00
Lampros Smyrnaios 83f9ce36ce Add a script to install and run the app.
(some missing dependencies will be deployed on a public repo soon)
2022-11-16 16:20:58 +02:00
Konstantinos Spyrou e99f334708 1. removed registeredBy from add/update interface controllers
2. changed authorization expression for interface validation
2022-11-10 17:34:23 +00:00
Konstantinos Spyrou cff16a2d7c refactored mails 2022-11-09 16:16:54 +00:00
Konstantinos Spyrou ee01491995 1. fixed auth expression when updating a repository
2. filter out ftp interfaces
2022-11-08 18:37:51 +00:00
Konstantinos Spyrou 1491d8e280 fixed remaining spel expressions 2022-11-03 12:35:05 +00:00
Konstantinos Spyrou 82cf64a3e3 fixed null equality in spel expression 2022-11-03 11:09:12 +00:00
Konstantinos Spyrou 0b322c87d6 fixed auth expression 2022-11-01 12:08:42 +00:00
Konstantinos Spyrou 0f73e95209 filter out only sword and rest 2022-10-10 16:04:06 +00:00
Antonis Lempesis 9ca8aed160 ignoring non aoi interfaces 2022-10-10 11:27:28 +00:00
Konstantinos Spyrou 9633710cec quick fix for Journal registration. Removed ValidationException and replaced with log. Should be revisited. 2022-10-06 10:29:21 +00:00
Konstantinos Spyrou a46e64f5b5 added date in logs 2022-09-07 13:49:23 +00:00
Konstantinos Spyrou 3c41f3206a fixed getting accessSets and accessFormats 2022-09-07 13:29:04 +00:00
Konstantinos Spyrou 0590efba9a CompatibilityClasses fix 2022-09-06 13:55:05 +00:00
Konstantinos Spyrou 2afd3ef412 fixed re3data lastCollectionDate 2022-08-05 13:24:32 +00:00
Konstantinos Spyrou c7b7d37ae3 svn merge -r62311:HEAD https://svn.driver.research-infrastructures.eu/driver/dnet45/modules/uoa-repository-manager-service/branches/new-datasource-model . 2022-08-05 08:48:16 +00:00
Konstantinos Spyrou 2a8294a958 reintroduced typology field 2022-07-22 10:34:26 +00:00
Antonis Lempesis d3f13fc8c2 correctly passing the repo id... 2022-06-02 13:18:04 +00:00
Antonis Lempesis d57782444d avoiding an OutOfBounds exception 2022-06-02 08:42:08 +00:00
Antonis Lempesis a61b259ca3 getting aggregations uising the correct dsm api method 2022-05-24 14:01:08 +00:00
Konstantinos Spyrou 94a8b1abf9 update lastConsentTermsOfUseDate instead of consentTermsOfUseDate 2022-05-24 09:15:52 +00:00
Konstantinos Spyrou 03dad9cb1e added lastConsentTermsOfUseDate 2022-05-24 08:28:39 +00:00
Konstantinos Spyrou bd31f55685 cris repository registration functionality 2022-04-20 10:26:28 +00:00
Konstantinos Spyrou 982d299b2d repository terms functionality 2022-03-15 11:33:49 +00:00
Antonis Lempesis 2a57236c9a removed basic hhtp authentication 2021-11-05 08:54:20 +00:00
Antonis Lempesis 9d38d86c03 merged properties branch 2021-10-26 11:46:08 +00:00
Antonis Lempesis b5f1c113c6 merged branch springboot - 61629:HEAD 2021-10-22 11:32:44 +00:00
Konstantinos Spyrou 746f3bbfbe created method to retrieve total registered datasources and added an entry in prometheus metrics 2021-10-12 13:30:38 +00:00
Konstantinos Spyrou 03e0b8f472 changed prometheus metrics names 2021-10-12 10:16:37 +00:00
Konstantinos Spyrou 1b6ca905da replaced hasAnyRole with hasAnyAuthority 2021-07-28 12:28:58 +00:00
Konstantinos Spyrou 4d7f3d7a64 fixed dependency version 2021-07-26 11:56:28 +00:00
Konstantinos Spyrou 500c379b8c created prometheus reports and /metrics 2021-07-26 11:34:42 +00:00
Konstantinos Spyrou 84df31c87a enabled emails 2021-07-22 13:52:50 +00:00
Antonis Lempesis 9e967cab33 using md5 instead of base64 encoding for new aggregator ids 2021-07-22 10:55:27 +00:00
Antonis Lempesis e603487103 merged branch aai_roles_new to trunk 2021-07-21 11:51:18 +00:00
Antonis Lempesis 25ff488405 returning fair guidelines as such 2021-04-16 08:16:09 +00:00
Antonis Lempesis a8143fe775 code cleanup 2021-04-05 10:35:28 +00:00
Antonis Lempesis fca5ada4c0 removed unnecessary dep to spring boot 2021-04-02 16:01:25 +00:00
Antonis Lempesis b19f53bf80 deleting angular app... 2021-03-30 12:24:40 +00:00
636 changed files with 7431 additions and 23473 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
# Ignore directories
.idea
target

View File

@ -1,9 +1,10 @@
FROM tomcat:7.0.90-jre8
#FROM tomcat:7.0.90-jre8
FROM tomcat:8.5.71-jre8-openjdk-bullseye
MAINTAINER "pkanakakis@di.uoa.gr"
RUN ["rm", "-fr", "/usr/local/tomcat/webapps/ROOT"]
COPY ./target/uoa-repository-manager-service.war /usr/local/tomcat/webapps/uoa-repository-manager-service.war
#COPY src/main/resources/dnet-override-new.properties /usr/local/tomcat/lib/dnet-override.properties
COPY src/main/resources/application.properties /usr/local/tomcat/lib/dnet-override.properties
COPY validator.env /usr/local/tomcat/lib/dnet-override.properties
#COPY src/main/resources/application.properties /usr/local/tomcat/lib/application.properties
#COPY src/main/resources/email-texts.properties /usr/local/tomcat/lib/email-texts.properties
VOLUME /var/log/uoa-repository-manager-service:/var/log/uoa-repository-manager-service/

176
LICENSE.txt Normal file
View File

@ -0,0 +1,176 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

218
README.md Normal file
View File

@ -0,0 +1,218 @@
# OpenAIRE Provide - backend service
## Introduction
OpenAIRE PROVIDE is the content gateway service of OpenAIRE, where data providers are invited to connect scholarly content with OpenAIRE.<br>
OpenAIRE PROVIDE allows repositories, data archives, journals, aggregators, CRIS systems, to enter the OpenAIRE and European Open Science (EOSC) ecosystem and be accessible by millions of researchers, research institutes and networks, research funders, policy makers and citizens.<br>
OpenAIRE PROVIDE lowers any technological barriers, by supporting a series of integrations, therefore, enabling its users to visually access OpenAIRE's services that are responsible for the data harvesting process.
<br><br>
There are four distinctive steps from the initial express of interest for OpenAIRE PROVIDE to the actual content availability on OpenAIRE and EOSC.
<br>
The steps indicate the important subservices of OpenAIRE that perform the following functionalities:
- Validation of data sources with the OpenAIRE guidelines (via the OpenAIRE Validator)
- Registration of data sources to OpenAIRE and global interlinked networks provides links to content for text and data mining, view history of validations, status of harvesting Enrichment of metadata information that describes the data sources to be available through OpenAIRE.
- Subscribe and view/receive notifications to enrich the metadata or the content of the data source (via the OpenAIRE Broker)
- View usage statistics of Open research impact by subscribing to the OpenAIRE UsageCounts service; view aggregated, cleaned usage stats for repository access and broaden your mechanisms for impact assessment.
<br>
## Architecture
[...]
<br>
## Building
The backend is a [Maven](https://maven.apache.org/index.html) project. It has been tested using Java 8.
<br>
#### Manual Build Instructions
###### Requirements:
- Java 8
- Apache Maven 3+
###### Build Instructions:
1. Clone the repository and move inside the directory
<br> `git clone https://code-repo.d4science.org/MaDgIK/uoa-repository-manager-service.git && cd uoa-repository-manager-service`
2. Provide all not-set or redacted configurations, inside the **src/main/resources/application.yml** file.
3. Build Maven project
<br> `mvn clean package`
<br> Produces the file "./target/**uoa-repository-manager-service.jar**" which can be run with: - Run the app with: `java -jar ./target/uoa-repository-manager-service.jar`
#### Build using Docker
The repository contains a **Dockerfile** which can be used to build an image containing the compiled project.
###### Requirements:
- Docker
###### Build Instructions:
1. Clone the repository and move inside the directory
<br> `git clone https://code-repo.d4science.org/MaDgIK/uoa-repository-manager-service.git && cd uoa-repository-manager-service`
2. Provide all not-set or redacted configurations, inside the **src/main/resources/application.yml** file.
3. Build Docker image
<br> `docker build . -t <docker-image-name>`
<br>
## Configuration
The configuration can be set inside the **src/main/resources/application.yml** file.
### Server-related configuration.
```
server:
port: 8480
servlet:
context-path: /uoa-repository-manager-service
```
### Swagger UI - related configuration
```
springdoc:
swagger-ui:
disable-swagger-default-url: true
path: /swagger-ui.html
displayRequestDuration: true
api-docs:
path: /api-docs
```
### Datasource -related configuration
```
spring:
jpa:
hibernate:
ddl-auto: update
datasource:
url: ${services.provide.db.url}
username: ${services.provide.db.username}
password: ${services.provide.db.password}
driverClassName: ${services.provide.db.driverClassName}
```
### Configurations about the provide service.
```
services:
openaireServicesBaseUrl: https://beta.services.openaire.eu
provide:
dev-machine: 88.197.53.71 # VM-71
```
### Configuration about Authentication and authorization infrastructure (AAI).
```
aai:
baseURL: https://aai.openaire.eu
oidc:
domain: .openaire.eu # use empty value for local, otherwise: ".openaire.eu"
id: XX
issuer: ${services.provide.aai.baseURL}/oidc/
redirectURL: http://localhost:${server.port}${server.servlet.context-path}/openid_connect_login
secret: XX
webURL: http://localhost:4200/join
registry:
coid: XX
username: XX
password: XX
production: false
url: ${services.provide.aai.baseURL}/registry/
```
### Broker's configuration
```
broker:
api: api/
openaire: openaireBroker
port: 443
url: https://beta.broker.openaire.eu
```
### Client's configuration
```
clients:
dsm: ${services.provide.baseUrl}
search: ${services.openaireServicesBaseUrl}/search/v2/api
usageEvents: http://beta.lbs.openaire.eu:8080/ajax/summary
usagestats: ${services.openaireServicesBaseUrl}/usagestats
```
### Database configuration for Provide.
```
db:
driverClassName: org.postgresql.Driver
password: dnetPwd
url: jdbc:postgresql://${services.provide.dev-machine}:5432/repomanager
username: dnet
```
### Configuration for the IS LookUp service.
```
iSLookUpService:
url: https://dev-openaire.d4science.org:443/is/services/isLookUp
```
### Mail-server configuration.
```
mail:
authenticate: true
debug: false
from: XX
host: smtp.gmail.com
mode: ssl
password: XX
port: 465
replyTo: XX
username: XX
```
### Redis's configuration
```
redis:
host: vereniki.athenarc.gr
password: XX
port: 6379
```
### Usage statistics
```
usageStatisticsDiagramsBaseURL: https://beta.openaire.eu/stats3/
usageStatisticsNumbersBaseURL: ${services.openaireServicesBaseUrl}/usagestats/datasources/
usagestats:
adminEmail: XX
sushiliteEndpoint: ${services.openaireServicesBaseUrl}/usagestats/sushilite/
sushiliteR5Endpoint: ${services.openaireServicesBaseUrl}/usagestats_r5/sushilite/r5/
```
### Validator - related configuration.
```
validator:
results:
url: https://beta.provide.openaire.eu/compatibility/browseHistory/
validatorService:
url: http://${services.provide.dev-machine}:8080/uoa-validator-service/services/validatorWebService
```
### Miscellaneous
```
topic_types:
url: ${services.openaireServicesBaseUrl}/provision/mvc/vocabularies/dnet:topic_types.json
adminEmail: XX
analyticsURL: https://analytics.openaire.eu/addsite.php?
baseUrl: ${services.openaireServicesBaseUrl}/openaire
```
<br>
## Deployment
### Prerequisites
* install [PostgreSQL 9.5+](https://www.postgresql.org/)
* Run the app with: `java -jar ./target/uoa-repository-manager-service.jar --spring.config.location=application.yml`
<br>
## misc
### Notes for Swagger-UI:
- Access it through this url: http://localhost:8480/uoa-repository-manager-service/swagger-ui/index.html
- In order to request data from most endpoints, you have to be a "REGISTERED_USER", otherwise you will get a 403 error code.
- In order to be a registered user, you have to run the [UI-service](https://code-repo.d4science.org/MaDgIK/uoa-repository-manager-ui) , in the same machine, at the same time and login through a browser, using the following url: http://localhost:8480/uoa-repository-manager-service/openid_connect_login

View File

@ -1,35 +0,0 @@
/* You can add fonts */
@font-face {
font-family: "CharterITCW05";
src: url("assets/fonts/CharterITCW05-Regular.woff") format("woff");
}
/*@font-face {
font-family: "HelveticaNeue";
src: url("assets/fonts/HelveticaNeue-Regular.ttf") format("truetype");
}
@font-face {
font-family: "PragmaticaMedium";
src: url("assets/fonts/PragmaticaMedium.otf") format("opentype");
}*/
:host ::ng-deep {
h1, h2, h3, h4, h5, h6 {
font-family: "CharterITCW05";
}
/*label, .p-button, th {
font-family: "PragmaticaMedium";
font-weight: 100;
}*/
/** {
:not(h1, h2, h3, h4, h5, h6, label, .p-button, th, .pi) {
font-family: "HelveticaNeue";
font-weight: 600;
}
}*/
}

File diff suppressed because it is too large Load Diff

View File

@ -1,32 +0,0 @@
/* You can add media styles */
@media (max-width: 991px) {
//header
.header {
padding: 10px;
}
//main-content
.main-content {
padding: 10px;
}
}
@media (max-width: 961px) {
.hidden-left-panel {
display: none !important;
}
}
@media (min-width: 961px) {
.hidden-horizontal-menu {
display: none !important;
}
}

View File

@ -1,65 +0,0 @@
/* You can add reusable colors */
// Primary
$dark-orange-1: #933401;
$dark-orange-2: #571F01;
$orange: #D04A02;
$light-orange-1: #FD6412;
$light-orange-2: #FEB791;
$dark-tangerine-1: #AE6800;
$dark-tangerine-2: #714300;
$tangerine: #EB8C00;
$light-tangerine-1: #FFA929;
$light-tangerine-2: #FFDCA9;
$dark-yellow-1: #C28A00;
$dark-yellow-2: #855F00;
$yellow: #FFB600;
$light-yellow-1: #FFC83D;
$light-yellow-2: #FFECBD;
$dark-rose-1: #A43E50;
$dark-rose-2: #6E2A35;
$rose: #DB536A;
$light-rose-1: #E27588;
$light-rose-2: #F1BAC3;
$dark-red-1: #AA2417;
$dark-red-2: #741910;
$red: #E0301E;
$light-red-1: #E86153;
$Light-red-2: #F7C8C4;
$white: white;
$black: black;
$dark-grey: #2D2D2D;
$medium-grey: #464646;
$grey: #7D7D7D;
$light-grey: #DEDEDE;
// Secondary
$dark-purple-1: #6A1CE2;
$dark-purple-2: #4B06B2;
$purple: #9013FE;
$light-purple-1: #B15AFE;
$light-purple-2: #DEB8FF;
$dark-blue-1: #0060D7;
$dark-blue-2: #003DAB;
$blue: #0089EB;
$light-blue-1: #4DACF1;
$light-blue-2: #B3DCF9;
$dark-green-1: #2C8646;
$dark-green-2: #175C2C;
$green: #4EB523;
$light-green-1: #86DB4F;
$light-green-2: #C4FC9F;
$rose-for-white-type: #D93954;
// Status Colors
$status-red: #E0301E;
$status-yellow: #FFB600;
$status-green: #175C2C;

View File

@ -1,10 +0,0 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
const routes: Routes = [];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }

View File

@ -1,9 +0,0 @@
<p-toast preventOpenDuplicates="true"></p-toast>
<div class="container">
<div class="p-grid p-nogutter">
<div class="p-col-12">
<app-header></app-header>
<app-main-page></app-main-page>
</div>
</div>
</div>

View File

@ -1 +0,0 @@
@import 'src/styles';

View File

@ -1,35 +0,0 @@
import { TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [
RouterTestingModule
],
declarations: [
AppComponent
],
}).compileComponents();
});
it('should create the app', () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app).toBeTruthy();
});
it(`should have as title 'RTA-Admin-UI'`, () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app.title).toEqual('RTA-Admin-UI');
});
it('should render title', () => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.nativeElement;
expect(compiled.querySelector('.content span').textContent).toContain('RTA-Admin-UI app is running!');
});
});

View File

@ -1,19 +0,0 @@
import { AuthService } from './shared/services/auth.service';
import { Component, OnInit } from '@angular/core';
import { PrimeNGConfig } from 'primeng/api';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
constructor(private primengConfig: PrimeNGConfig, private authService: AuthService) { }
ngOnInit(): void {
this.primengConfig.ripple = true;
this.authService.initializeOAuth2Login();
}
}

View File

@ -1,61 +0,0 @@
import { ToastModule } from 'primeng/toast';
import { OAuthModule, OAuthStorage } from 'angular-oauth2-oidc';
import { environment } from './../environments/environment';
import { HttpClientModule, HttpClient } from '@angular/common/http';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { CoreModule } from './core/core.module';
import { FeaturesModule } from './features/features.module';
import { SharedModule } from './shared/shared.module';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
import { ConfirmationService, MessageService } from 'primeng/api';
import { SearchListStateService } from './shared/back-button/search-list-state.service';
export function HttpLoaderFactory(http: HttpClient): TranslateHttpLoader {
return new TranslateHttpLoader(http);
}
// Settings localStorage as OAuth2 storage.
// We need a factory, since localStorage is not available during AOT build time.
export function storageFactory(): OAuthStorage {
return localStorage;
}
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
BrowserAnimationsModule,
AppRoutingModule,
CoreModule,
FeaturesModule,
SharedModule,
HttpClientModule,
ToastModule,
OAuthModule.forRoot({
resourceServer: {
allowedUrls: [environment.baseApiUrl],
sendAccessToken: true
}
}),
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: HttpLoaderFactory,
deps: [HttpClient]
},
defaultLanguage: environment.defaultLanguage,
}),
],
providers: [ConfirmationService, { provide: OAuthStorage, useFactory: storageFactory }, MessageService, SearchListStateService],
bootstrap: [AppComponent]
})
export class AppModule { }

View File

@ -1,25 +0,0 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { SharedModule } from 'primeng/api';
import { PrimengSharedModule } from './../shared/primeng-shared/primeng-shared.module';
import { ErrorHandlingModule } from './error-handling/error-handling.module';
import { HeaderComponent } from './header/header.component';
import { MainPageComponent } from './main-page/main-page.component';
@NgModule({
declarations: [HeaderComponent, MainPageComponent],
imports: [
CommonModule,
FormsModule,
PrimengSharedModule,
SharedModule,
ErrorHandlingModule
],
exports: [
HeaderComponent,
MainPageComponent
]
})
export class CoreModule { }

View File

@ -1,15 +0,0 @@
import { HttpErrorInterceptor } from './http-error.interceptor';
import { GlobalErrorHandler } from './global-error-handler';
import { NgModule, ErrorHandler } from '@angular/core';
import { CommonModule } from '@angular/common';
import { HTTP_INTERCEPTORS } from '@angular/common/http';
@NgModule({
declarations: [],
imports: [CommonModule],
providers: [
{ provide: ErrorHandler, useClass: GlobalErrorHandler },
{ provide: HTTP_INTERCEPTORS, useClass: HttpErrorInterceptor, multi: true }
]
})
export class ErrorHandlingModule { }

View File

@ -1,21 +0,0 @@
import { environment } from 'src/environments/environment';
import { HttpErrorResponse } from '@angular/common/http';
import { ErrorHandlingService } from './../../shared/services/error-handling/error-handling.service';
import { ErrorHandler, Injectable, NgZone } from '@angular/core';
@Injectable()
export class GlobalErrorHandler implements ErrorHandler {
constructor(private errorHandlingService: ErrorHandlingService, private zone: NgZone) {}
handleError(error: Error): void {
this.zone.run(() => {
if (!(error instanceof HttpErrorResponse) && environment.enableGlobalErrorToast){
this.errorHandlingService.showErrorMessage(error.message);
}
});
console.error('Error from global error handler', error);
}
}

View File

@ -1,34 +0,0 @@
import { ErrorHandlingService } from './../../shared/services/error-handling/error-handling.service';
import {
HttpHandler,
HttpRequest,
HttpEvent,
HttpErrorResponse,
HttpInterceptor
} from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { Injectable, Injector } from '@angular/core';
@Injectable()
export class HttpErrorInterceptor implements HttpInterceptor {
constructor(
private injector: Injector,
) {}
intercept(
request: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
return next.handle(request).pipe(
catchError((error: HttpErrorResponse) => {
// We don't inject an HttpClient dependent service directly to an http interceptor's constructor,
// or we'll get cyclic dependency errors
const errorHandlingService = this.injector.get(ErrorHandlingService);
errorHandlingService.showHttpResponseError(error);
return throwError(error);
})
) as Observable<HttpEvent<any>>;
}
}

View File

@ -1,14 +0,0 @@
<div class="p-grid p-nogutter p-ai-center vertical-container header">
<div class="p-col-4">
<div class="p-grid p-nogutter p-jc-start">
<a routerLink=''>
<img src="assets/images/pwc-logo.svg" height="50">
</a>
</div>
</div>
<div class="p-col-8">
<div class="p-grid p-nogutter p-jc-end">
<p-selectButton class="p-ml-auto" [options]="languages" [(ngModel)]="selectedLang" (ngModelChange)="changeLanguage($event)" optionLabel="name" optionValue="code"></p-selectButton>
</div>
</div>
</div>

View File

@ -1 +0,0 @@
@import 'src/styles';

View File

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

View File

@ -1,28 +0,0 @@
import { Language } from './../../shared/models/language.interface';
import { environment } from './../../../environments/environment';
import { AuthService } from './../../shared/services/auth.service';
import { Component, OnInit } from '@angular/core';
import { MenuItem } from 'primeng/api';
import { TranslateService } from '@ngx-translate/core';
import {Router} from "@angular/router";
@Component({
selector: 'app-header',
templateUrl: './header.component.html',
styleUrls: ['./header.component.scss']
})
export class HeaderComponent implements OnInit {
languages: Language[] = environment.languages;
selectedLang = environment.defaultLanguage;
constructor(private auth: AuthService, private translateService: TranslateService, private router: Router) {
}
ngOnInit(): void {
}
changeLanguage($event): void {
this.translateService.use($event);
}
}

View File

@ -1 +0,0 @@
<router-outlet></router-outlet>

View File

@ -1 +0,0 @@
@import 'src/styles';

View File

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

View File

@ -1,22 +0,0 @@
import { Router } from '@angular/router';
import { AuthService } from 'src/app/shared/services/auth.service';
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-main-page',
templateUrl: './main-page.component.html',
styleUrls: ['./main-page.component.scss']
})
export class MainPageComponent implements OnInit {
constructor(private auth: AuthService, private router: Router) { }
ngOnInit(): void {
this.auth.authStatusChanged.subscribe((isAuthenticated) => {
if (!isAuthenticated) {
this.router.navigateByUrl('login');
}
});
}
}

View File

@ -1,26 +0,0 @@
<div class="p-grid p-nogutter">
<app-left-panel class="hidden-left-panel"></app-left-panel>
<div class="p-col main-content">
<h1>{{'ADMINISTRATION' | translate}}</h1>
<div class="p-mb-5 hidden-horizontal-menu">
<app-horizontal-menu></app-horizontal-menu>
</div>
<ng-template ngFor let-section [ngForOf]="adminSections">
<h3 class="sub-title" id="{{section.title}}">{{section.title}}</h3>
<div class="sub-content">
<div class="p-grid">
<ng-template ngFor let-subSection [ngForOf]="section.subSections">
<div class="p-col-12 p-sm-6 p-xl-3">
<app-card [data]="subSection"></app-card>
</div>
</ng-template>
</div>
</div>
</ng-template>
</div>
</div>

View File

@ -1 +0,0 @@
@import 'src/styles';

View File

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

View File

@ -1,103 +0,0 @@
import { AuthService } from './../../shared/services/auth.service';
import { Component, OnInit } from '@angular/core';
import { CardItem } from './../../shared/models/card-item.interface';
import { AdministrationSection } from './../../shared/models/administration-section.interface';
@Component({
selector: 'app-administration',
templateUrl: './administration.component.html',
styleUrls: ['./administration.component.scss']
})
export class AdministrationComponent implements OnInit {
data: CardItem;
adminSections: AdministrationSection[];
constructor(public authService: AuthService) { }
ngOnInit(): void {
this.initializeData();
}
initializeData() {
this.adminSections = [
{
title: 'Monitoring',
subSections: [
{
title: 'Downloads Monitoring',
subtitle: 'View File\'s Downloading Process and Manage...',
headerImage: 'pi-image',
footerImage: 'pi-desktop',
path: '/pages/administration/downloads-monitoring'
}
]
},
{
title: 'Configurations',
subSections: [
{
title: 'Categories Managenent',
subtitle: 'View File\'s Downloading Process and Manage...',
headerImage: 'pi-image',
footerImage: 'pi-cog',
path: '/pages/administration/categories-management'
},
{
title: 'Templates Managenent',
subtitle: 'View File\'s Downloading Process and Manage...',
headerImage: 'pi-image',
footerImage: 'pi-cog',
path: '/pages/administration/templates-management'
},
{
title: 'Verification Managenent',
subtitle: 'View File\'s Downloading Process and Manage...',
headerImage: 'pi-image',
footerImage: 'pi-cog',
path: '/pages/administration/verification-management'
},
{
title: 'Configurator',
subtitle: 'View File\'s Downloading Process and Manage...',
headerImage: 'pi-image',
footerImage: 'pi-cog',
path: '/pages/administration/configurator'
}
]
},
{
title: 'Access',
subSections: [
{
title: 'Users Managenent',
subtitle: 'View File\'s Downloading Process and Manage...',
headerImage: 'pi-image',
footerImage: 'pi-key',
path: '/pages/administration/users-management'
},
{
title: 'Roles Managenent',
subtitle: 'View File\'s Downloading Process and Manage...',
headerImage: 'pi-image',
footerImage: 'pi-key',
path: '/pages/administration/roles-management'
}
]
},
{
title: 'Exception Handling',
subSections: [
{
title: 'Application Level Exceptions',
subtitle: 'View Application Level exceptions thrown by the Platform',
headerImage: 'pi-image',
footerImage: 'pi-exclamation-triangle',
path: '/pages/administration/application-level-exceptions'
}
]
}
];
}
}

View File

@ -1,18 +0,0 @@
<p-dialog [header]="header"
[(visible)]="displayDialog "
[closable]="false"
[draggable]="false"
[modal]="true"
appendTo="body">
<ng-template pTemplate="content">
<pre>
{{this.trace}}
</pre>
</ng-template>
<ng-template pTemplate="footer">
<p-button (onClick)="cancel()"
label="Close"
styleClass="p-mt-3 p-button-warning">
</p-button>
</ng-template>
</p-dialog>

View File

@ -1,13 +0,0 @@
@import 'src/styles';
pre {
width: 900px !important;
}
@media (max-width: 991px) {
pre {
width: 420px !important;
}
}

View File

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

View File

@ -1,24 +0,0 @@
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { ApplicationLevelExceptionService } from 'src/app/shared/services/administration/application-level-exception.service';
@Component({
selector: 'app-application-level-dialog',
templateUrl: './application-level-dialog.component.html',
styleUrls: ['./application-level-dialog.component.scss']
})
export class ApplicationLevelDialogComponent implements OnInit {
@Input() displayDialog: boolean;
@Input() header: string;
@Input() trace: string;
@Output() cancelled = new EventEmitter<boolean>();
constructor() { }
ngOnInit(): void {
}
cancel() {
this.cancelled.emit(true);
}
}

View File

@ -1,29 +0,0 @@
<div class="p-grid p-nogutter">
<div class="p-col main-content">
<h1>{{'APPLICATION-LEVEL-EXCEPTIONS' | translate}}</h1>
<app-horizontal-menu></app-horizontal-menu>
<app-breadcrumb></app-breadcrumb>
<div class="p-grid p-nogutter p-pt-5 p-pb-5">
<div class="p-col p-nogutter p-col-12 p-mt-5">
<app-application-level-search-form [paginationEventRequest]="paginationEventRequest"
(searchResults)="searchMade($event)"
(loading)="passloading($event)">
</app-application-level-search-form>
</div>
<div class="p-col p-nogutter p-col-12 p-mt-5">
<app-application-level-table [searchResultsPage]="searchResults"
(paginationEvent)="paginationEvent($event)"
[loading]="loading">
</app-application-level-table>
</div>
</div>
</div>
</div>

View File

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

View File

@ -1,33 +0,0 @@
import { Component, OnInit } from '@angular/core';
import { Page } from 'src/app/shared/models/paging/page.interface';
import { SystemException } from 'src/app/shared/models/system-exception.interface';
@Component({
selector: 'app-application-level-exceptions',
templateUrl: './application-level-exceptions.component.html',
styleUrls: ['./application-level-exceptions.component.scss']
})
export class ApplicationLevelExceptionsComponent implements OnInit {
searchResults: Page<SystemException> = null;
paginationEventRequest: { page: number, offset: number } ;
loading: boolean;
constructor() { }
ngOnInit(): void {
}
searchMade(results) {
this.searchResults = results;
}
passloading(loading) {
this.loading = loading;
}
paginationEvent(paginationEvent) {
this.paginationEventRequest = paginationEvent;
}
}

View File

@ -1,14 +0,0 @@
<p-fieldset legend="Search Filters"
[toggleable]="true">
<app-application-level-form></app-application-level-form>
<div class="p-formgroup-inline p-jc-center p-mt-3">
<p-button (onClick)="clear()" label="Clear" styleClass="p-mr-2 p-button-warning"
[disabled]="!canPreviewLevelExceptions()">
</p-button>
<p-button (onClick)="search()"
label="Search"
styleClass="p-button-primary"
[disabled]="!canPreviewLevelExceptions()">
</p-button>
</div>
</p-fieldset>

View File

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

View File

@ -1,59 +0,0 @@
import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
import { USER_RIGHTS } from 'src/app/shared/enums/USER_RIGHTS.enum';
import { Page } from 'src/app/shared/models/paging/page.interface';
import { SystemException } from 'src/app/shared/models/system-exception.interface';
import { AuthService } from 'src/app/shared/services/auth.service';
import { ApplicationLevelExceptionService } from 'src/app/shared/services/administration/application-level-exception.service';
import { ErrorHandlingService } from 'src/app/shared/services/error-handling/error-handling.service';
import { environment } from 'src/environments/environment';
import { ApplicationLevelFormComponent } from '../../forms/application-level-form/application-level-form.component';
@Component({
selector: 'app-application-level-search-form',
templateUrl: './application-level-search-form.component.html',
styleUrls: ['./application-level-search-form.component.scss']
})
export class ApplicationLevelSearchFormComponent implements OnInit {
@Input() set paginationEventRequest(value: { page: number, offset: number }) {
// This setter may be called on page setup without arguments.
if (!value) {
return;
}
// this._value = Object.assign(value);
this.search(value.page, value.offset);
}
@Output() searchResults = new EventEmitter<Page<SystemException>>();
@Output() loading = new EventEmitter<boolean>();
@ViewChild(ApplicationLevelFormComponent)
private searchCriteriaForm: ApplicationLevelFormComponent;
_value : {page: number , offset: number};
constructor(private applicationLevelService: ApplicationLevelExceptionService,private errorHandlingService: ErrorHandlingService, private auth: AuthService) { }
ngOnInit(): void {
}
search(pageNumber = 0, pageSize = 10) {
this.loading.emit(true);
this.applicationLevelService.searchByCriteriaPaged(pageNumber, pageSize, this.searchCriteriaForm?.formValue(), 'search').subscribe((results: Page<SystemException>) => {
this.searchResults.emit(results);
this.loading.emit(false);
},
err => {
this.errorHandlingService.showHttpResponseError(err)
this.loading.emit(false);
});
}
canPreviewLevelExceptions(): boolean {
return this.auth.userHasRightForClient(USER_RIGHTS.K01, environment.globalRightsClientID);
}
clear() {
this.searchCriteriaForm.resetForm();
}
}

View File

@ -1,54 +0,0 @@
<p-fieldset legend="List of Exceptions"
[toggleable]="_searchResultsPage?.data !== null"
[collapsed]="_searchResultsPage?.data === null">
<p-table [value]="_searchResultsPage?.data"
[lazy]="true"
(onLazyLoad)="onLazyLoad($event)"
[paginator]="true"
[rows]="rows"
[totalRecords]="totalRecords"
styleClass="p-datatable-gridlines"
[rowHover]="true"
[loading]="loading"
[autoLayout]="true"
[lazyLoadOnInit]="false">
<ng-template pTemplate="header">
<tr>
<th>Date-Time</th>
<th>Process Id</th>
<th>Job Name</th>
<th>File Code</th>
<th>iPower Client Code</th>
<th>Message</th>
<th>Cause</th>
<th class="one-button">Trace</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-systemException>
<tr>
<td>{{systemException.creationDatetime|date: "dd/MM/yyyy HH:mm"}}</td>
<td>{{systemException.processId}}</td>
<td>{{systemException.jobName}}</td>
<td>{{systemException.dmsFileCode}}</td>
<td>{{systemException.clientId}}</td>
<td>{{systemException.message}}</td>
<td>{{systemException.cause}}</td>
<!-- Edit & Delete icons -->
<td>
<button pButton
(click)="edit(systemException.id)"
icon="pi pi-align-justify"
pTooltip="{{'TRACE' | translate}}"
class="p-button-secondary p-button-rounded p-button-outlined">
</button>
</td>
</tr>
</ng-template>
</p-table>
</p-fieldset>
<app-application-level-dialog [displayDialog]="displayTraceDialog"
[header]="header"
(cancelled)="cancelEditLevelException()"
[trace]="trace">
</app-application-level-dialog>

View File

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

View File

@ -1,64 +0,0 @@
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { LazyLoadEvent } from 'primeng/api';
import { Page } from 'src/app/shared/models/paging/page.interface';
import { SystemException } from 'src/app/shared/models/system-exception.interface';
import { ApplicationLevelExceptionService } from 'src/app/shared/services/administration/application-level-exception.service';
import { ErrorHandlingService } from 'src/app/shared/services/error-handling/error-handling.service';
@Component({
selector: 'app-application-level-table',
templateUrl: './application-level-table.component.html',
styleUrls: ['./application-level-table.component.scss']
})
export class ApplicationLevelTableComponent implements OnInit {
@Output() paginationEvent = new EventEmitter<{ page: number, offset: number }>(); // TODO: Use interface.
@Input() loading: boolean;
@Input() set searchResultsPage(results: Page<SystemException>) {
// This setter may be called on page setup without arguments.
if (!results) {
return;
}
this._searchResultsPage = results;
this.totalRecords = results.totalElements;
// this.loading = false;
}
_searchResultsPage: Page<SystemException>;
totalRecords: number;
rows: number = 10;
header: string;
trace: string;
displayTraceDialog: boolean;
constructor(private errorHandlingService: ErrorHandlingService, private applicationLevelService: ApplicationLevelExceptionService) { }
ngOnInit(): void {
}
onLazyLoad(event: LazyLoadEvent) {
let pageToRequest = Math.floor(event.first / event.rows);
let pageSize = event.rows;
this.paginationEvent.emit({ page: pageToRequest, offset: pageSize });
}
edit(id: number) {
this.header = "Trace";
if(id){
this.applicationLevelService.getById(id).subscribe(result => {
this.trace = result.trace;
this.displayTraceDialog = true;
},
err => {
this.errorHandlingService.showHttpResponseError(err)
});
}
}
cancelEditLevelException() {
this.displayTraceDialog = false;
this.trace = null;
}
}

View File

@ -1,53 +0,0 @@
<div class="p-grid p-nogutter">
<div class="p-col main-content">
<h1>{{'CATEGORIES-MANAGEMENT' | translate}}</h1>
<app-horizontal-menu></app-horizontal-menu>
<app-breadcrumb></app-breadcrumb>
<div class="p-grid p-nogutter p-pt-5 p-pb-5">
<div class="p-col p-nogutter p-col-12">
<div class="p-grid p-nogutter p-jc-end">
<button pButton
pRipple
type="button"
label="{{'ADD-NEW-CATEGORY' | translate}}"
icon="pi pi-plus-circle"
class="p-button-primary"
[disabled]="!canAddNewCategory()"
(click)="addCategory()">
</button>
<app-create-category-dialog [displayDialog]="displayCategoryCreationDialog"
[header]="header"
(cancelled)="displayCategoryCreationDialog = false"
(valueChange)="passEvent($event)">
</app-create-category-dialog>
</div>
</div>
<div class="p-col p-nogutter p-col-12 p-mt-5">
<app-categories-search [paginationEventRequest]="paginationEventRequest"
(searchResults)="searchMade($event)"
(loading)="passLoadingForTable($event)"
[passChange]="passChange"
(loading)="passloading($event)"
[valueTableChange]="valueOfTableChanged">
</app-categories-search>
</div>
<div class="p-col p-nogutter p-col-12 p-mt-5">
<app-categories-search-results [searchResultsPage]="searchResults"
[loading]="loading"
(paginationEvent)="paginationEvent($event)"
(valueChange)="passEvent($event)"
[loading]="loading">
</app-categories-search-results>
</div>
</div>
</div>
</div>

View File

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

View File

@ -1,59 +0,0 @@
import { environment } from './../../../../environments/environment.prod';
import { USER_RIGHTS } from 'src/app/shared/enums/USER_RIGHTS.enum';
import { AuthService } from './../../../shared/services/auth.service';
import { Category } from 'src/app/shared/models/category.interface';
import { Component, OnInit } from '@angular/core';
import { Page } from '../../../shared/models/paging/page.interface';
import { TranslateService } from '@ngx-translate/core';
@Component({
selector: 'app-categories-management',
templateUrl: './categories-management.component.html',
styleUrls: ['./categories-management.component.scss']
})
export class CategoriesManagementComponent implements OnInit {
displayCategoryCreationDialog: boolean = false;
searchResults: Page<Category> = null;
paginationEventRequest: { page: number, offset: number } = { page: 10, offset: 0 };
header: string;
passChange: boolean;
valueOfTableChanged: any;
loading: boolean;
constructor(private authService: AuthService, private translate: TranslateService) { }
ngOnInit(): void {
}
searchMade(results) {
this.searchResults = results;
}
passLoadingForTable(loading) {
this.loading = loading;
}
paginationEvent(paginationEvent) {
this.paginationEventRequest = paginationEvent;
}
canAddNewCategory(): boolean {
return this.authService.userHasRightForClient(USER_RIGHTS.H02, environment.globalRightsClientID);
}
addCategory(): void {
this.header = this.translate.instant('ADD-NEW-CATEGORY');
this.displayCategoryCreationDialog = true;
}
passEvent(passEvent: any) {
if (passEvent) {
this.valueOfTableChanged = passEvent;
}
}
passloading(loading) {
this.loading = loading;
}
}

View File

@ -1,64 +0,0 @@
<p-fieldset legend="Results"
[toggleable]="_searchResultsPage?.data !== null"
[collapsed]="_searchResultsPage?.data === null">
<p-confirmDialog message="{{'DELETE-MESSAGE-CATEGORY' | translate}}"
header="{{'DELETE-CONFIRMATION' | translate}}"
acceptLabel="{{'ACCEPT_LABEL' | translate}}"
rejectLabel="{{'REJECT_LABEL' | translate}}"
acceptButtonStyleClass="p-button-primary"
rejectButtonStyleClass="p-button-warning"
acceptIcon="null"
rejectIcon="null">
</p-confirmDialog>
<p-table [value]="_searchResultsPage?.data"
[lazy]="true"
(onLazyLoad)="onLazyLoad($event)"
[paginator]="true"
[rows]="rows"
[totalRecords]="totalRecords"
styleClass="p-datatable-gridlines"
[rowHover]="true"
[loading]="loading"
[autoLayout]="true">
<ng-template pTemplate="header">
<tr>
<th>Document Classification</th>
<th>Category Name</th>
<th>Category Code</th>
<!-- TODO: Fix hacky way of locking the icon-columns in place -->
<th class="two-buttons">Actions</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-category>
<tr>
<td>{{category.documentClassification.classificationName}}</td>
<td>{{category.categoryName}}</td>
<td>{{category.categoryCode}}</td>
<!-- Edit & Delete icons -->
<td>
<button pButton
[disabled]="!canEditCategories()"
(click)="edit(category)"
icon="pi pi-pencil"
pTooltip="{{'EDIT-CATEGORY' | translate}}"
class="p-button-secondary p-button-rounded p-button-outlined">
</button>
<button pButton
[disabled]="!canDeleteCategories()"
(click)="delete(category)"
icon="pi pi pi-trash"
pTooltip="{{'DELETE-CATEGORY' | translate}}"
class="p-button-danger p-button-rounded p-button-outlined p-ml-2">
</button>
</td>
</tr>
</ng-template>
</p-table>
</p-fieldset>
<app-create-category-dialog [displayDialog]="displayEditCategoryDialog"
[header]="header"
(cancelled)="cancelEditCategory()"
[categoryToEdit]="categoryToEdit"
(valueChange)="passEvent($event)">
</app-create-category-dialog>

View File

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

View File

@ -1,103 +0,0 @@
import { AuthService } from './../../../../shared/services/auth.service';
import { environment } from './../../../../../environments/environment.prod';
import { ErrorHandlingService } from './../../../../shared/services/error-handling/error-handling.service';
import { CategoriesService } from 'src/app/shared/services/administration/categories.service';
import { Category } from './../../../../shared/models/category.interface';
import { Component, Input, OnInit, Output, EventEmitter } from '@angular/core';
import { ConfirmationService, LazyLoadEvent } from 'primeng/api';
import { Page } from '../../../../shared/models/paging/page.interface';
import { USER_RIGHTS } from 'src/app/shared/enums/USER_RIGHTS.enum';
import { TranslateService } from '@ngx-translate/core';
import { NotificationsHandlingService } from 'src/app/shared/services/notifications-handling/notifications-handling.service';
@Component({
selector: 'app-categories-search-results',
templateUrl: './categories-search-results.component.html',
styleUrls: ['./categories-search-results.component.scss']
})
export class CategoriesSearchResultsComponent implements OnInit {
@Output() paginationEvent = new EventEmitter<{ page: number, offset: number }>(); // TODO: Use interface.
@Output() valueChange = new EventEmitter<any>();
@Input() loading: boolean;
@Input() set searchResultsPage(results: Page<Category>) {
// This setter may be called on page setup without arguments.
if (!results) {
return;
}
this._searchResultsPage = results;
this.totalRecords = results.totalElements;
}
_searchResultsPage: Page<Category>;
totalRecords: number;
rows: number = 10;
header: string;
displayEditCategoryDialog: boolean;
categoryToEdit: Category;
constructor(
private categoriesService: CategoriesService,
private errorHandlingService: ErrorHandlingService,
private authService: AuthService,
private translate: TranslateService,
private confirmationService: ConfirmationService,
private notifications: NotificationsHandlingService
) { }
ngOnInit(): void {
}
onLazyLoad(event: LazyLoadEvent) {
let pageToRequest = Math.floor(event.first / event.rows);
let pageSize = event.rows;
this.paginationEvent.emit({ page: pageToRequest, offset: pageSize });
}
edit(category: Category) {
this.header = this.translate.instant('EDIT-CATEGORY');
this.categoryToEdit = category;
this.displayEditCategoryDialog = true;
}
cancelEditCategory() {
this.displayEditCategoryDialog = false;
this.categoryToEdit = null;
}
delete(category: Category): void {
this.confirmationService.confirm({
accept: () => {
this.categoriesService.delete(category.id).subscribe((success) => {
this.valueChange.emit(success);
this.notifications.showDeleteCategorySuccess();
},
err => this.errorHandlingService.showHttpResponseError(err)
);
// Actual logic to perform a confirmation
}, reject: () => {
}
});
}
/*
* UserRights-check Methods
*/
canEditCategories(): boolean {
return this.authService.userHasRightForClient(USER_RIGHTS.H03, environment.globalRightsClientID);
}
canDeleteCategories(): boolean {
return this.authService.userHasRightForClient(USER_RIGHTS.H04, environment.globalRightsClientID);
}
passEvent(passEvent : any) {
if(passEvent){
this.valueChange.emit(passEvent);
}
}
}

View File

@ -1,15 +0,0 @@
<p-fieldset legend="Search Filters"
[toggleable]="true">
<app-category-form [autosuggestInputs]="true"></app-category-form>
<div class="p-formgroup-inline p-jc-center p-mt-3">
<p-button (onClick)="clear()"
label="Clear"
styleClass="p-mr-2 p-button-warning">
</p-button>
<p-button (onClick)="searchButtonClicked()"
label="Search"
styleClass="p-ml-2 p-button-primary"
[disabled]="!canPreviewAllCategories()">
</p-button>
</div>
</p-fieldset>

View File

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

View File

@ -1,101 +0,0 @@
import { environment } from './../../../../../environments/environment.prod';
import { ErrorHandlingService } from './../../../../shared/services/error-handling/error-handling.service';
import { AuthService } from 'src/app/shared/services/auth.service';
import { CategoryFormComponent } from './../../forms/category-form/category-form.component';
import { Category } from './../../../../shared/models/category.interface';
import { DocumentClassificationsService } from './../../../../shared/services/administration/document-classifications.service';
import { Component, OnInit, Output, EventEmitter, Input, ViewChild } from '@angular/core';
import { FormBuilder } from '@angular/forms';
import { CategoriesService } from 'src/app/shared/services/administration/categories.service';
import { DocumentClassification } from 'src/app/shared/models/document-classification.interface';
import { Page } from '../../../../shared/models/paging/page.interface';
import { USER_RIGHTS } from 'src/app/shared/enums/USER_RIGHTS.enum';
@Component({
selector: 'app-categories-search',
templateUrl: './categories-search.component.html',
styleUrls: ['./categories-search.component.scss']
})
export class CategoriesSearchComponent implements OnInit {
@Input() set paginationEventRequest(value: { page: number, offset: number }) {
// This setter may be called on page setup without arguments.
if (!value) {
return;
}
this._value = Object.assign(value);
this.search(value.page, value.offset);
}
@Input() set valueTableChange(results: any) {
if (results) {
this.search(this._value.page, this._value.offset);
}
}
@Output() searchResults = new EventEmitter<Page<Category>>();
@Output() loading = new EventEmitter<boolean>();
@Output() searchInitiated = new EventEmitter<boolean>();
@Input() passChange: any;
@ViewChild(CategoryFormComponent)
private categoryForm: CategoryFormComponent;
_value: { page: number, offset: number };
lastSearchCriteria: { docClassificationId: number | string, categoryName: string, categoryCode: string };
constructor(
private categoriesService: CategoriesService,
private authService: AuthService,
private errorHandlingService: ErrorHandlingService
) { }
ngOnInit(): void {
}
clear() {
this.categoryForm.resetForm();
}
searchButtonClicked() {
// Inform anyone interested there is a new search underway.
this.searchInitiated.emit(true);
// Remember the search for future pagination/whatever requests.
let categoryFormValue = this.categoryForm.formValue();
let docClassId = categoryFormValue.documentClassification ? categoryFormValue.documentClassification.classificationId : null;
let catName = categoryFormValue.categoryName;
let catCode = categoryFormValue.categoryCode;
this.lastSearchCriteria = {
docClassificationId: docClassId ? docClassId : '',
categoryName: catName ? catName : '',
categoryCode: catCode ? catCode : ''
};
// Then actually search.
this.search();
}
search(pageNumber = 0, pageSize = 10) {
this.loading.emit(true);
// A search may be issued by the paginationEventRequest setter upon setting up the page.
if (!this.lastSearchCriteria) {
return;
}
this.categoriesService.searchByCriteriaPaged(pageNumber, pageSize, this.lastSearchCriteria, 'search').subscribe((results: Page<Category>) => {
this.searchResults.emit(results);
this.loading.emit(false);
},err => {
this.loading.emit(false);
this.errorHandlingService.showHttpResponseError(err);
});
}
canPreviewAllCategories(): boolean {
return this.authService.userHasRightForClient(USER_RIGHTS.H01, environment.globalRightsClientID);
}
}

View File

@ -1,33 +0,0 @@
<p-dialog [header]="header"
[(visible)]="displayDialog"
[closable]="false"
[draggable]="false"
[modal]="true"
(onHide)="reset()"
(onShow)="onShow()"
appendTo="body">
<div class="p-grid p-nogutter">
<app-category-form [dialogLayout]="true"
[requiredFields]="true"
[displayValidationMessagesEvenIfPristine]="displayValidationMessagesEvenIfPristine">
</app-category-form>
</div>
<div class="p-grid p-nogutter p-jc-center">
<div class="p-formgroup-inline p-mt-3">
<p-button (onClick)="cancel()"
label="Cancel"
styleClass="p-mr-2 p-button-warning">
</p-button>
<p-button *ngIf="!categoryToEdit"
(onClick)="addCategory()"
label="Add"
styleClass="p-ml-2 p-mr-2 p-button-primary">
</p-button>
<p-button *ngIf="categoryToEdit && categoryToEdit.id"
(onClick)="editCategory()"
label="Update"
styleClass="p-ml-2 p-button-primary">
</p-button>
</div>
</div>
</p-dialog>

View File

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

View File

@ -1,86 +0,0 @@
import { CategoryFormComponent } from './../../forms/category-form/category-form.component';
import { Category } from './../../../../shared/models/category.interface';
import { CategoriesService } from '../../../../shared/services/administration/categories.service';
import { Component, Input, OnInit, Output, EventEmitter, ViewChild } from '@angular/core';
import { NotificationsHandlingService } from 'src/app/shared/services/notifications-handling/notifications-handling.service';
@Component({
selector: 'app-create-category-dialog',
templateUrl: './create-category-dialog.component.html',
styleUrls: ['./create-category-dialog.component.scss']
})
export class CreateCategoryDialogComponent implements OnInit {
@ViewChild(CategoryFormComponent) private categoryForm: CategoryFormComponent;
@Input() displayDialog: boolean;
@Input() header: string;
@Input() categoryToEdit: Category;
@Output() cancelled = new EventEmitter<boolean>();
@Output() valueChange = new EventEmitter<any>();
displayValidationMessagesEvenIfPristine: boolean;
constructor(private categoriesService: CategoriesService,
private notificationHandling: NotificationsHandlingService) { }
ngOnInit(): void {
this.reset(); // Make sure everything is cleanly initialised.
}
reset() {
this.displayValidationMessagesEvenIfPristine = false;
this.categoryForm?.resetForm();
}
onShow() {
// If a categoryToEdit was provided.
if (this.categoryToEdit) {
this.categoryForm.setValue(this.categoryToEdit);
}
}
cancel() {
this.cancelled.emit(true);
}
addCategory() {
// If the form is not valid, make sure validator messages are displayed and then return without doing anything.
if (!this.categoryForm.isValid()) {
this.displayValidationMessagesEvenIfPristine = true;
return;
}
// TODO: What is returned from backend on create()??
this.categoriesService.createNewCategory(this.categoryForm.formValue()).subscribe(result => {
this.valueChange.emit(result);
this.notificationHandling.showCreateCategorySuccess();
this.cancel();
this.notificationHandling.showCreateNewCategorySuccess();
},
error => {
});
}
editCategory() {
// If the form is not valid, make sure validator messages are displayed and then return without doing anything.
if (!this.categoryForm.isValid()) {
this.displayValidationMessagesEvenIfPristine = true;
return;
}
// TODO: What is returned from backend on create()??
// TODO: It's to ugly having to provide the categoryId separately.
this.categoriesService.updateCategory(this.categoryForm.formValue()).subscribe(result => {
this.valueChange.emit(result);
this.cancel();
this.notificationHandling.showUpdateCategorySuccess();
},
error => {
});
}
}

View File

@ -1,3 +0,0 @@
<p-fieldset legend="Edit Value" [toggleable]="true">
<app-configurator-form [editableParameter]="editParameterRequestInput" (valueChange)="passValueChange($event)"></app-configurator-form>
</p-fieldset>

View File

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

View File

@ -1,28 +0,0 @@
import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
import {ConfiguratorParameter} from "../../../../shared/models/configurator-parameter.interface";
@Component({
selector: 'app-configurator-edit-value',
templateUrl: './configurator-edit-value.component.html',
styleUrls: ['./configurator-edit-value.component.scss']
})
export class ConfiguratorEditValueComponent implements OnInit {
@Input() editParameterRequestInput: ConfiguratorParameter;
@Output() newConfiguratorParameter = new EventEmitter<ConfiguratorParameter>();
selectedParameter: ConfiguratorParameter = null;
constructor() { }
ngOnInit(): void {
console.log('editParameterRequestInput in edit: ' + JSON.stringify(this.editParameterRequestInput));
}
passValueChange(newConfigurator: ConfiguratorParameter) {
this.newConfiguratorParameter.emit(newConfigurator);
}
}

View File

@ -1,38 +0,0 @@
<p-fieldset legend="List of Parameters" [toggleable]="true">
<p-table [value]="parametersListInput"
[lazy]="true"
[loading]="loadingInput"
[autoLayout]="true"
[rowHover]="true"
styleClass="p-datatable-gridlines"
[(selection)]="selectedParameterRequest"
(click)="passSelectedConfigurator(selectedParameterRequest)">
<ng-template pTemplate="header">
<tr>
<th style="width: 3rem"></th>
<th>Parameter Name</th>
<th>Parameter Type</th>
<th>Parameter Value </th>
</tr>
</ng-template>
<ng-template pTemplate="body"
let-parameter>
<tr>
<td>
<p-tableRadioButton *ngIf="parameter.configurationVariable != 'CRON_EXPRESSION'" [value]="parameter"></p-tableRadioButton>
</td>
<td>{{parameter.configurationVariable}}</td>
<td>{{parameter.variableType}}</td>
<td>
<span *ngIf="parameter.variableType === 'Integer'" >{{parameter.integerValue}}<br></span>
<span *ngIf="parameter.variableType === 'String'" >{{parameter.stringValue}}<br></span>
</td>
<!-- <td *ngIf="parameter.variableType === 'Integer'">{{parameter.integerValue}}</td>
<td *ngIf="parameter.variableType === 'String'">{{parameter.stringValue}}</td> -->
<!-- Edit & Delete icons -->
</tr>
</ng-template>
</p-table>
</p-fieldset>

View File

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

View File

@ -1,40 +0,0 @@
import {AfterViewChecked, AfterViewInit, Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
import {ConfiguratorParameter} from '../../../../shared/models/configurator-parameter.interface';
import {Page} from '../../../../shared/models/paging/page.interface';
import {LazyLoadEvent} from 'primeng/api';
@Component({
selector: 'app-configurator-list-of-parameters',
templateUrl: './configurator-list-of-parameters.component.html',
styleUrls: ['./configurator-list-of-parameters.component.scss']
})
export class ConfiguratorListOfParametersComponent implements OnInit {
@Input() parametersListInput: ConfiguratorParameter[];
@Input() loadingInput: boolean;
@Output() editEventRequest = new EventEmitter<ConfiguratorParameter>();
editParameterRequest: ConfiguratorParameter;
parametersList: ConfiguratorParameter[];
selectedParameterRequest: ConfiguratorParameter;
totalRecords: number;
rows = 10;
constructor() { }
ngOnInit(): void {
const initParameter: ConfiguratorParameter = {
configurationId: null,
configurationVariable: null,
variableType: null,
integerValue: null,
stringValue: null
};
this.editEventRequest.emit(initParameter);
}
passSelectedConfigurator(parameter: ConfiguratorParameter) {
this.editEventRequest.emit(parameter);
}
}

View File

@ -1,27 +0,0 @@
<div class="p-grid p-nogutter">
<div class="p-col main-content">
<h1>{{'CONFIGURATOR' | translate}}</h1>
<app-horizontal-menu></app-horizontal-menu>
<app-breadcrumb></app-breadcrumb>
<div class="p-grid p-nogutter p-pt-5 p-pb-5">
<div class="p-col p-nogutter p-col-12">
<app-configurator-list-of-parameters [parametersListInput]="parametersList"
[loadingInput] = "loading"
(editEventRequest)="editEvent($event)">
</app-configurator-list-of-parameters>
</div>
<div class="p-col p-nogutter p-col-12 p-mt-5">
<app-configurator-edit-value [editParameterRequestInput]="editEventRequest" (newConfiguratorParameter)="passNewConfigurator($event)">
</app-configurator-edit-value>
</div>
</div>
</div>
</div>

View File

@ -1 +0,0 @@
@import 'src/styles';

View File

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

View File

@ -1,51 +0,0 @@
import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
import { ErrorHandlingService } from 'src/app/shared/services/error-handling/error-handling.service';
import {ConfiguratorParameter} from '../../../shared/models/configurator-parameter.interface';
import {ConfiguratorService} from "../../../shared/services/administration/configurator.service";
@Component({
selector: 'app-configurator',
templateUrl: './configurator.component.html',
styleUrls: ['./configurator.component.scss']
})
export class ConfiguratorComponent implements OnInit {
// @Input() parametersListInput: [ConfiguratorParameter];
// @Input() editParameterRequestInput: ConfiguratorParameter;
// @Output() editEventRequest = new EventEmitter<ConfiguratorParameter>();
parametersList: ConfiguratorParameter[];
editEventRequest: ConfiguratorParameter;
loading = true;
constructor(private configuratorService: ConfiguratorService, private errorHandlingService: ErrorHandlingService) { }
ngOnInit(): void {
this.initData();
console.log('this.parametersList: ' + JSON.stringify(this.parametersList));
}
initData() {
this.loading = true;
this.configuratorService.getConfiguratorParameters().subscribe(values => {
this.parametersList = values;
this.loading = false;
console.log('IN this.parametersList: ' + JSON.stringify(this.parametersList));
},
err => {
this.errorHandlingService.showHttpResponseError(err);
this.loading = false;
});
}
editEvent(request) {
this.editEventRequest = request;
}
passNewConfigurator(newConfigurator: ConfiguratorParameter){
this.initData();
}
}

View File

@ -1,101 +0,0 @@
<div [formGroup]="searchCriteriaForm">
<div class="p-fluid p-grid">
<div class="p-col-12">
<div class="p-grid p-formgrid">
<div class="p-field p-col-12 p-sm-6 p-lg-3">
<label for="dateFrom">
Date From
</label>
<p-calendar id="dateFrom"
formControlName="dateFrom"
[maxDate]="searchCriteriaForm.get('dateFrom').value"
[readonlyInput]="false"
showButtonBar="true"
todayButtonStyleClass="p-button-set-today"
clearButtonStyleClass="p-button-clear-date"
[showIcon]="true"
dateFormat="dd/mm/yy">
</p-calendar>
</div>
<div class="p-field p-col-12 p-sm-6 p-lg-3">
<label for="dateTo">
Date To
</label>
<p-calendar id="dateTo"
formControlName="dateTo"
showButtonBar="true"
[minDate]="searchCriteriaForm.get('dateTo').value"
[readonlyInput]="false"
todayButtonStyleClass="p-button-set-today"
clearButtonStyleClass="p-button-clear-date"
[showIcon]="true"
dateFormat="dd/mm/yy">
</p-calendar>
</div>
<div class="p-field p-col-12 p-sm-6 p-lg-3">
<label for="clientName">
iPower Client Name
</label>
<p-autoComplete id="clientName"
formControlName="clientName"
[suggestions]="iPowerClientNameSuggestions"
[forceSelection]="true"
(completeMethod)="autosuggestIPowerClientName($event)"
(onSelect)="iPowerClientNameSelected($event)">
</p-autoComplete>
</div>
<div class="p-field p-col-12 p-sm-6 p-lg-3">
<label for="clientId">
iPower Client Code
</label>
<p-autoComplete id="clientId"
formControlName="clientId"
[suggestions]="iPowerClientCodeSuggestions"
[forceSelection]="true"
(completeMethod)="autosuggestIPowerClientCode($event)"
(onSelect)="iPowerClientCodeSelected($event)">
</p-autoComplete>
</div>
</div>
</div>
<div class="p-col-12">
<div class="p-grid p-formgrid">
<div class="p-field p-col-12 p-sm-6 p-lg-3">
<label for="processId">
Process Id
</label>
<input id="processId"
type="text"
pInputText
formControlName="processId">
</div>
<div class="p-field p-col-12 p-sm-6 p-lg-3">
<label for="jobName">
Job Name
</label>
<input id="jobName"
type="text"
pInputText
formControlName="jobName">
</div>
<div class="p-field p-col-12 p-sm-6 p-lg-3">
<label for="dmsFileCode">
File Code
</label>
<input id="dmsFileCode"
type="text"
pInputText
formControlName="dmsFileCode">
</div>
</div>
</div>
</div>
</div>

View File

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

View File

@ -1,111 +0,0 @@
import { Component, Input, OnInit } from '@angular/core';
import { FormBuilder } from '@angular/forms';
import { IPowerClient } from 'src/app/shared/models/ipower-client.interface';
import { SearchSystemException} from 'src/app/shared/models/request/search-system-exceptions.interface';
import { IpowerClientsService } from 'src/app/shared/services/administration/ipower-clients.service';
@Component({
selector: 'app-application-level-form',
templateUrl: './application-level-form.component.html',
styleUrls: ['./application-level-form.component.scss']
})
export class ApplicationLevelFormComponent implements OnInit {
iPowerClientNameSuggestions: any;
iPowerClientCodeSuggestions: any;
private iPowerClientSuggestions: IPowerClient[];
private selectedIPowerClient: IPowerClient;
searchCriteriaForm = this.fb.group({
processId: [null],
clientId: [null],
clientName: [null],
jobName: [null],
dmsFileCode: [null],
dateFrom: [null],
dateTo: [null],
});
constructor(private fb: FormBuilder, private iPowerClientsService: IpowerClientsService) { }
ngOnInit(): void {
}
public formValue(): SearchSystemException {
let formValue: SearchSystemException = {
processId: this.searchCriteriaForm.get('processId').value,
clientId: this.searchCriteriaForm.get('clientId').value,
jobName: this.searchCriteriaForm.get('jobName').value,
dmsFileCode: this.searchCriteriaForm.get('dmsFileCode').value,
dateFrom: this.searchCriteriaForm.get('dateFrom').value,
dateTo: this.searchCriteriaForm.get('dateTo').value
};
formValue.dateFrom = new Date(Date.UTC(formValue.dateFrom?.getFullYear(), formValue.dateFrom?.getMonth(), formValue.dateFrom?.getDate()));
formValue.dateTo = new Date(Date.UTC(formValue.dateTo?.getFullYear(), formValue.dateTo?.getMonth(),formValue.dateTo?.getDate() + 1));
formValue.dateTo?.setMilliseconds(formValue.dateTo?.getMilliseconds() -1);
return formValue;
}
/*
* Auto-suggest & Auto-complete IPower Client
*/
autosuggestIPowerClientName(event): void {
if (!event.query || event.query.length < 3) {
this.iPowerClientNameSuggestions = [];
return;
}
this.iPowerClientsService.getClientsByNameOnly(event.query).subscribe(
(values: IPowerClient[]) => {
this.iPowerClientSuggestions = values;
const temp: string[] = [];
values.map(val => temp.push(val.name));
this.iPowerClientNameSuggestions = temp;
},
err => console.error(err) // TODO: Handle this via a Messaging Service
);
}
autosuggestIPowerClientCode(event): void {
if (event.query.length < 3) {
this.iPowerClientCodeSuggestions = [];
return;
}
this.iPowerClientsService.getClientsByCodeOnly(event.query).subscribe(
(values: IPowerClient[]) => {
this.iPowerClientSuggestions = values;
const temp: string[] = [];
values.map(val => temp.push(val.clientCode));
this.iPowerClientCodeSuggestions = temp;
},
err => console.error(err)
);
}
iPowerClientNameSelected(name: string): void {
this.selectedIPowerClient = this.iPowerClientSuggestions.find(client => client.name === name);
this.searchCriteriaForm.get('clientId').patchValue(this.selectedIPowerClient ? this.selectedIPowerClient.clientCode : '');
this.searchCriteriaForm.updateValueAndValidity();
}
iPowerClientCodeSelected(code: string): void {
this.selectedIPowerClient = this.iPowerClientSuggestions.find(client => client.clientCode === code);
this.searchCriteriaForm.get('clientName').patchValue(this.selectedIPowerClient ? this.selectedIPowerClient.name : '');
this.searchCriteriaForm.updateValueAndValidity();
}
resetForm() {
this.searchCriteriaForm.reset();
}
}

View File

@ -1,72 +0,0 @@
<div [formGroup]="categoryForm">
<div class="p-fluid p-formgrid"
[ngClass]="{'p-grid': !dialogLayout}">
<div class="p-field"
[ngClass]="{'p-col-12 p-lg-4': !dialogLayout}">
<label for="document-classification">
Document Classification
</label>
<p-dropdown inputId="document-classification"
[options]="documentClassificationsList"
optionLabel="classificationName"
placeholder="Select"
formControlName="documentClassification"
[showClear]="true">
</p-dropdown>
<app-validation-message [control]="categoryForm.get('documentClassification')"
[validationMessage]="'This field is required.'"
[displayEvenIfPristine]="displayValidationMessagesEvenIfPristine">
</app-validation-message>
</div>
<div class="p-field"
[ngClass]="{'p-col-12 p-lg-4': !dialogLayout}">
<label for="categoryName">
Category Name
</label>
<p-autoComplete id="categoryName"
formControlName="categoryName"
*ngIf="autosuggestInputs"
[suggestions]="categoryNameSuggestions"
(completeMethod)="autosuggestCategoryName($event)"
(onSelect)="categoryNameSelected($event)"
[forceSelection]="true">
</p-autoComplete>
<input id="categoryName"
type="text"
pInputText
formControlName="categoryName"
*ngIf="!autosuggestInputs">
<app-validation-message [control]="categoryForm.get('categoryName')"
[validationMessage]="'This field is required.'"
[displayEvenIfPristine]="displayValidationMessagesEvenIfPristine">
</app-validation-message>
</div>
<div class="p-field"
[ngClass]="{'p-col-12 p-lg-4': !dialogLayout}">
<label for="categoryCode">
Category Code
</label>
<p-autoComplete id="categoryCode"
formControlName="categoryCode"
*ngIf="autosuggestInputs"
[suggestions]="categoryCodeSuggestions"
(completeMethod)="autosuggestCategoryCode($event)"
(onSelect)="categoryCodeSelected($event)"
[forceSelection]="true">
</p-autoComplete>
<input id="categoryCode"
type="text"
pInputText
formControlName="categoryCode"
*ngIf="!autosuggestInputs">
<app-validation-message [control]="categoryForm.get('categoryCode')"
[validationMessage]="'This field is required.'"
[displayEvenIfPristine]="displayValidationMessagesEvenIfPristine">
</app-validation-message>
</div>
</div>
</div>

View File

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

View File

@ -1,183 +0,0 @@
import { Category } from './../../../../shared/models/category.interface';
import { Component, Input, OnInit } from '@angular/core';
import { FormBuilder, Validators, FormGroup } from '@angular/forms';
import { DocumentClassification } from 'src/app/shared/models/document-classification.interface';
import { CategoriesService } from 'src/app/shared/services/administration/categories.service';
import { DocumentClassificationsService } from 'src/app/shared/services/administration/document-classifications.service';
import { ErrorHandlingService } from 'src/app/shared/services/error-handling/error-handling.service';
@Component({
selector: 'app-category-form',
templateUrl: './category-form.component.html',
styleUrls: ['./category-form.component.scss']
})
export class CategoryFormComponent implements OnInit {
/*
* Inputs
*/
@Input() dialogLayout: boolean = false; // Controls .scss classes to allow for better dispay in a dialog.
@Input() requiredFields: string[] | boolean = false; // True/False indicates that all/none are required. String[] specifies the form controls required.
@Input() displayValidationMessagesEvenIfPristine: boolean = false; // Serves for manually treating the controls as dirty.
@Input() autosuggestInputs: boolean = false;
/*
* Reactive Form
*/
categoryForm = this.fb.group({
documentClassification: [null],
categoryName: [null],
categoryCode: [null]
});
setFormValue: Category = null;
selectedCategory: Category;
/*
* Other Variables
*/
documentClassificationsList: DocumentClassification[];
categoryNameSuggestions: string[];
categoryCodeSuggestions: string[];
categorySuggestions: Category[];
/*
* Constructor & Initialisers
*/
constructor(
private fb: FormBuilder,
private categoriesService: CategoriesService,
private documentClassificationsService: DocumentClassificationsService,
private errorHandlingService: ErrorHandlingService
) { }
ngOnInit(): void {
this.initData();
this.initValidators()
}
initData() {
this.documentClassificationsService.getAll().subscribe(
value => this.documentClassificationsList = value,
err => this.errorHandlingService.showHttpResponseError(err)
);
}
// TODO: Have this mechanism offer the use of custom validators too.
initValidators() {
if (!this.requiredFields) { return; }
// In a true/false case.
if (typeof this.requiredFields == 'boolean') {
// If true, enable the required validator for all controls and sub-controls.
if (this.requiredFields) {
this.setRequiredValidatorRecursively(this.categoryForm);
}
// If false, do nothing.
return;
}
// If it was a string array, enable the validators for all provided field-names. TODO: This ONLY supports 1st level controls and not nested ones.
(<string[]>this.requiredFields).forEach(field => this.categoryForm.controls[field].setValidators(Validators.required))
}
setRequiredValidatorRecursively(group: FormGroup) {
Object.keys(group.controls).forEach(key => {
group.controls[key].setValidators(Validators.required);
if (group.controls[key]['controls']) {
this.setRequiredValidatorRecursively(<FormGroup>group.controls[key])
}
});
}
/*
* Auto-suggest & Auto-complete Category
*/
autosuggestCategoryName(event) {
if (!event.query || event.query.length < 3) {
this.categoryNameSuggestions = [];
return;
}
let classId = this.categoryForm.get('documentClassification').value ? this.categoryForm.get('documentClassification').value.classificationId : null;
this.categoriesService.autosuggestCategoryName(event.query, classId).subscribe(
(values: Category[]) => {
let temp: string[] = [];
this.categorySuggestions = values;
values.map(val => temp.push(val.categoryName));
this.categoryNameSuggestions = temp
},
err => this.errorHandlingService.showHttpResponseError(err)
);
}
autosuggestCategoryCode(event) {
if (event.query.length < 3) {
this.categoryCodeSuggestions = [];
return;
}
let classId = this.categoryForm.get('documentClassification').value ? this.categoryForm.get('documentClassification').value.classificationId : null;
this.categoriesService.autosuggestCategoryCode(event.query, classId).subscribe(
(values: Category[]) => {
let temp: string[] = [];
this.categorySuggestions = values;
values.map(val => temp.push(val.categoryCode));
this.categoryCodeSuggestions = temp
},
err => this.errorHandlingService.showHttpResponseError(err)
);
}
categoryNameSelected(name: string) {
this.selectedCategory = this.categorySuggestions.find(cat => cat.categoryName == name);
this.categoryForm.get('categoryCode').patchValue(this.selectedCategory.categoryCode);
this.categoryForm.updateValueAndValidity();
}
categoryCodeSelected(code: string) {
this.selectedCategory = this.categorySuggestions.find(cat => cat.categoryCode == code);
this.categoryForm.get('categoryName').patchValue(this.selectedCategory.categoryName);
this.categoryForm.updateValueAndValidity();
}
/*
* API methods
*/
public resetForm(): void {
this.categoryForm.reset();
}
public formValue(): Category {
let formValue: Category = {
id: this.setFormValue ? this.setFormValue.id : null,
documentClassification: this.categoryForm.get('documentClassification').value,
categoryName: this.categoryForm.get('categoryName').value,
categoryCode: this.categoryForm.get('categoryCode').value
};
return formValue;
}
public setValue(value: Category): void {
if (!value) {
return;
}
this.setFormValue = value;
this.categoryForm.get('documentClassification').setValue(value.documentClassification);
this.categoryForm.get('categoryName').setValue(value.categoryName);
this.categoryForm.get('categoryCode').setValue(value.categoryCode);
this.categoryForm.updateValueAndValidity();
}
public isValid(): boolean {
return this.categoryForm.valid;
}
}

View File

@ -1,60 +0,0 @@
<div [formGroup]="configuratorForm">
<div class="p-fluid p-formgrid"
[ngClass]="{'p-grid': !dialogLayout}">
<!-- Configurator Form - Start -->
<div class="p-field p-col-12"
[ngClass]="{'p-lg-4': !dialogLayout}">
<label for="parameterName">Parameter Name</label>
<input id="parameterName"
type="text"
pInputText
disabled
formControlName="parameterName"
value="{{editableParameter?.configurationVariable}}">
<app-validation-message [control]="configuratorForm.get('parameterName')"
[validationMessage]="'This field is required.'"
[displayEvenIfPristine]="displayValidationMessagesEvenIfPristine">
</app-validation-message>
</div>
<div class="p-field p-col-12 "
[ngClass]="{'p-col-12 p-lg-4': !dialogLayout}">
<label for="parameterType">Parameter Type</label>
<input id="parameterType"
type="text"
pInputText
disabled
formControlName="parameterType"
value="{{editableParameter?.variableType}}">
<app-validation-message [control]="configuratorForm.get('parameterType')"
[validationMessage]="'This field is required.'"
[displayEvenIfPristine]="displayValidationMessagesEvenIfPristine">
</app-validation-message>
</div>
<div class="p-field p-col-12 "
[ngClass]="{'p-col-12 p-lg-4': !dialogLayout}">
<label for="parameterValue">Parameter Value</label>
<input id="parameterValue"
type="text"
pInputText
formControlName="parameterValue"
value="{{editableParameter?.variableType == 'Integer' ? editableParameter?.integerValue : editableParameter?.stringValue}}">
<app-validation-message [control]="configuratorForm.get('parameterValue')"
[validationMessage]="'This field is required.'"
[displayEvenIfPristine]="displayValidationMessagesEvenIfPristine">
</app-validation-message>
</div>
</div>
<div class="p-formgroup-inline p-jc-center p-mt-3">
<p-button (onClick)="updateButtonClicked()"
label="Update"
styleClass="p-button-primary"
[disabled]="!canEditParameterValue()">
</p-button>
</div>
</div>

View File

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

View File

@ -1,69 +0,0 @@
import { AfterViewChecked, Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { FormBuilder } from '@angular/forms';
import { ConfiguratorParameter } from '../../../../shared/models/configurator-parameter.interface';
import { ConfiguratorService } from "../../../../shared/services/administration/configurator.service";
import { Category } from "../../../../shared/models/category.interface";
import { Router } from "@angular/router";
import { NotificationsHandlingService } from 'src/app/shared/services/notifications-handling/notifications-handling.service';
import { AuthService } from 'src/app/shared/services/auth.service';
import { USER_RIGHTS } from 'src/app/shared/enums/USER_RIGHTS.enum';
import { environment } from 'src/environments/environment';
@Component({
selector: 'app-configurator-form',
templateUrl: './configurator-form.component.html',
styleUrls: ['./configurator-form.component.scss']
})
export class ConfiguratorFormComponent implements OnInit {
@Input() dialogLayout: boolean = false; // Controls .scss classes to allow for better dispay in a dialog.
@Input() displayValidationMessagesEvenIfPristine: boolean = false; // Serves for manually treating the controls as dirty.
@Input() editableParameter: ConfiguratorParameter;
@Output() valueChange = new EventEmitter<ConfiguratorParameter>();
/*
* Reactive Form
*/
configuratorForm = this.fb.group({
parameterName: null,
parameterType: null,
parameterValue: null
});
constructor(private fb: FormBuilder, private configuratorService: ConfiguratorService, private router: Router, private notificationService: NotificationsHandlingService,
private authService: AuthService) { }
ngOnInit(): void {
}
ngOnChanges(): void {
}
public formValue(): ConfiguratorParameter {
let formValue: ConfiguratorParameter = {
configurationId: this.editableParameter.configurationId,
configurationVariable: this.editableParameter.configurationVariable,
variableType: this.editableParameter.variableType,
integerValue: this.editableParameter.variableType === 'Integer' ? Number(this.configuratorForm.get('parameterValue').value) : null,
stringValue: this.editableParameter.variableType === 'String' ? String(this.configuratorForm.get('parameterValue').value) : null
};
return formValue;
}
updateButtonClicked(): void {
if (this.editableParameter.configurationId !== null) {
this.configuratorService.updateConfiguratorParameter(this.formValue()).subscribe(result => {
this.valueChange.emit(this.formValue());
this.notificationService.showUpdateConfiguratorParameterSuccess();
},
error => {
});
}
}
canEditParameterValue(): boolean {
return this.authService.userHasRightForClient(USER_RIGHTS.J01, environment.globalRightsClientID) && this.editableParameter.configurationVariable != null;
}
}

View File

@ -1,138 +0,0 @@
<div [formGroup]="templateForm">
<div class="p-fluid p-formgrid"
[ngClass]="{'p-grid': !dialogLayout}">
<div class="p-field"
*ngIf="!excludedFormControls?.includes('documentClassification')"
[ngClass]="{'p-col-12 p-lg-4': !dialogLayout}">
<label for="documentClassification">
Document Classification
</label>
<p-dropdown inputId="documentClassification"
[options]="documentClassificationsList"
optionLabel="classificationName"
placeholder="Select"
formControlName="documentClassification"
(onChange)="documentClassificationSelected($event.value)"
[showClear]="true">
</p-dropdown>
<app-validation-message [control]="templateForm.get('documentClassification')"
[validationMessage]="'This field is required.'"
[displayEvenIfPristine]="displayValidationMessagesEvenIfPristine">
</app-validation-message>
</div>
<div class="p-field"
*ngIf="!excludedFormControls?.includes('categoryName')"
[ngClass]="{'p-col-12 p-lg-4': !dialogLayout}">
<label for="categoryName">
Category Name
</label>
<p-autoComplete id="categoryName"
formControlName="categoryName"
[suggestions]="categoryNameSuggestions"
[forceSelection]="true"
(completeMethod)="autosuggestCategoryName($event)"
(onSelect)="categoryNameSelected($event)">
</p-autoComplete>
<app-validation-message [control]="templateForm.get('categoryName')"
[validationMessage]="'This field is required.'"
[displayEvenIfPristine]="displayValidationMessagesEvenIfPristine">
</app-validation-message>
</div>
<div class="p-field"
*ngIf="!excludedFormControls?.includes('categoryName')"
[ngClass]="{'p-col-12 p-lg-4': !dialogLayout}">
<label for="categoryCode">
Category Code
</label>
<p-autoComplete id="categoryCode"
formControlName="categoryCode"
[suggestions]="categoryCodeSuggestions"
[forceSelection]="true"
(completeMethod)="autosuggestCategoryCode($event)"
(onSelect)="categoryCodeSelected($event)">
</p-autoComplete>
<app-validation-message [control]="templateForm.get('categoryCode')"
[validationMessage]="'This field is required.'"
[displayEvenIfPristine]="displayValidationMessagesEvenIfPristine">
</app-validation-message>
</div>
<div class="p-field"
*ngIf="!excludedFormControls?.includes('iPowerClient')"
[ngClass]="{'p-col-12 p-lg-4': !dialogLayout}">
<label for="iPowerClientName">
iPower Client Name
</label>
<p-autoComplete id="iPowerClientName"
formControlName="iPowerClientName"
[suggestions]="iPowerClientNameSuggestions"
[forceSelection]="true"
(completeMethod)="autosuggestIPowerClientName($event)"
(onSelect)="iPowerClientNameSelected($event)">
</p-autoComplete>
<app-validation-message [control]="templateForm.get('iPowerClientName')"
[validationMessage]="'This field is required.'"
[displayEvenIfPristine]="displayValidationMessagesEvenIfPristine">
</app-validation-message>
</div>
<div class="p-field"
*ngIf="!excludedFormControls?.includes('iPowerClient')"
[ngClass]="{'p-col-12 p-lg-4': !dialogLayout}">
<label for="iPowerClientCode">
iPower Client Code
</label>
<p-autoComplete id="iPowerClientCode"
formControlName="iPowerClientCode"
[suggestions]="iPowerClientCodeSuggestions"
[forceSelection]="true"
(completeMethod)="autosuggestIPowerClientCode($event)"
(onSelect)="iPowerClientCodeSelected($event)">
</p-autoComplete>
<app-validation-message [control]="templateForm.get('iPowerClientCode')"
[validationMessage]="'This field is required.'"
[displayEvenIfPristine]="displayValidationMessagesEvenIfPristine">
</app-validation-message>
</div>
<div class="p-field"
*ngIf="!excludedFormControls?.includes('documentSubclassification')"
[ngClass]="{'p-col-12 p-lg-4': !dialogLayout}">
<label for="documentSubclassification">
Sub-Category Name
</label>
<p-dropdown inputId="documentSubclassification"
[options]="availableDocumentSubclassifications"
[disabled]="subCategoryCodeDisabled"
optionLabel="subclassificationName"
placeholder="Select a Document Classification first"
formControlName="documentSubclassification">
</p-dropdown>
<app-validation-message [control]="templateForm.get('documentSubclassification')"
[validationMessage]="'This field is required.'"
[displayEvenIfPristine]="displayValidationMessagesEvenIfPristine">
</app-validation-message>
</div>
<div class="p-field"
*ngIf="!excludedFormControls?.includes('abbyyTemplateCode')"
[ngClass]="{'p-col-12 p-lg-4': !dialogLayout}">
<label for="abbyyTemplateCode"
>
ABBYY Template Code
</label>
<input id="abbyyTemplateCode"
type="text"
pInputText
formControlName="abbyyTemplateCode">
<app-validation-message [control]="templateForm.get('abbyyTemplateCode')"
[validationMessage]="'This field is required.'"
[displayEvenIfPristine]="displayValidationMessagesEvenIfPristine">
</app-validation-message>
</div>
</div>
</div>

View File

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

View File

@ -1,369 +0,0 @@
import { DocumentClassification } from './../../../../shared/models/document-classification.interface';
import { Category } from './../../../../shared/models/category.interface';
import { DocumentSubclassificationsService } from './../../../../shared/services/administration/document-subclassifications.service';
import { DocumentSubclassification } from './../../../../shared/models/document-subclassification.interface';
import { IPowerClient } from './../../../../shared/models/ipower-client.interface';
import { Template } from './../../../../shared/models/template.interface';
import { Component, Input, OnInit } from '@angular/core';
import { FormBuilder, Validators } from '@angular/forms';
import { CategoriesService } from 'src/app/shared/services/administration/categories.service';
import { DocumentClassificationsService } from 'src/app/shared/services/administration/document-classifications.service';
import { IpowerClientsService } from 'src/app/shared/services/administration/ipower-clients.service';
import { ErrorHandlingService } from 'src/app/shared/services/error-handling/error-handling.service';
@Component({
selector: 'app-template-form',
templateUrl: './template-form.component.html',
styleUrls: ['./template-form.component.scss']
})
export class TemplateFormComponent implements OnInit {
/*
* Inputs
*/
@Input() dialogLayout: boolean = false; // Controls .scss classes to allow for better dispay in a dialog.
@Input() requiredFields: string[] | boolean = false; // True/False indicates that all/none are required. String[] specifies the form controls required.
@Input() displayValidationMessagesEvenIfPristine: boolean = false; // Serves for manually treating the controls as dirty.
@Input() excludedFormControls: string[]; // Specifies the form controls to be removed and NOT displayed.
/*
* Reactive Form
*/
templateForm = this.fb.group({
documentClassification: [null],
categoryName: [null],
categoryCode: [null],
iPowerClientName: [null],
iPowerClientCode: [null],
documentSubclassification: [null],
abbyyTemplateCode: [null]
});
initiallySetFormValue: Template;
/*
* Other Variables
*/
documentClassificationsList: DocumentClassification[];
categoryNameSuggestions: string[];
categoryCodeSuggestions: string[];
categorySuggestions: Category[];
selectedCategory: Category;
iPowerClientNameSuggestions: string[];
iPowerClientCodeSuggestions: string[];
iPowerClientSuggestions: IPowerClient[];
selectedIPowerClient: IPowerClient;
documentSubclassificationsList: DocumentSubclassification[];
availableDocumentSubclassifications: DocumentSubclassification[];
subCategoryCodeDisabled: boolean = true;
/*
* Constructor & Initialisers
*/
constructor(
private fb: FormBuilder,
private documentClassificationsService: DocumentClassificationsService,
private categoriesService: CategoriesService,
private iPowerClientsService: IpowerClientsService,
private documentSubclassificationsService: DocumentSubclassificationsService,
private errorHandlingService: ErrorHandlingService
) { }
ngOnInit(): void {
this.initData();
this.initValidators()
this.excludeFields();
}
initData() {
this.documentClassificationsService.getAll().subscribe(
value => this.documentClassificationsList = value,
err => this.errorHandlingService.showHttpResponseError(err)
);
// This is NOT the list of Subclassifications used for the dropdown.
this.documentSubclassificationsService.getAll().subscribe(
value => this.documentSubclassificationsList = value,
err => this.errorHandlingService.showHttpResponseError(err)
);
}
// TODO: Have this mechanism offer the use of custom validators too.
initValidators() {
if (!this.requiredFields) { return; }
// In a true/false case.
if (typeof this.requiredFields == 'boolean') {
// If true, enable the required validator for all controls.
if (this.requiredFields) {
Object.keys(this.templateForm.controls).forEach(key => this.templateForm.controls[key].setValidators(Validators.required));
}
// If false, do nothing.
return;
}
// If it was a string array, enable the validators for all provided field-names.
(<string[]>this.requiredFields).forEach(field => this.templateForm.controls[field].setValidators(Validators.required))
}
excludeFields() {
if (!this.excludedFormControls) { return; }
this.excludedFormControls.forEach(field => this.templateForm.removeControl(field));
}
/*
* Auto-suggest & Auto-complete Category
*/
autosuggestCategoryName(event) {
if (!event.query || event.query.length < 3) {
this.categoryNameSuggestions = [];
return;
}
let classId = this.templateForm.get('documentClassification').value ? this.templateForm.get('documentClassification').value.classificationId : null;
this.categoriesService.autosuggestCategoryName(event.query, classId).subscribe(
(values: Category[]) => {
let temp: string[] = [];
this.categorySuggestions = values;
values.map(val => temp.push(val.categoryName));
this.categoryNameSuggestions = temp
},
err => this.errorHandlingService.showHttpResponseError(err)
);
}
autosuggestCategoryCode(event) {
if (event.query.length < 3) {
this.categoryCodeSuggestions = [];
return;
}
let classId = this.templateForm.get('documentClassification').value ? this.templateForm.get('documentClassification').value.classificationId : null;
this.categoriesService.autosuggestCategoryCode(event.query, classId).subscribe(
(values: Category[]) => {
let temp: string[] = [];
this.categorySuggestions = values;
values.map(val => temp.push(val.categoryCode));
this.categoryCodeSuggestions = temp
},
err => this.errorHandlingService.showHttpResponseError(err)
);
}
categoryNameSelected(name: string) {
this.selectedCategory = this.categorySuggestions.find(cat => cat.categoryName == name);
this.templateForm.get('categoryCode')?.patchValue(this.selectedCategory.categoryCode);
this.templateForm.updateValueAndValidity();
}
categoryCodeSelected(code: string) {
this.selectedCategory = this.categorySuggestions.find(cat => cat.categoryCode == code);
this.templateForm.get('categoryName')?.patchValue(this.selectedCategory?.categoryName);
this.templateForm.updateValueAndValidity();
}
/*
* Auto-suggest & Auto-complete IPower Client
*/
autosuggestIPowerClientName(event): void {
if (!event.query || event.query.length < 3) {
this.iPowerClientNameSuggestions = [];
return;
}
this.iPowerClientsService.getClientsByNameOnly(event.query).subscribe(
(values: IPowerClient[]) => {
this.iPowerClientSuggestions = values;
const temp: string[] = [];
values.map(val => temp.push(val.name));
this.iPowerClientNameSuggestions = temp;
},
err => this.errorHandlingService.showHttpResponseError(err)
);
}
autosuggestIPowerClientCode(event): void {
if (event.query.length < 3) {
this.iPowerClientCodeSuggestions = [];
return;
}
this.iPowerClientsService.getClientsByCodeOnly(event.query).subscribe(
(values: IPowerClient[]) => {
this.iPowerClientSuggestions = values;
const temp: string[] = [];
values.map(val => temp.push(val.clientCode));
this.iPowerClientCodeSuggestions = temp;
},
err => this.errorHandlingService.showHttpResponseError(err)
);
}
iPowerClientNameSelected(name: string) {
this.selectedIPowerClient = this.iPowerClientSuggestions.find(client => client.name == name);
this.templateForm.get('iPowerClientCode')?.patchValue(this.selectedIPowerClient ? this.selectedIPowerClient.clientCode : '');
this.templateForm.updateValueAndValidity();
}
iPowerClientCodeSelected(code: string) {
this.selectedIPowerClient = this.iPowerClientSuggestions.find(client => client.clientCode == code);
this.templateForm.get('iPowerClientName')?.patchValue(this.selectedIPowerClient ? this.selectedIPowerClient.name : '');
this.templateForm.updateValueAndValidity();
}
/*
* Other Methods
*/
documentClassificationSelected(selection: DocumentClassification) {
this.availableDocumentSubclassifications = this.documentSubclassificationsList.filter(element => element.documentClassification.classificationId == selection.classificationId);
this.templateForm.get('documentSubclassification')?.setValue(this.availableDocumentSubclassifications[0]); //TODO CHECK
this.subCategoryCodeDisabled = false;
}
/*
* Utility Methods
*/
// Auto-suggest inputs - We have to ensure our reactive form holds values that represent the selectedCategory, otherwise truncate it.
syncSelectedCategory() {
// Ιf our form has no value, truncate the selectedCategory either way.
if (!this.templateForm.get('categoryName')?.value && !this.templateForm.get('categoryCode').value) {
this.selectedCategory = null;
return;
}
// If both or either of our form's values match the selectedCategory's and the other one doesn't have a value, all is good.
// Just sync the values in case one is missing. Otherwise truncate the selectedCategory.
if (
this.templateForm.get('categoryName')?.value == this.selectedCategory.categoryName
|| this.templateForm.get('categoryCode')?.value == this.selectedCategory.categoryCode
) {
this.selectedCategory.categoryName = this.templateForm.get('categoryName')?.value;
this.selectedCategory.categoryCode = this.templateForm.get('categoryCode')?.value;
}
// If both our values were different from the selectedCategory's, truncate it. This is an extremely abnormal scenario.
else {
console.error('WARNING - syncSelectedCategory()', 'Both of our form\'s values were different from the selectedCategory\'s.');
this.selectedCategory = null;
this.templateForm.get('categoryName')?.setValue('');
this.templateForm.get('categoryCode')?.setValue('');
this.templateForm.updateValueAndValidity();
}
}
// Auto-suggest inputs - We have to ensure our reactive form holds values that represent the selectedIPowerClient, otherwise truncate it.
syncSelectedIPowerClient() {
// Ιf our form has no value, truncate the selectedIPowerClient either way.
if (!this.templateForm.get('iPowerClientName')?.value && !this.templateForm.get('iPowerClientCode')?.value) {
this.selectedIPowerClient = null;
return;
}
// If both or either of our form's values match the selectedIPowerClient's and the other one doesn't have a value, all is good.
// Just sync the values in case one is missing. Otherwise truncate the selectedIPowerClient.
if (
this.templateForm.get('iPowerClientName')?.value == this.selectedIPowerClient.name
|| this.templateForm.get('iPowerClientCode')?.value == this.selectedIPowerClient.clientCode
) {
this.selectedIPowerClient.name = this.templateForm.get('iPowerClientName')?.value;
this.selectedIPowerClient.clientCode = this.templateForm.get('iPowerClientCode')?.value;
}
// If both our values were different from the selectedIPowerClient's, truncate it. This is an extremely abnormal scenario.
else {
console.error('WARNING - syncSelectedIPowerClient()', 'Both of our form\'s values were different from the selectedIPowerClient\'s.');
this.selectedIPowerClient = null;
this.templateForm.get('iPowerClientName')?.setValue('');
this.templateForm.get('iPowerClientCode')?.setValue('');
this.templateForm.updateValueAndValidity();
}
}
/*
* API methods
*/
public resetForm(): void {
this.templateForm.reset();
this.selectedCategory = null;
this.selectedIPowerClient = null;
}
public formValue(): Template {
// A field ('documentSubclassification') may be excluded, so we have to check for that too.
let subCatCode = this.templateForm.get('documentSubclassification') ? this.templateForm.get('documentSubclassification').value : null;
let abbyy = this.templateForm.get('abbyyTemplateCode') ? this.templateForm.get('abbyyTemplateCode').value : null;
let docClass = this.templateForm.get('documentClassification') ? this.templateForm.get('documentClassification').value : null;
// We keep track of those two using object-variables separate from our ReactiveForm.
// Thus, we now have to make sure the ReactiveForm really has values that match those objects, before we forward them.
// Also, keep in mind that our auto-suggest inputs limit the user in choosing one of their values.
this.syncSelectedCategory();
this.syncSelectedIPowerClient();
let formValue: Template = {
id: this.initiallySetFormValue ? this.initiallySetFormValue.id : null,
category: this.selectedCategory,
subCategoryCode: subCatCode ? subCatCode.subclassificationName : '',
abbyyTemplate: abbyy,
client: this.selectedIPowerClient,
documentClassification: docClass
};
return formValue;
}
public setValue(value: Template): void {
if (!value) {
return;
}
this.initiallySetFormValue = value;
// If a documentClassification is already selected, enable the subclassification dropdown.
this.subCategoryCodeDisabled = !value.documentClassification;
this.templateForm.get('documentClassification')?.setValue(value.documentClassification);
// Having set the documentClassification -and provided there was one- we must also set the availableDocumentSubclassifications.
this.availableDocumentSubclassifications = this.documentSubclassificationsList.filter(element =>
element.documentClassification.classificationId == value.documentClassification.classificationId
);
this.selectedCategory = value.category;
this.templateForm.get('categoryName')?.setValue(value.category.categoryName);
this.templateForm.get('categoryCode')?.setValue(value.category.categoryCode);
this.selectedIPowerClient = value.client;
this.templateForm.get('iPowerClientName')?.setValue(value.client.name);
this.templateForm.get('iPowerClientCode')?.setValue(value.client.clientCode);
this.templateForm.get('abbyyTemplateCode')?.setValue(value.abbyyTemplate);
// To set the subcategory/subclassification we also have to make sure the documentClassification is the same.
// WARNING: Since we have enabled the [forceSelection] option of the <p-autoComplete>,
// if the availableDocumentSubclassifications are not set, documentSubclassification won't be displayed.
this.templateForm.get('documentSubclassification').setValue(
this.documentSubclassificationsList.find(subClass => (
subClass.subclassificationName == value.subCategoryCode && subClass.documentClassification.classificationId == value.documentClassification.classificationId
))
);
this.templateForm.updateValueAndValidity();
}
public isValid(): boolean {
return this.templateForm.valid;
}
}

View File

@ -1,16 +0,0 @@
import { JournalVerification } from './../../../../shared/models/journal-verification.interface';
import { CapturingVerification } from './../../../../shared/models/capturing-verification.interface';
import { Template } from 'src/app/shared/models/template.interface';
export interface CreateVerificationRuleFormValue {
id: number;
confidenceLevelMinThreshold: number;
clientId: string
categoryId: number;
subCategoryCode: string;
verificationRuleStatus: string;
capturingVerification: CapturingVerification;
journalVerification: JournalVerification;
alteryxRoutineId: string;
template: Template;
}

View File

@ -1,200 +0,0 @@
<div [formGroup]="verificationRuleForm">
<div class="p-fluid"
[ngClass]="{'p-formgrid': !dialogLayout}">
<div class="p-grid">
<div class="p-field p-col-12 p-lg-6">
<label for="document-classification">
Document Classification
</label>
<p-dropdown inputId="document-classification"
[options]="documentClassificationsList"
optionLabel="classificationName"
placeholder="Select"
formControlName="documentClassification"
[disabled]="editMode"
(onChange)="documentClassificationSelected($event)">
</p-dropdown>
<app-validation-message [control]="verificationRuleForm.get('documentClassification')"
[validationMessage]="'This field is required.'"
[displayEvenIfPristine]="displayValidationMessagesEvenIfPristine">
</app-validation-message>
</div>
</div>
<div class="p-grid">
<div class="p-field p-col-12 p-lg-6">
<label for="ipower-name">
iPower Client Name
</label>
<p-autoComplete id="ipower-name"
formControlName="ipowerName"
[suggestions]="iPowerClientNameSuggestions"
[forceSelection]="true"
(completeMethod)="autosuggestIPowerClientName($event)"
[disabled]="editMode"
(onSelect)="iPowerClientNameSelected($event)">
</p-autoComplete>
<app-validation-message [control]="verificationRuleForm.get('ipowerName')"
[validationMessage]="'This field is required.'"
[displayEvenIfPristine]="displayValidationMessagesEvenIfPristine">
</app-validation-message>
</div>
<div class="p-field p-col-12 p-lg-6">
<label for="ipower-code">
iPower Client Code
</label>
<p-autoComplete id="ipower-code"
formControlName="ipowerCode"
[suggestions]="iPowerClientCodeSuggestions"
[forceSelection]="true"
(completeMethod)="autosuggestIPowerClientCode($event)"
[disabled]="editMode"
(onSelect)="iPowerClientCodeSelected($event)">
</p-autoComplete>
<app-validation-message [control]="verificationRuleForm.get('ipowerCode')"
[validationMessage]="'This field is required.'"
[displayEvenIfPristine]="displayValidationMessagesEvenIfPristine">
</app-validation-message>
</div>
</div>
<div class="p-grid">
<div class="p-field p-col-12 p-lg-6">
<label for="category-name">
Category Name
</label>
<p-autoComplete id="category-name"
formControlName="categoryName"
[suggestions]="categoryNameSuggestions"
[forceSelection]="true"
(completeMethod)="autosuggestCategoryName($event)"
[disabled]="editMode"
(onSelect)="categoryNameSelected($event)">
</p-autoComplete>
<app-validation-message [control]="verificationRuleForm.get('categoryName')"
[validationMessage]="'This field is required.'"
[displayEvenIfPristine]="displayValidationMessagesEvenIfPristine">
</app-validation-message>
</div>
<div class="p-field p-col-12 p-lg-6">
<label for="category-code">
Category Code
</label>
<p-autoComplete id="category-code"
formControlName="categoryCode"
[suggestions]="categoryCodeSuggestions"
[forceSelection]="true"
(completeMethod)="autosuggestCategoryCode($event)"
[disabled]="editMode"
(onSelect)="categoryCodeSelected($event)">
</p-autoComplete>
<app-validation-message [control]="verificationRuleForm.get('categoryCode')"
[validationMessage]="'This field is required.'"
[displayEvenIfPristine]="displayValidationMessagesEvenIfPristine">
</app-validation-message>
</div>
</div>
<div class="p-grid">
<div class="p-field p-col-12 p-lg-6">
<label for="document-subclassification">
Sub-Category Name
</label>
<p-dropdown inputId="document-subclassification"
[options]="availableDocumentSubclassifications"
[disabled]="subCategoryCodeDisabled || editMode"
optionLabel="subclassificationName"
placeholder="Select a Document Classification first"
formControlName="subCategoryCode">
</p-dropdown>
<app-validation-message [control]="verificationRuleForm.get('subCategoryCode')"
[validationMessage]="'This field is required.'"
[displayEvenIfPristine]="displayValidationMessagesEvenIfPristine">
</app-validation-message>
</div>
</div>
<div class="p-grid"
[ngClass]="{'p-pt-4': (initiallySetFormValue && initiallySetFormValue.id)}">
<div class="p-field p-col-12 p-lg-6">
<label for="capturing-verification">
Capturing Verification
</label>
<p-dropdown inputId="capturing-verification"
formControlName="capturingVerification"
[options]="capturingVerificationsList"
optionLabel="capturingVerificationName"
placeholder="Select">
</p-dropdown>
<app-validation-message [control]="verificationRuleForm.get('capturingVerification')"
[validationMessage]="'This field is required.'"
[displayEvenIfPristine]="displayValidationMessagesEvenIfPristine">
</app-validation-message>
</div>
<div class="p-field p-col-12 p-lg-6">
<label for="journal-verification">
Journal Verification
</label>
<p-dropdown inputId="journal-verification"
formControlName="journalVerification"
[options]="journalVerificationsList"
optionLabel="journalVerificationName"
placeholder="Select">
</p-dropdown>
<app-validation-message [control]="verificationRuleForm.get('journalVerification')"
[validationMessage]="'This field is required.'"
[displayEvenIfPristine]="displayValidationMessagesEvenIfPristine">
</app-validation-message>
</div>
</div>
<div class="p-grid">
<div class="p-field p-col-12 p-lg-6">
<label for="confidence-level">
Confidence Level
</label>
<input id="confidence-level"
type="number"
pInputText
formControlName="confidenceLevelMinThreshold">
<app-validation-message [control]="verificationRuleForm.get('confidenceLevelMinThreshold')"
[validationMessage]="'This field is required.'"
[displayEvenIfPristine]="displayValidationMessagesEvenIfPristine">
</app-validation-message>
</div>
<div class="p-field p-col-12 p-lg-6">
<label for="alteryx-routine-id">
Alteryx Routine Name
</label>
<input id="alteryx-routine-id"
type="text"
pInputText
formControlName="alteryxRoutineId">
<app-validation-message [control]="verificationRuleForm.get('alteryxRoutineId')"
[validationMessage]="'This field is required.'"
[displayEvenIfPristine]="displayValidationMessagesEvenIfPristine">
</app-validation-message>
</div>
</div>
<div class="p-grid p-jc-center" *ngIf="editMode">
<div class="p-field p-col-5 p-m-2">
</div>
<div class="p-field p-col-5 p-m-2">
<p-checkbox id="verification-rule-status" name="verification-rule-status"
[formControl]="verificationRuleForm.get('verificationRuleStatus')" binary="true" label="Rule Status"
[disabled]="!canEditRuleStatus()">
</p-checkbox>
</div>
</div>
</div>
</div>

View File

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

View File

@ -1,527 +0,0 @@
import { Right } from './../../../../shared/models/right.interface';
import { environment } from './../../../../../environments/environment';
import { USER_RIGHTS } from './../../../../shared/enums/USER_RIGHTS.enum';
import { AuthService } from 'src/app/shared/services/auth.service';
import { CreateVerificationRuleFormValue } from './create-verification-rule-form-value.interface';
import { JournalVerificationsService } from './../../../../shared/services/administration/journal-verifications.service';
import { CapturingVerificationsService } from './../../../../shared/services/administration/capturing-verifications.service';
import { DocumentSubclassificationsService } from 'src/app/shared/services/administration/document-subclassifications.service';
import { IpowerClientsService } from 'src/app/shared/services/administration/ipower-clients.service';
import { CategoriesService } from 'src/app/shared/services/administration/categories.service';
import { DocumentClassificationsService } from 'src/app/shared/services/administration/document-classifications.service';
import { FormBuilder, Validators } from '@angular/forms';
import { DocumentSubclassification } from 'src/app/shared/models/document-subclassification.interface';
import { Component, Input, OnInit } from '@angular/core';
import { CapturingVerification } from '../../../../shared/models/capturing-verification.interface';
import { Category } from '../../../../shared/models/category.interface';
import { IPowerClient } from '../../../../shared/models/ipower-client.interface';
import { JournalVerification } from '../../../../shared/models/journal-verification.interface';
import { VerificationRule } from './../../../../shared/models/verification-rule.interface';
import { DocumentClassification } from '../../../../shared/models/document-classification.interface';
import { ErrorHandlingService } from 'src/app/shared/services/error-handling/error-handling.service';
import { Observable } from 'rxjs';
@Component({
selector: 'app-verification-rule-form',
templateUrl: './verification-rule-form.component.html',
styleUrls: ['./verification-rule-form.component.scss']
})
export class VerificationRuleFormComponent implements OnInit {
/*
* Inputs
*/
@Input() dialogLayout: boolean = false; // Controls .scss classes to allow for better dispay in a dialog.
@Input() requiredFields: string[] | boolean = false; // True/False indicates that all/none are required. String[] specifies the form controls required.
@Input() displayValidationMessagesEvenIfPristine: boolean = false; // Serves for manually treating the controls as dirty.
@Input() excludedFormControls: string[]; // Specifies the form controls to be removed and NOT displayed.
/*
* Reactive Form
*/
verificationRuleForm = this.fb.group({
documentClassification: [null],
ipowerName: [null],
ipowerCode: [null],
categoryName: [null],
categoryCode: [null],
subCategoryCode: [null], // This actually represents the complete DocumentSubclassification object.
confidenceLevelMinThreshold: [null],
capturingVerification: [null],
journalVerification: [null],
alteryxRoutineId: [null],
verificationRuleStatus: [true],
template: [null]
});
initiallySetFormValue: VerificationRule;
editMode: boolean = false;
/*
* Other Variables
*/
documentClassificationsList: DocumentClassification[];
categoryNameSuggestions: string[];
categoryCodeSuggestions: string[];
categorySuggestions: Category[];
selectedCategory: Category;
iPowerClientNameSuggestions: string[];
iPowerClientCodeSuggestions: string[];
iPowerClientSuggestions: IPowerClient[];
selectedIPowerClient: IPowerClient;
capturingVerificationsList: CapturingVerification[];
journalVerificationsList: JournalVerification[];
documentSubclassificationsList: DocumentSubclassification[];
availableDocumentSubclassifications: DocumentSubclassification[];
subCategoryCodeDisabled: boolean = true;
/*
* Constructor & Initialisers
*/
constructor(
private fb: FormBuilder,
private documentClassificationsService: DocumentClassificationsService,
private categoriesService: CategoriesService,
private iPowerClientsService: IpowerClientsService,
private documentSubclassificationsService: DocumentSubclassificationsService,
private capturingVerificationsService: CapturingVerificationsService,
private journalVerificationsService: JournalVerificationsService,
private authService: AuthService,
private errorHandlingService: ErrorHandlingService
) { }
ngOnInit(): void {
this.initData();
this.initValidators()
this.excludeFields();
}
initData() {
this.documentClassificationsService.getAll().subscribe(
value => this.documentClassificationsList = value,
err => this.errorHandlingService.showHttpResponseError(err)
);
// This is NOT the list of Subclassifications used for the dropdown.
this.documentSubclassificationsService.getAll().subscribe(
value => this.documentSubclassificationsList = value,
err => this.errorHandlingService.showHttpResponseError(err)
);
this.journalVerificationsService.getJournalVerifications().subscribe(
values => this.journalVerificationsList = values,
err => this.errorHandlingService.showHttpResponseError(err)
);
this.capturingVerificationsService.getCapturingVerifications().subscribe(
values => this.capturingVerificationsList = values,
err => this.errorHandlingService.showHttpResponseError(err)
);
}
// TODO: Have this mechanism offer the use of custom validators too.
initValidators() {
if (!this.requiredFields) { return; }
// In a true/false case.
if (typeof this.requiredFields == 'boolean') {
// If true, enable the required validator for all controls.
if (this.requiredFields) {
Object.keys(this.verificationRuleForm.controls).forEach(key => this.verificationRuleForm.controls[key].setValidators(Validators.required));
}
// If false, do nothing.
return;
}
// If it was a string array, enable the validators for all provided field-names.
(<string[]>this.requiredFields).forEach(field => this.verificationRuleForm.controls[field].setValidators(Validators.required))
}
excludeFields() {
if (!this.excludedFormControls) { return; }
this.excludedFormControls.forEach(field => this.verificationRuleForm.removeControl(field));
}
/*
* Auto-suggest & Auto-complete Category
*/
autosuggestCategoryName(event) {
if (!event.query || event.query.length < 3) {
this.categoryNameSuggestions = [];
return;
}
let classId = this.verificationRuleForm.get('documentClassification').value ? this.verificationRuleForm.get('documentClassification').value.classificationId : null;
this.categoriesService.autosuggestCategoryName(event.query, classId).subscribe(
(values: Category[]) => {
let temp: string[] = [];
this.categorySuggestions = values;
values.map(val => temp.push(val.categoryName));
this.categoryNameSuggestions = temp
},
err => this.errorHandlingService.showHttpResponseError(err)
);
}
autosuggestCategoryCode(event) {
if (event.query.length < 3) {
this.categoryCodeSuggestions = [];
return;
}
let classId = this.verificationRuleForm.get('documentClassification').value ? this.verificationRuleForm.get('documentClassification').value.classificationId : null;
this.categoriesService.autosuggestCategoryCode(event.query, classId).subscribe(
(values: Category[]) => {
let temp: string[] = [];
this.categorySuggestions = values;
values.map(val => temp.push(val.categoryCode));
this.categoryCodeSuggestions = temp
},
err => this.errorHandlingService.showHttpResponseError(err)
);
}
categoryNameSelected(name: string) {
this.selectedCategory = this.categorySuggestions.find(cat => cat.categoryName == name);
this.verificationRuleForm.get('categoryCode').patchValue(this.selectedCategory.categoryCode);
this.verificationRuleForm.updateValueAndValidity();
}
categoryCodeSelected(code: string) {
this.selectedCategory = this.categorySuggestions.find(cat => cat.categoryCode == code);
this.verificationRuleForm.get('categoryName').patchValue(this.selectedCategory.categoryName);
this.verificationRuleForm.updateValueAndValidity();
}
// /*
// * Auto-suggest & Auto-complete IPower Client
// */
// /*
// * Rights-check Warning:
// * If we are NOT in editMode (and are thus on "addMode"), we should only suggest clients for which we actually CAN add a new rule.
// * Note:
// * I know that if on editMode the client-input is disabled and thus this check is redundant,
// * but I think it is better to work with as an abstract perspective as possible, not entailing business logic that may either be unrelated or prone to changes.
// */
// autosuggestIPowerClientName(event) {
// if (!event.query || event.query.length < 3) {
// this.iPowerClientNameSuggestions = [];
// return;
// }
// // If the user has the right to preview all rules (B01), we use the endpoint that returns all iPowerClients,
// // otherwise, the one that checks for the user's User_Access too. Whether the user can see ANY rule has already been handled by the 'search' button.
// let endpointToSubscribeTo: Observable<IPowerClient[]> = USER_RIGHTS.B01.isGrantedToUser(
// this.authService.userRights.find(rbc => rbc.client.id == environment.globalRightsClientID)?.rights
// )
// ? this.iPowerClientsService.getClientsByNameOnly(event.query)
// : this.iPowerClientsService.getClientsByNameDistinct(event.query);
// endpointToSubscribeTo.subscribe(
// (values: IPowerClient[]) => {
// // ***See comment at method level***
// if (!this.editMode) {
// values = values.filter(client => USER_RIGHTS.B03.isGrantedToUser(
// this.authService.userRights.find(rdc => rdc.client.id == client.id)?.rights
// ));
// }
// let temp: string[] = [];
// this.iPowerClientSuggestions = values;
// values.map(val => temp.push(val.name));
// this.iPowerClientNameSuggestions = temp
// },
// err => this.errorHandlingService.showHttpResponseError(err)
// );
// }
// /*
// * Rights-check Warning:
// * If we are NOT in editMode (and are thus on "addMode"), we should only suggest clients for which we actually CAN add a new rule.
// * Note:
// * I know that if on editMode the client-input is disabled and thus this check is redundant,
// * but I think it is better to work with as an abstract perspective as possible, not entailing business logic that may either be unrelated or prone to changes.
// */
// autosuggestIPowerClientCode(event) {
// if (event.query.length < 3) {
// this.iPowerClientCodeSuggestions = [];
// return;
// }
// // If the user has the right to preview all rules (B01), we use the endpoint that returns all iPowerClients,
// // otherwise, the one that checks for the user's User_Access too. Whether the user can see ANY rule has already been handled by the 'search' button.
// let endpointToSubscribeTo: Observable<IPowerClient[]> = USER_RIGHTS.B01.isGrantedToUser(
// this.authService.userRights.find(rbc => rbc.client.id == environment.globalRightsClientID)?.rights
// )
// ? this.iPowerClientsService.getClientsByCodeOnly(event.query): null;
// endpointToSubscribeTo.subscribe(
// (values: IPowerClient[]) => {
// // ***See comment at method level***
// if (!this.editMode) {
// values = values;
// }
// let temp: string[] = [];
// this.iPowerClientSuggestions = values;
// values.map(val => temp.push(val.clientCode));
// this.iPowerClientCodeSuggestions = temp
// },
// err => this.errorHandlingService.showHttpResponseError(err)
// );
// }
/*
* Auto-suggest & Auto-complete IPower Client
*/
autosuggestIPowerClientName(event): void {
if (!event.query || event.query.length < 3) {
this.iPowerClientNameSuggestions = [];
return;
}
this.iPowerClientsService.getClientsByNameOnly(event.query).subscribe(
(values: IPowerClient[]) => {
this.iPowerClientSuggestions = values;
const temp: string[] = [];
values.map(val => temp.push(val.name));
this.iPowerClientNameSuggestions = temp;
},
err => this.errorHandlingService.showHttpResponseError(err)
);
}
autosuggestIPowerClientCode(event): void {
if (event.query.length < 3) {
this.iPowerClientCodeSuggestions = [];
return;
}
this.iPowerClientsService.getClientsByCodeOnly(event.query).subscribe(
(values: IPowerClient[]) => {
this.iPowerClientSuggestions = values;
const temp: string[] = [];
values.map(val => temp.push(val.clientCode));
this.iPowerClientCodeSuggestions = temp;
},
err => this.errorHandlingService.showHttpResponseError(err)
);
}
iPowerClientNameSelected(name: string) {
this.selectedIPowerClient = this.iPowerClientSuggestions.find(client => client.name == name);
this.verificationRuleForm.get('ipowerCode').patchValue(this.selectedIPowerClient.clientCode);
this.verificationRuleForm.updateValueAndValidity();
}
iPowerClientCodeSelected(code: string) {
this.selectedIPowerClient = this.iPowerClientSuggestions.find(client => client.clientCode == code);
this.verificationRuleForm.get('ipowerName').patchValue(this.selectedIPowerClient.name);
this.verificationRuleForm.updateValueAndValidity();
}
/*
* Other Methods
*/
documentClassificationSelected(selection) {
this.availableDocumentSubclassifications = this.documentSubclassificationsList.filter(element => element.documentClassification.classificationId == selection.value.classificationId);
this.verificationRuleForm.get('subCategoryCode').setValue(this.availableDocumentSubclassifications[0]);
this.subCategoryCodeDisabled = false;
}
/*
* Utility Methods
*/
initiateEditMode() {
// let controlsToDisableInEditMode = ['documentClassification', 'ipowerName', 'ipowerCode', 'categoryName', 'categoryCode', 'subCategoryCode']
// controlsToDisableInEditMode.forEach(ctrl => this.verificationRuleForm.get(ctrl).disable());
}
// Auto-suggest inputs - We have to ensure our reactive form holds values that represent the selectedCategory, otherwise truncate it.
syncSelectedCategory() {
// Ιf our form has no value, truncate the selectedCategory either way.
if (!this.verificationRuleForm.get('categoryName').value && !this.verificationRuleForm.get('categoryCode').value) {
this.selectedCategory = null;
return;
}
// If both or either of our form's values match the selectedCategory's and the other one doesn't have a value, all is good.
// Just sync the values in case one is missing. Otherwise truncate the selectedCategory.
if (
this.verificationRuleForm.get('categoryName').value == this.selectedCategory.categoryName
|| this.verificationRuleForm.get('categoryCode').value == this.selectedCategory.categoryCode
) {
this.selectedCategory.categoryName = this.verificationRuleForm.get('categoryName').value;
this.selectedCategory.categoryCode = this.verificationRuleForm.get('categoryCode').value;
}
// If both our values were different from the selectedCategory's, truncate it. This is an extremely abnormal scenario.
else {
console.error('WARNING - syncSelectedCategory()', 'Both of our form\'s values were different from the selectedCategory\'s.');
this.selectedCategory = null;
this.verificationRuleForm.get('categoryName').setValue('');
this.verificationRuleForm.get('categoryCode').setValue('');
this.verificationRuleForm.updateValueAndValidity();
}
}
// Auto-suggest inputs - We have to ensure our reactive form holds values that represent the selectedIPowerClient, otherwise truncate it.
syncSelectedIPowerClient() {
// Ιf our form has no value, truncate the selectedIPowerClient either way.
if (!this.verificationRuleForm.get('ipowerName').value && !this.verificationRuleForm.get('ipowerCode').value) {
this.selectedIPowerClient = null;
return;
}
// If both or either of our form's values match the selectedIPowerClient's and the other one doesn't have a value, all is good.
// Just sync the values in case one is missing. Otherwise truncate the selectedIPowerClient.
if (
this.verificationRuleForm.get('ipowerName').value == this.selectedIPowerClient.name
|| this.verificationRuleForm.get('ipowerCode').value == this.selectedIPowerClient.clientCode
) {
this.selectedIPowerClient.name = this.verificationRuleForm.get('ipowerName').value;
this.selectedIPowerClient.clientCode = this.verificationRuleForm.get('ipowerCode').value;
}
// If both our values were different from the selectedIPowerClient's, truncate it. This is an extremely abnormal scenario.
else {
console.error('WARNING - syncSelectedIPowerClient()', 'Both of our form\'s values were different from the selectedIPowerClient\'s.');
this.selectedIPowerClient = null;
this.verificationRuleForm.get('ipowerName').setValue('');
this.verificationRuleForm.get('ipowerCode').setValue('');
this.verificationRuleForm.updateValueAndValidity();
}
}
/*
* Rights-check Methods
*/
canEditRuleStatus(): boolean {
if (this.initiallySetFormValue) {
return this.authService.userHasRightForClient(USER_RIGHTS.B06, environment.globalRightsClientID);
}
return false;
}
// canEditConfidenceLevel(): boolean {
// if (this.initiallySetFormValue) {
// return this.authService.userHasRightForClient(USER_RIGHTS.B07, this.initiallySetFormValue.client.id);
// }
// return false;
// }
/*
* API methods
*/
public resetForm(): void {
this.verificationRuleForm.reset();
this.selectedCategory = null;
this.selectedIPowerClient = null;
this.editMode = null;
}
public formValue(): CreateVerificationRuleFormValue {
// We keep track of those two using object-variables separate from our ReactiveForm.
// Thus, we now have to make sure the ReactiveForm really has values that match those objects, before we forward them.
// Also, keep in mind that our auto-suggest inputs limit the user in choosing one of their values.
this.syncSelectedCategory();
this.syncSelectedIPowerClient();
let formValue: CreateVerificationRuleFormValue = {
id: this.initiallySetFormValue ? this.initiallySetFormValue.id : null,
confidenceLevelMinThreshold: this.verificationRuleForm.get('confidenceLevelMinThreshold').value,
clientId: this.selectedIPowerClient ? this.selectedIPowerClient.id : null,
categoryId: this.selectedCategory ? this.selectedCategory.id : null,
// The backend only requires the subcategory's name, and not the whole object. Don't ask me.
subCategoryCode: this.verificationRuleForm.get('subCategoryCode').value ? this.verificationRuleForm.get('subCategoryCode').value['subclassificationName'] : '',
capturingVerification: this.verificationRuleForm.get('capturingVerification').value,
journalVerification: this.verificationRuleForm.get('journalVerification').value,
alteryxRoutineId: this.verificationRuleForm.get('alteryxRoutineId').value,
template: this.verificationRuleForm.get('template').value,
// Convert 'verificationRuleStatus' from boolean to string.
verificationRuleStatus: this.verificationRuleForm.get('verificationRuleStatus').value ? 'Enabled' : 'Disabled'
};
return formValue;
}
public setValue(value: VerificationRule): void {
if (!value) {
return;
}
this.editMode = true;
this.initiateEditMode();
this.initiallySetFormValue = value
// If a documentClassification is already selected, enable the subclassification dropdown.
this.subCategoryCodeDisabled = !value.docClassificationCategory;
this.verificationRuleForm.get('documentClassification').setValue(value.docClassificationCategory);
// Having set the documentClassification -and provided there was one- we must also set the availableDocumentSubclassifications.
this.availableDocumentSubclassifications = this.documentSubclassificationsList.filter(element =>
element.documentClassification.classificationId == value.docClassificationCategory.classificationId
);
this.verificationRuleForm.get('ipowerName').setValue(value.client.name);
this.verificationRuleForm.get('ipowerCode').setValue(value.client.clientCode);
this.selectedIPowerClient = value.client;
this.verificationRuleForm.get('categoryName').setValue(value.template.category.categoryName);
this.verificationRuleForm.get('categoryCode').setValue(value.template.category.categoryCode);
this.selectedCategory = value.template.category;
this.verificationRuleForm.get('confidenceLevelMinThreshold').setValue(value.confidenceLevelMinThreshold);
this.verificationRuleForm.get('capturingVerification').setValue(value.capturingVerification);
this.verificationRuleForm.get('journalVerification').setValue(value.journalVerification);
this.verificationRuleForm.get('alteryxRoutineId').setValue(value.alteryxRoutineId);
this.verificationRuleForm.get('template').setValue(value.template);
// Convert 'verificationRuleStatus' from string to boolean.
this.verificationRuleForm.get('verificationRuleStatus').setValue(value.verificationRuleStatus.trim().toLowerCase() == 'enabled');
// To set the subcategory/subclassification we also have to make sure the documentClassification is the same.
// WARNING: Since we have enabled the [forceSelection] option of the <p-autoComplete>,
// if the availableDocumentSubclassifications are not set, documentSubclassification won't be displayed.
this.verificationRuleForm.get('subCategoryCode').setValue(
this.documentSubclassificationsList.find(subClass =>
subClass.subclassificationName == value.template.subCategoryCode && subClass.documentClassification.classificationId == value.docClassificationCategory.classificationId
)
);
this.verificationRuleForm.updateValueAndValidity();
}
public isValid(): boolean {
return this.verificationRuleForm.valid;
}
public initForCreation() {
this.editMode = false;
this.verificationRuleForm.get('verificationRuleStatus').setValue(true); // The default value in creation mode.
this.verificationRuleForm.get('template').setValidators(null); // 'template' is not required during the creation.
}
}

View File

@ -1,6 +0,0 @@
export interface VerificationRuleSearchFormValue {
clientId: string;
categoryId: number;
docClassificationId: number;
docSubcategory: string;
}

View File

@ -1,93 +0,0 @@
<div [formGroup]="verificationRuleForm">
<div class="p-fluid p-grid">
<div class="p-col-12 p-lg-8">
<div class="p-grid p-formgrid">
<div class="p-field p-col-12 p-lg-6">
<label for="ipower-name">
iPower Client Name
</label>
<p-autoComplete id="ipower-name"
formControlName="ipowerName"
[suggestions]="ipowerClientNameSuggestions"
(completeMethod)="autosuggestIPowerClientName($event)"
(onSelect)="ipowerClientNameSelected($event)"
[forceSelection]="true">
</p-autoComplete>
</div>
<div class="p-field p-col-12 p-lg-6">
<label for="ipower-code">
iPower Client Code
</label>
<p-autoComplete id="ipower-code"
formControlName="ipowerCode"
[suggestions]="ipowerClientCodeSuggestions"
(completeMethod)="autosuggestIPowerClientCode($event)"
(onSelect)="ipowerClientCodeSelected($event)"
[forceSelection]="true">
</p-autoComplete>
</div>
<div class="p-field p-col-12 p-lg-6">
<label for="category-name">
Category Name
</label>
<p-autoComplete id="category-name"
formControlName="categoryName"
[suggestions]="categoryNameSuggestions"
(completeMethod)="autosuggestCategoryName($event)"
(onSelect)="categoryNameSelected($event)"
[forceSelection]="true">
</p-autoComplete>
</div>
<div class="p-field p-col-12 p-lg-6">
<label for="category-code">
Category Code
</label>
<p-autoComplete id="category-code"
formControlName="categoryCode"
[suggestions]="categoryCodeSuggestions"
(completeMethod)="autosuggestCategoryCode($event)"
(onSelect)="categoryCodeSelected($event)"
[forceSelection]="true">
</p-autoComplete>
</div>
</div>
</div>
<div class="p-col-12 p-lg-4">
<div class="p-grid p-formgrid">
<div class="p-field p-col-12">
<label for="document-classification">
Document Classification
</label>
<p-dropdown inputId="document-classification"
[options]="documentClassificationsList"
optionLabel="classificationName"
placeholder="Select"
formControlName="documentClassification"
(onChange)="documentClassificationSelected($event.value)"
[showClear]="true">
</p-dropdown>
</div>
<div class="p-field p-col-12">
<label for="subCategoryCode">
Document Subclassification
</label>
<p-dropdown inputId="subCategoryCode"
[options]="availableDocumentSubclassifications"
optionLabel="subclassificationName"
placeholder="Select"
formControlName="subCategoryCode"
(onChange)="documentSubclassificationSelected($event.value)"
[showClear]="true">
</p-dropdown>
</div>
</div>
</div>
</div>
</div>

View File

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

View File

@ -1,358 +0,0 @@
import { VerificationRulesService } from './../../../../shared/services/administration/verification-rules.service';
import { VerificationRule } from './../../../../shared/models/verification-rule.interface';
import { Component, Input, OnInit } from '@angular/core';
import { AbstractControl, FormBuilder } from '@angular/forms';
import { Category } from 'src/app/shared/models/category.interface';
import { DocumentClassification } from 'src/app/shared/models/document-classification.interface';
import { DocumentSubclassification } from 'src/app/shared/models/document-subclassification.interface';
import { IPowerClient } from 'src/app/shared/models/ipower-client.interface';
import { CategoriesService } from 'src/app/shared/services/administration/categories.service';
import { DocumentClassificationsService } from 'src/app/shared/services/administration/document-classifications.service';
import { DocumentSubclassificationsService } from 'src/app/shared/services/administration/document-subclassifications.service';
import { IpowerClientsService } from 'src/app/shared/services/administration/ipower-clients.service';
import { VerificationRuleSearchFormValue } from './verification-rule-search-form-value.interface';
import { ErrorHandlingService } from 'src/app/shared/services/error-handling/error-handling.service';
import { USER_RIGHTS } from 'src/app/shared/enums/USER_RIGHTS.enum';
import { AuthService } from 'src/app/shared/services/auth.service';
import { Observable } from 'rxjs';
import { environment } from 'src/environments/environment';
@Component({
selector: 'app-verification-rule-search-form',
templateUrl: './verification-rule-search-form.component.html',
styleUrls: ['./verification-rule-search-form.component.scss']
})
export class VerificationRuleSearchFormComponent implements OnInit {
@Input() disabled: boolean = false;
documentClassificationsList: DocumentClassification[];
documentSubclassificationsList: DocumentSubclassification[];
availableDocumentSubclassifications: DocumentSubclassification[];
previouslySelectedClassificationId: number;
categoryNameSuggestions: string[];
categoryCodeSuggestions: string[];
categorySuggestions: Category[];
selectedCategory: Category = null;
ipowerClientNameSuggestions: string[];
ipowerClientCodeSuggestions: string[];
ipowerClientSuggestions: IPowerClient[];
selectedIPowerClient: IPowerClient = null;
displayValidationMessagesEvenIfPristine: boolean;
verificationRuleForm = this.fb.group({
ipowerName: [null],
ipowerCode: [null],
categoryName: [null],
categoryCode: [null],
documentClassification: [null],
subCategoryCode: [null] // This actually represents the complete DocumentSubclassification object.
});
constructor(
private fb: FormBuilder,
private documentClassificationsService: DocumentClassificationsService,
private documentSubclassificationsService: DocumentSubclassificationsService,
private verificationRulesService: VerificationRulesService,
private categoriesService: CategoriesService,
private ipowerClientsService: IpowerClientsService,
private errorHandlingService: ErrorHandlingService,
private authService: AuthService
) { }
ngOnInit(): void {
if (this.disabled) {
Object.keys(this.verificationRuleForm.controls).forEach(ctrl => this.verificationRuleForm.get(ctrl).disable());
return; // Don't even bother initialising or requesting anything.
}
this.initData();
}
initData() {
this.documentClassificationsService.getAll().subscribe(
value => this.documentClassificationsList = value,
err => this.errorHandlingService.showHttpResponseError(err)
);
// This is NOT the list of Subclassifications used for the dropdown.
this.documentSubclassificationsService.getAll().subscribe(
value => {
this.documentSubclassificationsList = value;
this.availableDocumentSubclassifications = value;
},
err => this.errorHandlingService.showHttpResponseError(err)
);
}
clear() {
this.verificationRuleForm.reset();
this.selectedCategory = null;
this.selectedIPowerClient = null;
}
documentClassificationSelected(selection: DocumentClassification) {
// If a different Classification has been selected, reset the Subclassification's value.
if (this.previouslySelectedClassificationId && selection.classificationId != this.previouslySelectedClassificationId) {
this.verificationRuleForm.get('subCategoryCode').reset();
}
this.previouslySelectedClassificationId = this.verificationRuleForm.get('documentClassification').value.classificationId;
this.availableDocumentSubclassifications = this.documentSubclassificationsList.filter(element => element.documentClassification.classificationId == selection.classificationId);
}
documentSubclassificationSelected(selection: DocumentSubclassification) {
this.verificationRuleForm.get('documentClassification').setValue(selection.documentClassification);
}
/*
* Auto-suggest/complete Categories
*/
autosuggestCategoryName(event) {
if (!event.query || event.query.length < 3) {
this.categoryNameSuggestions = [];
return;
}
let classId = this.verificationRuleForm.get('documentClassification').value ? this.verificationRuleForm.get('documentClassification').value.classificationId : null;
this.categoriesService.autosuggestCategoryName(event.query, classId).subscribe(
(values: Category[]) => {
let temp: string[] = [];
this.categorySuggestions = values;
values.map(val => temp.push(val.categoryName));
this.categoryNameSuggestions = temp
},
err => this.errorHandlingService.showHttpResponseError(err)
);
}
autosuggestCategoryCode(event) {
if (event.query.length < 3) {
this.categoryCodeSuggestions = [];
return;
}
let classId = this.verificationRuleForm.get('documentClassification').value ? this.verificationRuleForm.get('documentClassification').value.classificationId : null;
this.categoriesService.autosuggestCategoryCode(event.query, classId).subscribe(
(values: Category[]) => {
let temp: string[] = [];
this.categorySuggestions = values;
values.map(val => temp.push(val.categoryCode));
this.categoryCodeSuggestions = temp
},
err => this.errorHandlingService.showHttpResponseError(err)
);
}
categoryNameSelected(name: string) {
this.selectedCategory = this.categorySuggestions.find(cat => cat.categoryName == name);
this.verificationRuleForm.get('categoryCode').patchValue(this.selectedCategory.categoryCode);
this.verificationRuleForm.updateValueAndValidity();
}
categoryCodeSelected(code: string) {
this.selectedCategory = this.categorySuggestions.find(cat => cat.categoryCode == code);
this.verificationRuleForm.get('categoryName').patchValue(this.selectedCategory.categoryName);
this.verificationRuleForm.updateValueAndValidity();
}
// /*
// * Auto-suggest/complete iPower Clients
// */
// autosuggestIPowerClientName(event) {
// if (!event.query || event.query.length < 3) {
// this.ipowerClientNameSuggestions = [];
// return;
// }
// this.ipowerClientsService.getClientsByNameDistinct(event.query).subscribe(
// (values: IPowerClient[]) => {
// let temp: string[] = [];
// this.ipowerClientSuggestions = values;
// values.map(val => temp.push(val.name));
// this.ipowerClientNameSuggestions = temp
// },
// err => this.errorHandlingService.showHttpResponseError(err)
// );
// }
// autosuggestIPowerClientCode(event) {
// if (!event.query || event.query.length < 3) {
// this.ipowerClientCodeSuggestions = [];
// return;
// }
// this.ipowerClientsService.getClientsByCodeDistinct(event.query).subscribe(
// (values: IPowerClient[]) => {
// let temp: string[] = [];
// this.ipowerClientSuggestions = values;
// values.map(val => temp.push(val.clientCode));
// this.ipowerClientCodeSuggestions = temp
// },
// err => this.errorHandlingService.showHttpResponseError(err)
// );
// }
autosuggestIPowerClientCode(event) {
if (event.query.length < 3) {
this.ipowerClientCodeSuggestions = [];
return;
}
// If the user has the right to Preview of Scheduling Procedure (A02), we use the endpoint that returns all iPowerClients,
// otherwise, the one that checks for the user's User_Access too. Whether the user can see ANY rule has already been handled by the 'search' button.
let endpointToSubscribeTo: Observable<IPowerClient[]> = this.authService.userHasRightForClient(USER_RIGHTS.B01, environment.globalRightsClientID)
? this.ipowerClientsService.getClientsByCodeOnly(event.query)
: this.authService.userRights.find(rdc => USER_RIGHTS.B02.isGrantedToUser(rdc.rights)) != null ? this.ipowerClientsService.getClientsByCodeDistinct(event.query) : null;
endpointToSubscribeTo.subscribe(
(values: IPowerClient[]) => {
let temp: string[] = [];
this.ipowerClientSuggestions = values;
values.map(val => temp.push(val.clientCode));
this.ipowerClientCodeSuggestions = temp
},
err => this.errorHandlingService.showHttpResponseError(err)
);
}
/*
* Auto-suggest & Auto-complete IPower Client
*/
autosuggestIPowerClientName(event): void {
if (!event.query || event.query.length < 3) {
this.ipowerClientNameSuggestions = [];
return;
}
// If the user has the right to Preview of Scheduling Procedure (A02), we use the endpoint that returns all iPowerClients,
// otherwise, the one that checks for the user's User_Access too. Whether the user can see ANY rule has already been handled by the 'search' button.
let endpointToSubscribeTo: Observable<IPowerClient[]> = this.authService.userHasRightForClient(USER_RIGHTS.B01, environment.globalRightsClientID)
? this.ipowerClientsService.getClientsByNameOnly(event.query)
: this.authService.userRights.find(rdc => USER_RIGHTS.B02.isGrantedToUser(rdc.rights)) != null ? this.ipowerClientsService.getClientsByNameDistinct(event.query) : null;
endpointToSubscribeTo.subscribe(
(values: IPowerClient[]) => {
this.ipowerClientSuggestions = values;
const temp: string[] = [];
values.map(val => temp.push(val.name));
this.ipowerClientNameSuggestions = temp;
},
err => this.errorHandlingService.showHttpResponseError(err)
);
}
ipowerClientNameSelected(name: string) {
this.selectedIPowerClient = this.ipowerClientSuggestions.find(client => client.name == name);
this.verificationRuleForm.get('ipowerCode').patchValue(this.selectedIPowerClient.clientCode);
this.verificationRuleForm.updateValueAndValidity();
}
ipowerClientCodeSelected(code: string) {
this.selectedIPowerClient = this.ipowerClientSuggestions.find(client => client.clientCode == code);
this.verificationRuleForm.get('ipowerName').patchValue(this.selectedIPowerClient.name);
this.verificationRuleForm.updateValueAndValidity();
}
/*
* Utility Methods
*/
// Auto-suggest inputs - We have to ensure our reactive form holds values that represent the selectedCategory, otherwise truncate it.
syncSelectedCategory() {
// Ιf our form has no value, truncate the selectedCategory either way.
if (!this.verificationRuleForm.get('categoryName').value && !this.verificationRuleForm.get('categoryCode').value) {
this.selectedCategory = null;
return;
}
// If both or either of our form's values match the selectedCategory's and the other one doesn't have a value, all is good.
// Just sync the values in case one is missing. Otherwise truncate the selectedCategory.
if (
this.verificationRuleForm.get('categoryName').value == this.selectedCategory.categoryName
|| this.verificationRuleForm.get('categoryCode').value == this.selectedCategory.categoryCode
) {
this.selectedCategory.categoryName = this.verificationRuleForm.get('categoryName').value;
this.selectedCategory.categoryCode = this.verificationRuleForm.get('categoryCode').value;
}
// If both our values were different from the selectedCategory's, truncate it. This is an extremely abnormal scenario.
else {
console.error('WARNING - syncSelectedCategory()', 'Both of our form\'s values were different from the selectedCategory\'s.');
this.selectedCategory = null;
this.verificationRuleForm.get('categoryName').setValue('');
this.verificationRuleForm.get('categoryCode').setValue('');
this.verificationRuleForm.updateValueAndValidity();
}
}
// Auto-suggest inputs - We have to ensure our reactive form holds values that represent the selectedIPowerClient, otherwise truncate it.
syncSelectedIPowerClient() {
// Ιf our form has no value, truncate the selectedIPowerClient either way.
if (!this.verificationRuleForm.get('ipowerName').value && !this.verificationRuleForm.get('ipowerCode').value) {
this.selectedIPowerClient = null;
return;
}
// If both or either of our form's values match the selectedIPowerClient's and the other one doesn't have a value, all is good.
// Just sync the values in case one is missing. Otherwise truncate the selectedIPowerClient.
if (
this.verificationRuleForm.get('ipowerName').value == this.selectedIPowerClient.name
|| this.verificationRuleForm.get('ipowerCode').value == this.selectedIPowerClient.clientCode
) {
this.selectedIPowerClient.name = this.verificationRuleForm.get('ipowerName').value;
this.selectedIPowerClient.clientCode = this.verificationRuleForm.get('ipowerCode').value;
}
// If both our values were different from the selectedIPowerClient's, truncate it. This is an extremely abnormal scenario.
else {
console.error('WARNING - syncSelectedIPowerClient()', 'Both of our form\'s values were different from the selectedIPowerClient\'s.');
this.selectedIPowerClient = null;
this.verificationRuleForm.get('ipowerName').setValue('');
this.verificationRuleForm.get('ipowerCode').setValue('');
this.verificationRuleForm.updateValueAndValidity();
}
}
/*
* API methods
*/
public resetForm(): void {
this.verificationRuleForm.reset();
}
// TODO: Set type
public formValue(): VerificationRuleSearchFormValue {
this.syncSelectedCategory();
this.syncSelectedIPowerClient();
let formValue: VerificationRuleSearchFormValue = {
clientId: this.selectedIPowerClient ? this.selectedIPowerClient.id : '',
categoryId: this.selectedCategory ? this.selectedCategory.id : null,
docClassificationId: this.verificationRuleForm.get('documentClassification').value ? this.verificationRuleForm.get('documentClassification').value.classificationId : '',
// The backend only requires the subcategory's name, and not the whole object. Don't ask me.
docSubcategory: this.verificationRuleForm.get('subCategoryCode').value ? this.verificationRuleForm.get('subCategoryCode').value['subclassificationName'] : ''
}
return formValue;
}
public isValid(): boolean {
return this.verificationRuleForm.valid;
}
}

Some files were not shown because too many files have changed in this diff Show More