├── .dockerignore ├── .editorconfig ├── .gitignore ├── .prettierignore ├── .prettierrc ├── .travis.yml ├── .vscode └── launch.json ├── Dockerfile ├── README.md ├── angular.json ├── docker-compose.yaml ├── e2e ├── app.e2e-spec.ts ├── app.po.ts └── tsconfig.e2e.json ├── karma.conf.js ├── nginx └── nginx.conf.template ├── package-lock.json ├── package.json ├── protractor.conf.js ├── proxy.conf.js ├── replace.build.js ├── src ├── app │ ├── Services │ │ ├── authentication │ │ │ ├── authentication.module.ts │ │ │ ├── authentication.service.spec.ts │ │ │ └── authentication.service.ts │ │ ├── constants.ts │ │ ├── element-editor.service.spec.ts │ │ ├── element-editor.service.ts │ │ ├── form-list.service.ts │ │ ├── http-interceptor.service.ts │ │ ├── navigator.service.spec.ts │ │ ├── navigator.service.ts │ │ ├── openmrs-api │ │ │ ├── concept.service.spec.ts │ │ │ ├── concept.service.ts │ │ │ ├── encounter-type.service.spec.ts │ │ │ ├── encounter-type.service.ts │ │ │ ├── encounters.service.ts │ │ │ ├── fetch-all-forms.service.spec.ts │ │ │ ├── fetch-all-forms.service.ts │ │ │ ├── fetch-form-detail.service.spec.ts │ │ │ ├── fetch-form-detail.service.ts │ │ │ ├── location-resource.service.ts │ │ │ ├── patient-resource.service.ts │ │ │ └── save-form.service.ts │ │ ├── order.service.spec.ts │ │ ├── order.service.ts │ │ ├── question-control.service.spec.ts │ │ ├── question-control.service.ts │ │ ├── question-id.service.spec.ts │ │ ├── question-id.service.ts │ │ ├── route-guards │ │ │ ├── auth-guard.service.spec.ts │ │ │ ├── auth-guard.service.ts │ │ │ ├── save-forms-guard.service.spec.ts │ │ │ └── save-forms-guard.service.ts │ │ ├── schema-compiler.service.ts │ │ ├── storage │ │ │ ├── local-storage.service.ts │ │ │ ├── session-storage.service.ts │ │ │ └── session.service.ts │ │ ├── update-component.service.spec.ts │ │ └── update-component.service.ts │ ├── app-entry-point │ │ ├── form-builder.component.css │ │ ├── form-builder.component.html │ │ ├── form-builder.component.spec.ts │ │ └── form-builder.component.ts │ ├── app-material-module.ts │ ├── app-routing.module.ts │ ├── app.component.css │ ├── app.component.html │ ├── app.component.spec.ts │ ├── app.component.ts │ ├── app.module.ts │ ├── build-version-footer │ │ ├── build-version-footer.component.css │ │ ├── build-version-footer.component.html │ │ ├── build-version-footer.component.spec.ts │ │ └── build-version-footer.component.ts │ ├── collections │ │ └── linked-list.ts │ ├── form-editor │ │ ├── audit-info │ │ │ ├── audit-info.component.css │ │ │ ├── audit-info.component.html │ │ │ ├── audit-info.component.spec.ts │ │ │ └── audit-info.component.ts │ │ ├── concept │ │ │ ├── concept.component.css │ │ │ ├── concept.component.html │ │ │ ├── concept.component.spec.ts │ │ │ └── concept.component.ts │ │ ├── element-editor │ │ │ ├── dynamic-question │ │ │ │ ├── dynamic-question.component.css │ │ │ │ ├── dynamic-question.component.html │ │ │ │ ├── dynamic-question.component.spec.ts │ │ │ │ └── dynamic-question.component.ts │ │ │ ├── element-editor.component.css │ │ │ ├── element-editor.component.html │ │ │ ├── element-editor.component.spec.ts │ │ │ └── element-editor.component.ts │ │ ├── form-editor │ │ │ ├── assets │ │ │ │ ├── ampath-logo.png │ │ │ │ └── loader.gif │ │ │ ├── form-editor-menu.component.ts │ │ │ ├── form-editor-menu.html │ │ │ ├── form-editor.component.css │ │ │ ├── form-editor.component.html │ │ │ ├── form-editor.component.spec.ts │ │ │ ├── form-editor.component.ts │ │ │ └── form-editor.module.ts │ │ ├── form-elements │ │ │ ├── Form.ts │ │ │ ├── FormElement.ts │ │ │ ├── Page.ts │ │ │ ├── Question.ts │ │ │ ├── Section.ts │ │ │ ├── form-element-factory.spec.ts │ │ │ ├── form-element-factory.ts │ │ │ ├── form-factory.service.spec.ts │ │ │ └── form-factory.service.ts │ │ ├── form-renderer │ │ │ ├── form-renderer.component.css │ │ │ ├── form-renderer.component.html │ │ │ ├── form-renderer.component.spec.ts │ │ │ ├── form-renderer.component.ts │ │ │ ├── mock-data-source.service.ts │ │ │ └── mock-obs.ts │ │ ├── models │ │ │ ├── properties.ts │ │ │ ├── property-factory.spec.ts │ │ │ ├── property-factory.ts │ │ │ ├── property-model.ts │ │ │ ├── searchbox-property.ts │ │ │ ├── select-property.ts │ │ │ ├── textarea-property.ts │ │ │ └── textbox-property.ts │ │ ├── navigator │ │ │ ├── navigator.component.css │ │ │ ├── navigator.component.html │ │ │ ├── navigator.component.spec.ts │ │ │ └── navigator.component.ts │ │ ├── order │ │ │ ├── order.component.css │ │ │ ├── order.component.html │ │ │ ├── order.component.spec.ts │ │ │ └── order.component.ts │ │ ├── reference-forms │ │ │ ├── reference-form-model.ts │ │ │ ├── reference-forms.component.css │ │ │ ├── reference-forms.component.html │ │ │ ├── reference-forms.component.spec.ts │ │ │ └── reference-forms.component.ts │ │ ├── schema-editor │ │ │ ├── schema-editor.component.css │ │ │ ├── schema-editor.component.html │ │ │ ├── schema-editor.component.spec.ts │ │ │ └── schema-editor.component.ts │ │ ├── snackbar │ │ │ ├── notification-toast.ts │ │ │ ├── saved-snackbar.ts │ │ │ └── snackbar.component.ts │ │ ├── update-forms-wizard │ │ │ ├── update-forms-wizard.component.css │ │ │ ├── update-forms-wizard.component.html │ │ │ ├── update-forms-wizard.component.spec.ts │ │ │ └── update-forms-wizard.component.ts │ │ └── update-forms │ │ │ ├── update-forms.component.css │ │ │ ├── update-forms.component.html │ │ │ ├── update-forms.component.spec.ts │ │ │ └── update-forms.component.ts │ ├── login │ │ ├── login.component.css │ │ ├── login.component.html │ │ ├── login.component.spec.ts │ │ └── login.component.ts │ ├── modals │ │ ├── alert.component.ts │ │ ├── answers-modal │ │ │ ├── answers-modal.css │ │ │ ├── answers-modal.html │ │ │ └── answers.modal.ts │ │ ├── concept.modal.ts │ │ ├── confirm.component.ts │ │ ├── encounter-details.modal.ts │ │ ├── encounter-viewer-modal.ts │ │ ├── insert-reference-form-modal │ │ │ ├── insert-reference-forms.modal.css │ │ │ ├── insert-reference-forms.modal.html │ │ │ └── insert-reference-forms.modal.ts │ │ ├── modals-module.ts │ │ ├── navigator.modal.ts │ │ ├── prompt.component.ts │ │ ├── reference-form-modal │ │ │ ├── reference-form.madal.css │ │ │ ├── reference-form.modal.html │ │ │ └── reference-form.modal.ts │ │ ├── save-form-modal │ │ │ ├── save-form-modal.css │ │ │ ├── save-form-modal.html │ │ │ └── save-form-modal.ts │ │ ├── schema-editor.modal.ts │ │ ├── set-members-modal │ │ │ ├── set-members-modal.component.css │ │ │ ├── set-members-modal.component.html │ │ │ ├── set-members-modal.component.spec.ts │ │ │ └── set-members-modal.component.ts │ │ └── update-forms-wizard-modal │ │ │ ├── update-forms-wizard-modal.component.css │ │ │ ├── update-forms-wizard-modal.component.html │ │ │ ├── update-forms-wizard-modal.component.spec.ts │ │ │ └── update-forms-wizard-modal.component.ts │ ├── pipes │ │ ├── filter.pipe.spec.ts │ │ ├── filter.pipe.ts │ │ ├── search-form-filter.pipe.spec.ts │ │ ├── search-form-filter.pipe.ts │ │ └── string_to_number.pipe.ts │ ├── shared-module.ts │ └── view-forms │ │ ├── view-forms.component.css │ │ ├── view-forms.component.html │ │ ├── view-forms.component.spec.ts │ │ └── view-forms.component.ts ├── assets │ ├── .gitkeep │ ├── ampath-forms-logo.svg │ ├── ampath-logo.svg │ ├── inter.woff │ ├── loader.gif │ ├── logo.svg │ ├── roboto-mono.woff │ ├── screen.png │ └── select.svg ├── environments │ ├── environment.prod.ts │ └── environment.ts ├── favicon.ico ├── index.html ├── main.ts ├── polyfills.ts ├── styles.scss ├── test.ts ├── tsconfig.app.json ├── tsconfig.spec.json ├── typings.d.ts └── version.json ├── tsconfig.json └── tslint.json /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | 8 | # dependencies 9 | /node_modules 10 | 11 | # IDEs and editors 12 | /.idea 13 | .project 14 | .classpath 15 | .c9/ 16 | *.launch 17 | .settings/ 18 | *.sublime-workspace 19 | .editorconfig 20 | 21 | # IDE - VSCode 22 | .vscode/* 23 | !.vscode/settings.json 24 | !.vscode/tasks.json 25 | !.vscode/launch.json 26 | !.vscode/extensions.json 27 | 28 | # misc 29 | /.sass-cache 30 | /connect.lock 31 | /coverage 32 | /libpeerconnection.log 33 | npm-debug.log 34 | testem.log 35 | /typings 36 | 37 | # e2e 38 | /e2e/*.js 39 | /e2e/*.map 40 | 41 | # System Files 42 | .DS_Store 43 | Thumbs.db 44 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | 8 | # dependencies 9 | /node_modules 10 | 11 | # IDEs and editors 12 | /.idea 13 | .project 14 | .classpath 15 | .c9/ 16 | *.launch 17 | .settings/ 18 | *.sublime-workspace 19 | 20 | # IDE - VSCode 21 | .vscode/* 22 | !.vscode/settings.json 23 | !.vscode/tasks.json 24 | !.vscode/launch.json 25 | !.vscode/extensions.json 26 | 27 | # misc 28 | /.sass-cache 29 | /connect.lock 30 | /coverage 31 | /libpeerconnection.log 32 | npm-debug.log 33 | yarn-error.log 34 | testem.log 35 | /typings 36 | 37 | # System Files 38 | .DS_Store 39 | Thumbs.db 40 | ./package-lock.json 41 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "none", 3 | "singleQuote": true, 4 | "tabWidth": 2, 5 | "semi": true 6 | } 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | dist: xenial 3 | node_js: 4 | - '10' 5 | services: 6 | - docker 7 | - xvfb 8 | before_install: 9 | - npm i -g brfs 10 | script: 11 | - npm run build 12 | sudo: required 13 | addons: 14 | apt: 15 | packages: 16 | - sshpass 17 | - google-chrome-stable 18 | after_success: 19 | - mv dist "fb-${TRAVIS_BRANCH}" 20 | - tar -czf build.tgz "fb-${TRAVIS_BRANCH}" 21 | - export SSHPASS=$DEPLOY_PASS 22 | - sshpass -e scp -o stricthostkeychecking=no build.tgz $DEPLOY_USER@$DEPLOY_HOST:./ 23 | - sshpass -e ssh -o stricthostkeychecking=no $DEPLOY_USER@$DEPLOY_HOST ./deploy-build.sh 24 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "chrome", 6 | "request": "launch", 7 | "name": "Launch Chrome against localhost", 8 | "url": "http://localhost:4200", 9 | "webRoot": "${workspaceRoot}" 10 | }, 11 | { 12 | "type": "chrome", 13 | "request": "attach", 14 | "name": "Attach to Chrome", 15 | "port": 9222, 16 | "webRoot": "${workspaceRoot}" 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:12 as build-step 2 | RUN mkdir -p /app 3 | WORKDIR /app 4 | COPY package.json package-lock.json /app/ 5 | RUN npm install 6 | COPY . /app 7 | RUN npm run build --prod 8 | 9 | FROM nginx:1.19.2-alpine 10 | COPY ./nginx/nginx.conf.template /etc/nginx/templates/default.conf.template 11 | COPY --from=build-step /app/dist /usr/share/nginx/html -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | AMPATH logo 3 |

4 |
5 |
6 |

7 | Screenshot showing the Schema Editor of the AMPATH Form Builder 8 |

9 | 10 | # AMPATH Form Builder 11 | 12 | The AMPATH Form Builder is a tool that helps you build and render OpenMRS form schemas. It enables users to build form schemas of arbitrary complexity. These schemas can be built interactively or by writing JSON code inside the Schema editor. Once built, the form engine processes and renders these schemas. Users can test their forms within the form builder as well as saving them either locally or to a server. Once published, these forms can be made available to a frontend for data entry. 13 | 14 | ## Getting Started / Documentation 15 | 16 | Visit https://ampath-forms.vercel.app to view the full documentation. 17 | 18 | ## Contributing 19 | 20 | Read our [Contributing](https://ampath-forms.vercel.app/docs/developer-guide/contributing-guide) guide. 21 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "form-editor": { 7 | "root": "", 8 | "sourceRoot": "src", 9 | "projectType": "application", 10 | "architect": { 11 | "build": { 12 | "builder": "@angular-devkit/build-angular:browser", 13 | "options": { 14 | "outputPath": "dist", 15 | "index": "src/index.html", 16 | "main": "src/main.ts", 17 | "tsConfig": "src/tsconfig.app.json", 18 | "polyfills": "src/polyfills.ts", 19 | "assets": ["src/assets", "src/favicon.ico"], 20 | "styles": ["src/styles.scss"], 21 | "scripts": [] 22 | }, 23 | "configurations": { 24 | "production": { 25 | "optimization": true, 26 | "outputHashing": "all", 27 | "sourceMap": false, 28 | "extractCss": true, 29 | "namedChunks": false, 30 | "aot": true, 31 | "extractLicenses": true, 32 | "vendorChunk": false, 33 | "buildOptimizer": true, 34 | "fileReplacements": [ 35 | { 36 | "replace": "src/environments/environment.ts", 37 | "with": "src/environments/environment.prod.ts" 38 | } 39 | ] 40 | } 41 | } 42 | }, 43 | "serve": { 44 | "builder": "@angular-devkit/build-angular:dev-server", 45 | "options": { 46 | "browserTarget": "form-editor:build" 47 | }, 48 | "configurations": { 49 | "production": { 50 | "browserTarget": "form-editor:build:production" 51 | } 52 | } 53 | }, 54 | "extract-i18n": { 55 | "builder": "@angular-devkit/build-angular:extract-i18n", 56 | "options": { 57 | "browserTarget": "form-editor:build" 58 | } 59 | }, 60 | "test": { 61 | "builder": "@angular-devkit/build-angular:karma", 62 | "options": { 63 | "main": "src/test.ts", 64 | "karmaConfig": "./karma.conf.js", 65 | "polyfills": "src/polyfills.ts", 66 | "tsConfig": "src/tsconfig.spec.json", 67 | "scripts": [], 68 | "styles": ["src/styles.scss"], 69 | "assets": ["src/assets", "src/favicon.ico"] 70 | } 71 | }, 72 | "lint": { 73 | "builder": "@angular-devkit/build-angular:tslint", 74 | "options": { 75 | "tsConfig": ["src/tsconfig.app.json", "src/tsconfig.spec.json"], 76 | "exclude": [] 77 | } 78 | } 79 | } 80 | }, 81 | "form-editor-e2e": { 82 | "root": "e2e", 83 | "sourceRoot": "e2e", 84 | "projectType": "application", 85 | "architect": { 86 | "e2e": { 87 | "builder": "@angular-devkit/build-angular:protractor", 88 | "options": { 89 | "protractorConfig": "./protractor.conf.js", 90 | "devServerTarget": "form-editor:serve" 91 | } 92 | }, 93 | "lint": { 94 | "builder": "@angular-devkit/build-angular:tslint", 95 | "options": { 96 | "tsConfig": ["e2e/tsconfig.e2e.json"], 97 | "exclude": [] 98 | } 99 | } 100 | } 101 | } 102 | }, 103 | "defaultProject": "form-editor", 104 | "schematics": { 105 | "@schematics/angular:component": { 106 | "prefix": "app", 107 | "styleext": "css" 108 | }, 109 | "@schematics/angular:directive": { 110 | "prefix": "app" 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /docker-compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | formbuilder: 3 | image: ng2-openmrs-formbuilder 4 | ports: 5 | - "4200:80" 6 | environment: 7 | - OPENMRS_HOST_URL=${OPENMRS_HOST_URL} -------------------------------------------------------------------------------- /e2e/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { FormEditorPage } from './app.po'; 2 | 3 | describe('form-editor App', () => { 4 | let page: FormEditorPage; 5 | 6 | beforeEach(() => { 7 | page = new FormEditorPage(); 8 | }); 9 | 10 | it('should display welcome message', () => { 11 | page.navigateTo(); 12 | expect(page.getParagraphText()).toEqual('Welcome to app!'); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /e2e/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class FormEditorPage { 4 | navigateTo() { 5 | return browser.get('/'); 6 | } 7 | 8 | getParagraphText() { 9 | return element(by.css('app-root h1')).getText(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /e2e/tsconfig.e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/e2e", 5 | "baseUrl": "./", 6 | "module": "commonjs", 7 | "target": "es5", 8 | "types": ["jasmine", "jasminewd2", "node"] 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/0.13/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, 'coverage'), 20 | reports: ['html', 'lcovonly'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | 24 | reporters: ['progress', 'kjhtml'], 25 | port: 9876, 26 | colors: true, 27 | logLevel: config.LOG_INFO, 28 | autoWatch: true, 29 | browsers: ['Chrome'], 30 | singleRun: false 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /nginx/nginx.conf.template: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80 default_server; 3 | listen [::]:80 default_server; 4 | location ^~/openmrs { 5 | proxy_pass ${OPENMRS_HOST_URL}; 6 | proxy_redirect off; 7 | proxy_set_header Host $host; 8 | proxy_set_header X-Real-IP $remote_addr; 9 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 10 | proxy_set_header X-Forwarded-Host $server_name; 11 | } 12 | location / { 13 | root /usr/share/nginx/html; 14 | try_files $uri $uri/ /index.html; 15 | } 16 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ng2-openmrs-formbuilder", 3 | "version": "0.0.0", 4 | "license": "MIT", 5 | "scripts": { 6 | "ng": "ng", 7 | "start": "npm run updateBuild && ng serve --proxy-config proxy.conf.js", 8 | "build": "ng build && npm run updateBuild", 9 | "test": "ng test", 10 | "lint": "ng lint", 11 | "e2e": "ng e2e", 12 | "lite": "lite-server", 13 | "updateBuild": "node ./replace.build.js" 14 | }, 15 | "private": true, 16 | "dependencies": { 17 | "@angular/animations": "^7.1.3", 18 | "@angular/cdk": "7.1.1", 19 | "@angular/common": "^7.1.3", 20 | "@angular/compiler": "^7.1.3", 21 | "@angular/core": "^7.1.3", 22 | "@angular/forms": "^7.1.3", 23 | "@angular/http": "^7.1.3", 24 | "@angular/material": "7.1.1", 25 | "@angular/platform-browser": "^7.1.3", 26 | "@angular/platform-browser-dynamic": "^7.1.3", 27 | "@angular/router": "^7.1.3", 28 | "@swimlane/ngx-datatable": "^14.0.0", 29 | "brace": "^0.11.1", 30 | "file-saver": "2.0.0", 31 | "hammerjs": "^2.0.8", 32 | "mkdirp": "^0.5.1", 33 | "moment": "2.23.0", 34 | "ng2-ace-editor": "^0.3.8", 35 | "ng2-bootstrap-modal": "^1.0.1", 36 | "ngx-bootstrap": "^3.1.3", 37 | "ngx-clipboard": "^11.1.9", 38 | "ngx-cookie": "^4.1.2", 39 | "ngx-openmrs-formentry": "git+https://github.com/Fatmali/ngx-openmrs-formentry.git#v2.12.0", 40 | "ngx-pagination": "^3.2.1", 41 | "rxjs": "^6.3.3", 42 | "tslib": "^1.9.0", 43 | "tslint-loader": "^3.5.4", 44 | "zone.js": "^0.8.26" 45 | }, 46 | "devDependencies": { 47 | "@angular-devkit/build-angular": "^0.13.10", 48 | "@angular/cli": "^7.1.3", 49 | "@angular/compiler-cli": "^7.1.3", 50 | "@angular/language-service": "^7.1.3", 51 | "@types/hammerjs": "^2.0.36", 52 | "@types/jasmine": "^3.3.1", 53 | "@types/jasminewd2": "~2.0.2", 54 | "@types/node": "~10.12.15", 55 | "codelyzer": "~4.5.0", 56 | "husky": "^4.3.0", 57 | "jasmine-core": "~3.3.0", 58 | "jasmine-spec-reporter": "~4.2.1", 59 | "karma": "~3.1.3", 60 | "karma-chrome-launcher": "~2.2.0", 61 | "karma-cli": "~2.0.0", 62 | "karma-coverage-istanbul-reporter": "^2.0.4", 63 | "karma-jasmine": "~2.0.1", 64 | "karma-jasmine-html-reporter": "^1.4.0", 65 | "lint-staged": "^10.4.0", 66 | "lite-server": "^2.3.0", 67 | "prettier": "2.1.2", 68 | "protractor": "~5.4.1", 69 | "replace-in-file": "^3.0.0", 70 | "ts-node": "~7.0.1", 71 | "tslint": "^5.20.1", 72 | "typescript": "3.1.6", 73 | "webpack-version-file": "^0.1.3" 74 | }, 75 | "husky": { 76 | "hooks": { 77 | "pre-commit": "lint-staged" 78 | } 79 | }, 80 | "lint-staged": { 81 | "*.{js,css,md,ts}": "prettier --write" 82 | }, 83 | "volta": { 84 | "node": "12.22.12" 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /protractor.conf.js: -------------------------------------------------------------------------------- 1 | // Protractor configuration file, see link for more information 2 | // https://github.com/angular/protractor/blob/master/lib/config.ts 3 | 4 | const { SpecReporter } = require('jasmine-spec-reporter'); 5 | 6 | exports.config = { 7 | allScriptsTimeout: 11000, 8 | specs: ['./e2e/**/*.e2e-spec.ts'], 9 | capabilities: { 10 | browserName: 'chrome' 11 | }, 12 | directConnect: true, 13 | baseUrl: 'http://localhost:4200/', 14 | framework: 'jasmine', 15 | jasmineNodeOpts: { 16 | showColors: true, 17 | defaultTimeoutInterval: 30000, 18 | print: function () {} 19 | }, 20 | onPrepare() { 21 | require('ts-node').register({ 22 | project: 'e2e/tsconfig.e2e.json' 23 | }); 24 | jasmine 25 | .getEnv() 26 | .addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /proxy.conf.js: -------------------------------------------------------------------------------- 1 | const PROXY_CONFIG = { 2 | '/openmrs/': { 3 | target: process.env.OPENMRS_HOST_URL, 4 | secure: process.env.OPENMRS_SECURE || false, 5 | changeOrigin: true 6 | } 7 | }; 8 | 9 | module.exports = PROXY_CONFIG; 10 | -------------------------------------------------------------------------------- /replace.build.js: -------------------------------------------------------------------------------- 1 | var replace = require('replace-in-file'); 2 | var buildDate = "date: '" + Date.now() + "'"; 3 | const options = { 4 | files: 'src/environments/environment.ts', 5 | from: /date\:\s\'(.*?)\'/, 6 | to: buildDate, 7 | allowEmptyPaths: false 8 | }; 9 | 10 | try { 11 | let changedFiles = replace.sync(options); 12 | console.log('Build date set: ' + buildDate); 13 | } catch (error) { 14 | console.error('Error occurred:', error); 15 | } 16 | -------------------------------------------------------------------------------- /src/app/Services/authentication/authentication.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { AuthenticationService } from './authentication.service'; 4 | 5 | @NgModule({ 6 | imports: [CommonModule], 7 | declarations: [], 8 | providers: [AuthenticationService] 9 | }) 10 | export class AuthenticationModule {} 11 | -------------------------------------------------------------------------------- /src/app/Services/authentication/authentication.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { AuthenticationService } from './authentication.service'; 4 | 5 | describe('AuthenticationService', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [AuthenticationService] 9 | }); 10 | }); 11 | 12 | it('should be created', inject( 13 | [AuthenticationService], 14 | (service: AuthenticationService) => { 15 | expect(service).toBeTruthy(); 16 | } 17 | )); 18 | }); 19 | -------------------------------------------------------------------------------- /src/app/Services/authentication/authentication.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { SessionService } from '../storage/session.service'; 3 | import { SessionStorageService } from '../storage/session-storage.service'; 4 | import { Constants } from '../constants'; 5 | import { Observable, BehaviorSubject } from 'rxjs'; 6 | 7 | @Injectable() 8 | export class AuthenticationService { 9 | isLoggedIn = false; 10 | redirectUrl = ''; 11 | baseUrl: BehaviorSubject = new BehaviorSubject(''); 12 | credentials: BehaviorSubject = new BehaviorSubject(''); 13 | 14 | constructor( 15 | private sessionStorageService: SessionStorageService, 16 | private sessionService: SessionService 17 | ) { 18 | if ( 19 | this.sessionStorageService.getItem(Constants.CREDENTIALS_KEY) !== null && 20 | this.sessionStorageService.getItem(Constants.BASE_URL) !== null 21 | ) { 22 | this.setCredentialsSubjectEncrypted( 23 | this.sessionStorageService.getItem(Constants.CREDENTIALS_KEY) 24 | ); 25 | this.setBaseUrl(this.sessionStorageService.getItem(Constants.BASE_URL)); 26 | this.isLoggedIn = true; 27 | } 28 | } 29 | 30 | public authenticate(username: string, password: string, baseUrl: string) { 31 | const credentials = { 32 | username: username, 33 | password: password 34 | }; 35 | 36 | const request = this.sessionService.getSession(credentials, baseUrl); 37 | 38 | request.subscribe((data: any) => { 39 | if (data.authenticated) { 40 | this.isLoggedIn = true; 41 | this.setCredentials(username, password, baseUrl); 42 | // store logged in user details in session storage 43 | const user = data.user; 44 | this.storeUser(user); 45 | } else { 46 | this.isLoggedIn = false; 47 | } 48 | }); 49 | 50 | return request; 51 | } 52 | 53 | public logOut() { 54 | this.isLoggedIn = false; 55 | const response = this.sessionService.deleteSession(); 56 | 57 | response.subscribe( 58 | (res) => { 59 | this.clearSessionCache(); 60 | }, 61 | (error: Error) => { 62 | this.clearSessionCache(); 63 | } 64 | ); 65 | 66 | return response; 67 | } 68 | 69 | public clearSessionCache() { 70 | this.clearCredentials(); 71 | this.clearUserDetails(); 72 | } 73 | 74 | private setCredentials(username: string, password: string, baseUrl: string) { 75 | const base64 = btoa(username + ':' + password); 76 | this.sessionStorageService.setItem(Constants.CREDENTIALS_KEY, base64); 77 | this.sessionStorageService.setItem(Constants.BASE_URL, baseUrl); 78 | } 79 | 80 | private clearCredentials() { 81 | this.sessionStorageService.remove(Constants.CREDENTIALS_KEY); 82 | } 83 | 84 | private storeUser(user: any) { 85 | this.sessionStorageService.setObject(Constants.USER_KEY, user); 86 | } 87 | 88 | private clearUserDetails() { 89 | this.sessionStorageService.remove(Constants.USER_KEY); 90 | } 91 | 92 | setBaseUrl(baseUrl: string) { 93 | this.baseUrl.next(baseUrl); 94 | } 95 | 96 | getBaseUrl() { 97 | return this.baseUrl.asObservable(); 98 | } 99 | 100 | setCredentialsSubject(username: string, password: string) { 101 | const base64 = btoa(username + ':' + password); 102 | this.credentials.next(base64); 103 | } 104 | 105 | setCredentialsSubjectEncrypted(base64: string) { 106 | this.credentials.next(base64); 107 | } 108 | 109 | getCredentialsSubject() { 110 | return this.credentials.asObservable(); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/app/Services/constants.ts: -------------------------------------------------------------------------------- 1 | export class Constants { 2 | public static readonly CREDENTIALS_KEY = 'auth.credentials'; 3 | 4 | public static readonly USER_KEY = 'user'; 5 | 6 | public static readonly BASE_URL = 'url'; 7 | 8 | public static readonly SCHEMA = 'schema'; 9 | 10 | public static readonly RAW_SCHEMA = 'rawSchema'; 11 | 12 | public static readonly FORM_METADATA = 'formMetadata'; 13 | 14 | public static readonly TIME_STAMP = 'timestamp'; // for saving locally 15 | 16 | public static readonly COMPONENT = 'component'; 17 | 18 | public static readonly POC = 'poc'; 19 | 20 | public static readonly FORM_TYPE = 'formType'; 21 | } 22 | -------------------------------------------------------------------------------- /src/app/Services/element-editor.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { ElementEditorService } from './element-editor.service'; 4 | 5 | describe('ElementEditorService', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [ElementEditorService] 9 | }); 10 | }); 11 | 12 | it('should be created', inject( 13 | [ElementEditorService], 14 | (service: ElementEditorService) => { 15 | expect(service).toBeTruthy(); 16 | } 17 | )); 18 | }); 19 | -------------------------------------------------------------------------------- /src/app/Services/element-editor.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Subject } from 'rxjs'; 3 | @Injectable() 4 | export class ElementEditorService { 5 | private previouslySelectAnswers: Subject = new Subject(); 6 | private setMembersSubject: Subject = new Subject(); 7 | private reshowSetMembers: Subject = new Subject(); 8 | private mappings: Subject = new Subject(); 9 | private id: Subject = new Subject(); 10 | constructor() {} 11 | 12 | reShowAnswersDialog(previouslySelected: any) { 13 | this.previouslySelectAnswers.next(previouslySelected); 14 | } 15 | 16 | reselectAnswers() { 17 | return this.previouslySelectAnswers.asObservable(); 18 | } 19 | setMembers(setMembers: any[]) { 20 | this.setMembersSubject.next(setMembers); 21 | } 22 | 23 | getSetMembers() { 24 | return this.setMembersSubject.asObservable(); 25 | } 26 | 27 | reShowSetMembersDialog(setMembers: any[]) { 28 | this.reshowSetMembers.next(setMembers); 29 | } 30 | 31 | reselectSetMembers() { 32 | return this.reshowSetMembers.asObservable(); 33 | } 34 | 35 | setMappings(mappings) { 36 | this.mappings.next(mappings); 37 | } 38 | getMappings() { 39 | return this.mappings; 40 | } 41 | 42 | setConceptId(id) { 43 | this.id.next(id); 44 | } 45 | 46 | getConceptId() { 47 | return this.id; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/app/Services/http-interceptor.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { 3 | HttpInterceptor, 4 | HttpRequest, 5 | HttpHandler, 6 | HttpEvent, 7 | HttpResponse, 8 | HttpErrorResponse 9 | } from '@angular/common/http'; 10 | import { Observable, throwError as observableThrowError } from 'rxjs'; 11 | import { tap } from 'rxjs/operators'; 12 | import { Constants } from './constants'; 13 | import { SessionStorageService } from './storage/session-storage.service'; 14 | 15 | @Injectable() 16 | export class FormBuilderHttpInteceptor implements HttpInterceptor { 17 | constructor(private sessionStorageService: SessionStorageService) {} 18 | 19 | intercept( 20 | req: HttpRequest, 21 | next: HttpHandler 22 | ): Observable> { 23 | const credentials = this.sessionStorageService.getItem( 24 | Constants.CREDENTIALS_KEY 25 | ); 26 | let modifiedReq = req; 27 | if (credentials) { 28 | const authHeader = { Authorization: 'Basic ' + credentials }; 29 | modifiedReq = req.clone({ setHeaders: authHeader }); 30 | } 31 | return next.handle(modifiedReq).pipe( 32 | tap( 33 | (event) => { 34 | if (event instanceof HttpResponse) { 35 | // do stuff with response here 36 | } 37 | }, 38 | (err: any) => { 39 | if (err instanceof HttpErrorResponse) { 40 | return observableThrowError(err); 41 | } 42 | } 43 | ) 44 | ); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/app/Services/navigator.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { NavigatorService } from './navigator.service'; 4 | 5 | describe('NavigatorService', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [NavigatorService] 9 | }); 10 | }); 11 | 12 | it('should be created', inject( 13 | [NavigatorService], 14 | (service: NavigatorService) => { 15 | expect(service).toBeTruthy(); 16 | } 17 | )); 18 | }); 19 | -------------------------------------------------------------------------------- /src/app/Services/navigator.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Subject, Observable, BehaviorSubject } from 'rxjs'; 3 | import { LocalStorageService } from './storage/local-storage.service'; 4 | import { Constants } from './constants'; 5 | 6 | @Injectable() 7 | export class NavigatorService { 8 | schema: Object; // stores the state of the current schema and all the changes in memory. 9 | rawSchema: Object; // stores the raw version of above 10 | schemaEditorSubject: Subject = new Subject(); 11 | rawSchemaEditorSubject: Subject = new Subject(); 12 | schemaSubject: Subject = new Subject(); 13 | questionSubject: Subject = new Subject(); 14 | rawSchemaSubject: BehaviorSubject = new BehaviorSubject({}); 15 | excludedQuestionsSubject: BehaviorSubject = new BehaviorSubject(''); 16 | prechecked: BehaviorSubject = new BehaviorSubject(''); 17 | 18 | constructor() {} 19 | 20 | setClickedElementSchema(schema) { 21 | this.schemaEditorSubject.next(schema); 22 | } 23 | 24 | getClickedElementSchema(): Observable { 25 | return this.schemaEditorSubject.asObservable(); 26 | } 27 | 28 | setClickedElementRawSchema(rawSchema) { 29 | this.rawSchemaEditorSubject.next(rawSchema); 30 | } 31 | 32 | getClickedElementRawSchema() { 33 | return this.rawSchemaEditorSubject.asObservable(); 34 | } 35 | 36 | setSchema(schema: Object) { 37 | this.schema = schema; 38 | this.schemaSubject.next(schema); 39 | } 40 | 41 | getSchema() { 42 | return this.schemaSubject.asObservable(); 43 | } 44 | 45 | newQuestion( 46 | propModelArray: any, 47 | pageIndex: number, 48 | sectionIndex: number, 49 | questionIndex?: number, 50 | parentQuestionIndex?: number, 51 | schema?: any 52 | ) { 53 | if (questionIndex !== undefined) { 54 | console.log('ObsGroup Question!'); 55 | } 56 | const question = {}; 57 | question['propModelArray'] = propModelArray; 58 | question['pageIndex'] = pageIndex; 59 | question['sectionIndex'] = sectionIndex; 60 | question['questionIndex'] = questionIndex; 61 | question['parentQuestionIndex'] = parentQuestionIndex || -1; 62 | question['schema'] = schema; 63 | this.questionSubject.next(question); 64 | } 65 | 66 | getNewQuestion() { 67 | return this.questionSubject.asObservable(); 68 | } 69 | 70 | setRawSchema(rawSchema: Object) { 71 | this.rawSchema = rawSchema; 72 | this.rawSchemaSubject.next(rawSchema); 73 | } 74 | 75 | getRawSchema() { 76 | return this.rawSchemaSubject.asObservable(); 77 | } 78 | 79 | // addToRawSchema(options:{},pageIndex?:number){ 80 | // let obj = this.rawSchemaSubject.getValue(); 81 | // if (pageIndex === undefined) { 82 | // obj['pages'].push(options); 83 | // this.setRawSchema(obj); 84 | // } 85 | 86 | // else{ 87 | // obj['pages'][pageIndex].sections.push(options) 88 | // this.setRawSchema(obj) 89 | // }} 90 | 91 | setExcludedQuestions(questionIDs: string) { 92 | this.excludedQuestionsSubject.next(questionIDs); 93 | } 94 | 95 | getExcludedQuestions() { 96 | return this.excludedQuestionsSubject.asObservable(); 97 | } 98 | 99 | setPrechecked(label) { 100 | this.prechecked.next(label); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/app/Services/openmrs-api/concept.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | import { ConceptService } from './concept.service'; 3 | 4 | describe('ConceptService', () => { 5 | beforeEach(() => { 6 | TestBed.configureTestingModule({ 7 | providers: [ConceptService] 8 | }); 9 | }); 10 | 11 | it('should be created', inject( 12 | [ConceptService], 13 | (service: ConceptService) => { 14 | expect(service).toBeTruthy(); 15 | } 16 | )); 17 | }); 18 | -------------------------------------------------------------------------------- /src/app/Services/openmrs-api/concept.service.ts: -------------------------------------------------------------------------------- 1 | import { 2 | forkJoin as observableForkJoin, 3 | of as observableOf, 4 | Observable 5 | } from 'rxjs'; 6 | import { catchError, map } from 'rxjs/operators'; 7 | import { Injectable } from '@angular/core'; 8 | import { SessionStorageService } from '../storage/session-storage.service'; 9 | import { Constants } from '../constants'; 10 | 11 | import * as _ from 'lodash'; 12 | import { HttpClient, HttpHeaders } from '@angular/common/http'; 13 | interface ConceptUuid { 14 | uuid: string; 15 | conceptDetails: any; 16 | } 17 | @Injectable() 18 | export class ConceptService { 19 | private data: any = {}; 20 | private headers = new HttpHeaders(); 21 | private baseUrl = ''; 22 | 23 | constructor( 24 | private http: HttpClient, 25 | private sessionStorageService: SessionStorageService 26 | ) { 27 | const credentials = sessionStorageService.getItem( 28 | Constants.CREDENTIALS_KEY 29 | ); 30 | this.baseUrl = sessionStorageService.getItem(Constants.BASE_URL); 31 | this.headers.append('Content-Type', 'application/json'); 32 | } 33 | 34 | public searchConceptByName(conceptID: string): Observable { 35 | // searching with concept display 36 | return this.http 37 | .get(`${this.baseUrl}/ws/rest/v1/concept?q=${conceptID}&v=full`, { 38 | headers: this.headers 39 | }) 40 | .pipe( 41 | catchError((error) => { 42 | alert(error.message); 43 | return observableOf(error.json()); 44 | }) 45 | ); 46 | } 47 | 48 | public searchConceptByUUID(conceptUUID: string): Observable { 49 | return this.http 50 | .get(`${this.baseUrl}/ws/rest/v1/concept/${conceptUUID}?v=full`, { 51 | headers: this.headers 52 | }) 53 | .pipe( 54 | catchError((error: Response) => { 55 | console.error(error.status); 56 | return observableOf(error.json()); 57 | }) 58 | ); 59 | } 60 | 61 | public validateConcepts(conceptUuids: any) { 62 | const observablesArray = []; 63 | _.each(conceptUuids, (conceptUuid) => { 64 | observablesArray.push( 65 | this.searchConceptByUUID(conceptUuid).pipe( 66 | map((concept) => { 67 | return { conceptUuid: concept.uuid, valid: true }; 68 | }), 69 | catchError((error) => { 70 | return [{ conceptUuid: conceptUuid, valid: false }]; 71 | }) 72 | ) 73 | ); 74 | }); 75 | return observableForkJoin(observablesArray); 76 | } 77 | 78 | public createMappingsValue(mappings): any[] { 79 | const props = []; 80 | _.forEach(mappings, (mapping) => { 81 | const type = mapping.display.substring(0, mapping.display.indexOf(':')); 82 | const value = mapping.display.substring(mapping.display.indexOf(' ') + 1); 83 | props.push({ type: type, value: value }); 84 | }); 85 | return props; 86 | } 87 | 88 | public getConceptID(conceptUuid: string): Observable { 89 | return this.http 90 | .get(`${this.baseUrl}/ws/rest/v1/concept/${conceptUuid}`) 91 | .pipe( 92 | catchError((error: Response) => { 93 | console.error(error.status); 94 | console.log(error); 95 | return observableOf(error); 96 | }) 97 | ); 98 | } 99 | 100 | public getAllConcepts() { 101 | return this.http 102 | .get(`${this.baseUrl}/ws/rest/v1/concept?v=full`, { 103 | headers: this.headers 104 | }) 105 | .pipe( 106 | catchError((error: Response) => { 107 | console.error(error.status); 108 | return observableOf(error.json()); 109 | }) 110 | ); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/app/Services/openmrs-api/encounter-type.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { EncounterTypeService } from './encounter-type.service'; 4 | 5 | describe('EncounterTypeService', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [EncounterTypeService] 9 | }); 10 | }); 11 | 12 | it('should be created', inject( 13 | [EncounterTypeService], 14 | (service: EncounterTypeService) => { 15 | expect(service).toBeTruthy(); 16 | } 17 | )); 18 | }); 19 | -------------------------------------------------------------------------------- /src/app/Services/openmrs-api/encounter-type.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { SessionStorageService } from '../storage/session-storage.service'; 3 | import { Constants } from '../constants'; 4 | import { HttpClient } from '@angular/common/http'; 5 | @Injectable() 6 | export class EncounterTypeService { 7 | private baseUrl: string; 8 | 9 | constructor( 10 | private http: HttpClient, 11 | private sessionStorageService: SessionStorageService 12 | ) { 13 | this.baseUrl = sessionStorage.getItem(Constants.BASE_URL); 14 | } 15 | 16 | getEncounterTypes() { 17 | return this.http.get(`${this.baseUrl}/ws/rest/v1/encountertype`); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/app/Services/openmrs-api/encounters.service.ts: -------------------------------------------------------------------------------- 1 | import { map } from 'rxjs/operators'; 2 | import { Injectable } from '@angular/core'; 3 | import { Observable } from 'rxjs'; 4 | import { SessionStorageService } from '../storage/session-storage.service'; 5 | import { Constants } from '../constants'; 6 | import { HttpClient, HttpHeaders } from '@angular/common/http'; 7 | 8 | @Injectable() 9 | export class EncounterService { 10 | private baseUrl; 11 | private restUrl; 12 | private headers = new HttpHeaders(); 13 | constructor( 14 | private http: HttpClient, 15 | private sessionStorageService: SessionStorageService 16 | ) { 17 | this.baseUrl = sessionStorage.getItem(Constants.BASE_URL); 18 | this.restUrl = this.baseUrl + '/ws/rest/v1/encounter'; 19 | this.headers.append('Content-Type', 'application/json'); 20 | } 21 | 22 | getEncounterByUuid(encounterUuid: string) { 23 | const v = 24 | 'custom:(uuid,encounterDatetime,' + 25 | 'patient:(uuid,uuid,identifiers),form:(uuid,name),' + 26 | 'visit:(uuid,visitType,display,startDatetime,stopDatetime),' + 27 | 'location:ref,encounterType:ref,encounterProviders,orders:full,' + 28 | 'obs:(uuid,obsDatetime,concept:(uuid,uuid,name:(display)),value:ref,groupMembers))'; 29 | if (!encounterUuid) { 30 | return null; 31 | } 32 | return this.http.get(`${this.restUrl}/${encounterUuid}?v=${v}`, { 33 | headers: this.headers 34 | }); 35 | } 36 | 37 | public getEncountersByPatientUuid(patientUuid: string): Observable { 38 | if (!patientUuid) { 39 | return null; 40 | } 41 | const params = new URLSearchParams(); 42 | params.set('patient', patientUuid); 43 | return this.http 44 | .get(this.restUrl + '?patient=' + patientUuid, { 45 | headers: this.headers 46 | }) 47 | .pipe(map((response) => response.results)); 48 | } 49 | 50 | saveEncounter(payload: any) { 51 | if (!payload) { 52 | return null; 53 | } 54 | return this.http.post(this.restUrl, JSON.stringify(payload), { 55 | headers: this.headers 56 | }); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/app/Services/openmrs-api/fetch-all-forms.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { FetchAllFormsService } from './fetch-all-forms.service'; 4 | 5 | describe('FetchAllFormsService', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [FetchAllFormsService] 9 | }); 10 | }); 11 | 12 | it('should be created', inject( 13 | [FetchAllFormsService], 14 | (service: FetchAllFormsService) => { 15 | expect(service).toBeTruthy(); 16 | } 17 | )); 18 | }); 19 | -------------------------------------------------------------------------------- /src/app/Services/openmrs-api/fetch-form-detail.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { FetchFormDetailService } from './fetch-form-detail.service'; 4 | 5 | describe('FetchFormsService', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [FetchFormDetailService] 9 | }); 10 | }); 11 | 12 | it('should be created', inject( 13 | [FetchFormDetailService], 14 | (service: FetchFormDetailService) => { 15 | expect(service).toBeTruthy(); 16 | } 17 | )); 18 | }); 19 | -------------------------------------------------------------------------------- /src/app/Services/openmrs-api/location-resource.service.ts: -------------------------------------------------------------------------------- 1 | import { catchError } from 'rxjs/operators'; 2 | import { Injectable } from '@angular/core'; 3 | import { SessionStorageService } from '../storage/session-storage.service'; 4 | import { AuthenticationService } from '../authentication/authentication.service'; 5 | import { HttpClient } from '@angular/common/http'; 6 | @Injectable() 7 | export class LocationResourceService { 8 | private baseUrl: string; 9 | private rest_endpoint = '/ws/rest/v1/location'; 10 | private rest_url; 11 | constructor( 12 | private http: HttpClient, 13 | private auth: AuthenticationService, 14 | private sessionStorageService: SessionStorageService 15 | ) { 16 | auth.getBaseUrl().subscribe((baseUrl) => { 17 | this.baseUrl = baseUrl; 18 | this.rest_url = this.baseUrl + this.rest_endpoint; 19 | }); 20 | } 21 | 22 | getAllLocations() { 23 | return this.http.get(this.rest_url).pipe( 24 | catchError((error) => { 25 | console.error(error); 26 | return error; 27 | }) 28 | ); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/app/Services/openmrs-api/patient-resource.service.ts: -------------------------------------------------------------------------------- 1 | import { catchError } from 'rxjs/operators'; 2 | import { Injectable } from '@angular/core'; 3 | import { Constants } from '../constants'; 4 | import { SessionStorageService } from '../storage/session-storage.service'; 5 | import { AuthenticationService } from '../authentication/authentication.service'; 6 | import { Observable } from 'rxjs'; 7 | import { HttpClient } from '@angular/common/http'; 8 | @Injectable() 9 | export class PatientResourceService { 10 | private credentials: any; 11 | private baseUrl: string; 12 | private rest_endpoint = '/ws/rest/v1/patient'; 13 | private rest_url; 14 | constructor( 15 | private http: HttpClient, 16 | private auth: AuthenticationService, 17 | private sessionStorageService: SessionStorageService 18 | ) { 19 | this.credentials = sessionStorageService.getItem(Constants.CREDENTIALS_KEY); 20 | auth.getBaseUrl().subscribe((baseUrl) => { 21 | this.baseUrl = baseUrl; 22 | this.rest_url = this.baseUrl + this.rest_endpoint; 23 | }); 24 | } 25 | 26 | searchPatientByName(name: string) { 27 | const url = `${this.rest_url}?q=${name}`; 28 | return this.http.get(url).pipe( 29 | catchError((error) => { 30 | console.error(error); 31 | return error; 32 | }) 33 | ); 34 | } 35 | 36 | public searchPatientByUuid(uuid: string): Observable { 37 | const url = `${this.rest_url}/${uuid}`; 38 | return this.http.get(url).pipe( 39 | catchError((error) => { 40 | console.log(error); 41 | return error; 42 | }) 43 | ); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/app/Services/order.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { OrderService } from './order.service'; 4 | 5 | describe('OrderService', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [OrderService] 9 | }); 10 | }); 11 | 12 | it('should be created', inject([OrderService], (service: OrderService) => { 13 | expect(service).toBeTruthy(); 14 | })); 15 | }); 16 | -------------------------------------------------------------------------------- /src/app/Services/order.service.ts: -------------------------------------------------------------------------------- 1 | import { of as observableOf, Observable } from 'rxjs'; 2 | import { map, catchError } from 'rxjs/operators'; 3 | import { Injectable } from '@angular/core'; 4 | import { SessionStorageService } from './storage/session-storage.service'; 5 | import { Constants } from '../Services/constants'; 6 | 7 | import * as _ from 'lodash'; 8 | import { HttpClient, HttpHeaders } from '@angular/common/http'; 9 | interface ConceptUuid { 10 | uuid: string; 11 | conceptDetails: any; 12 | } 13 | @Injectable() 14 | export class OrderService { 15 | private data: any = {}; 16 | private headers = new HttpHeaders(); 17 | private baseUrl = ''; 18 | 19 | constructor( 20 | private http: HttpClient, 21 | private sessionStorageService: SessionStorageService 22 | ) { 23 | this.baseUrl = sessionStorageService.getItem(Constants.BASE_URL); 24 | this.headers.append('Content-Type', 'application/json'); 25 | } 26 | 27 | public searchOrder(orderID: string): Observable { 28 | // searching with concept display 29 | return this.http 30 | .get(`${this.baseUrl}/ws/rest/v1/order?q=${orderID}`, { 31 | headers: this.headers 32 | }) 33 | .pipe( 34 | catchError((error) => { 35 | alert(error.message); 36 | return observableOf(error.json()); 37 | }) 38 | ); 39 | } 40 | 41 | public searchOrderByUUID(orderUUID: string): Observable { 42 | return this.http 43 | .get(`${this.baseUrl}/ws/rest/v1/order/${orderUUID}?`, { 44 | headers: this.headers 45 | }) 46 | .pipe( 47 | catchError((error: Response) => { 48 | console.error(error.status); 49 | return observableOf(error.json()); 50 | }) 51 | ); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/app/Services/question-control.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { QuestionControlService } from './question-control.service'; 4 | 5 | describe('QuestionControlService', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [QuestionControlService] 9 | }); 10 | }); 11 | 12 | it('should be created', inject( 13 | [QuestionControlService], 14 | (service: QuestionControlService) => { 15 | expect(service).toBeTruthy(); 16 | } 17 | )); 18 | }); 19 | -------------------------------------------------------------------------------- /src/app/Services/question-id.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { QuestionIdService } from './question-id.service'; 4 | 5 | describe('QuestionIdService', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [QuestionIdService] 9 | }); 10 | }); 11 | 12 | it('should be created', inject( 13 | [QuestionIdService], 14 | (service: QuestionIdService) => { 15 | expect(service).toBeTruthy(); 16 | } 17 | )); 18 | }); 19 | -------------------------------------------------------------------------------- /src/app/Services/question-id.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Observable } from 'rxjs'; 3 | import * as _ from 'lodash'; 4 | @Injectable() 5 | export class QuestionIdService { 6 | private IDs = []; 7 | 8 | constructor() {} 9 | 10 | getIDs(schema) { 11 | return this.collectIDs(schema); 12 | } 13 | 14 | private collectIDs(schema) { 15 | // let $schema = _.cloneDeep(schema); 16 | // if($schema.pages!==ull) this.collectIDs(schema.pages); 17 | // if(Array.isArray($schema)){ 18 | // $schema.forEach(element => { 19 | // if(element.sections) this.collectIDs(element.sections) 20 | // if(element.questions) this.collectIDs(element.questions) 21 | // else { 22 | // let id = _.cloneDeep(element.id) 23 | // if(typeof(id) !=='undefined'){ 24 | // this.IDs.push(id) 25 | // } 26 | // } 27 | // }) } 28 | this.IDs = []; 29 | return this.IDs; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/app/Services/route-guards/auth-guard.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { AuthGuardService } from './auth-guard.service'; 4 | 5 | describe('AuthGuardService', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [AuthGuardService] 9 | }); 10 | }); 11 | 12 | it('should be created', inject( 13 | [AuthGuardService], 14 | (service: AuthGuardService) => { 15 | expect(service).toBeTruthy(); 16 | } 17 | )); 18 | }); 19 | -------------------------------------------------------------------------------- /src/app/Services/route-guards/auth-guard.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { 3 | CanActivate, 4 | ActivatedRouteSnapshot, 5 | RouterStateSnapshot, 6 | Router 7 | } from '@angular/router'; 8 | import { AuthenticationService } from '../authentication/authentication.service'; 9 | 10 | @Injectable() 11 | export class AuthGuardService implements CanActivate { 12 | constructor( 13 | private authService: AuthenticationService, 14 | private router: Router 15 | ) {} 16 | 17 | canActivate( 18 | route: ActivatedRouteSnapshot, 19 | state: RouterStateSnapshot 20 | ): boolean { 21 | const url = state.url; 22 | return this.checkLogin(url); 23 | } 24 | 25 | checkLogin(url): boolean { 26 | if (this.authService.isLoggedIn) { 27 | return true; 28 | } 29 | 30 | this.authService.redirectUrl = url; 31 | this.router.navigate(['/login']); 32 | return false; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/app/Services/route-guards/save-forms-guard.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { SaveFormsGuardService } from './save-forms-guard.service'; 4 | 5 | describe('SaveFormsGuardService', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [SaveFormsGuardService] 9 | }); 10 | }); 11 | 12 | it('should be created', inject( 13 | [SaveFormsGuardService], 14 | (service: SaveFormsGuardService) => { 15 | expect(service).toBeTruthy(); 16 | } 17 | )); 18 | }); 19 | -------------------------------------------------------------------------------- /src/app/Services/route-guards/save-forms-guard.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { 3 | CanDeactivate, 4 | ActivatedRouteSnapshot, 5 | RouterStateSnapshot 6 | } from '@angular/router'; 7 | import { FormEditorComponent } from '../../form-editor/form-editor/form-editor.component'; 8 | import { ConfirmComponent } from '../../modals/confirm.component'; 9 | import { Observable } from 'rxjs'; 10 | import { Router } from '@angular/router'; 11 | @Injectable() 12 | export class SaveFormsGuardService 13 | implements CanDeactivate { 14 | constructor(private router: Router) {} 15 | 16 | canDeactivate( 17 | component: FormEditorComponent, 18 | route: ActivatedRouteSnapshot, 19 | state: RouterStateSnapshot 20 | ): boolean | Observable | Promise { 21 | const can = component.canDeactivate(); 22 | // console.log('DeactivateGuard#canDeactivate called, can: ', can); 23 | if (!can) { 24 | this.router.navigate([this.router.url]); 25 | return false; 26 | } 27 | 28 | return can; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/app/Services/storage/local-storage.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | @Injectable() 4 | export class LocalStorageService { 5 | public getItem(keyName: string): string { 6 | return window.localStorage.getItem(keyName); 7 | } 8 | 9 | public setItem(keyName: string, value: string): void { 10 | window.localStorage.setItem(keyName, value); 11 | } 12 | 13 | public getObject(keyName: string): any { 14 | const stored = window.localStorage.getItem(keyName); 15 | try { 16 | const object = JSON.parse(stored); 17 | return object; 18 | } catch (error) { 19 | console.error(error); 20 | return null; 21 | } 22 | } 23 | 24 | public setObject(keyName: string, value: any) { 25 | window.localStorage.setItem(keyName, JSON.stringify(value)); 26 | } 27 | 28 | public remove(keyName: string): void { 29 | window.localStorage.removeItem(keyName); 30 | } 31 | 32 | public clear(): void { 33 | window.localStorage.clear(); 34 | } 35 | 36 | get storageLength(): number { 37 | return window.localStorage.length; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/app/Services/storage/session-storage.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | @Injectable() 4 | export class SessionStorageService { 5 | public getItem(keyName: string): string { 6 | return window.sessionStorage.getItem(keyName); 7 | } 8 | 9 | public setItem(keyName: string, value: string): void { 10 | window.sessionStorage.setItem(keyName, value); 11 | } 12 | 13 | public getObject(keyName: string): any { 14 | const stored = window.sessionStorage.getItem(keyName); 15 | try { 16 | const object = JSON.parse(stored); 17 | return object; 18 | } catch (error) { 19 | console.error(error); 20 | return null; 21 | } 22 | } 23 | 24 | public setObject(keyName: string, value: any) { 25 | window.sessionStorage.setItem(keyName, JSON.stringify(value)); 26 | } 27 | 28 | public remove(keyName: string): void { 29 | window.sessionStorage.removeItem(keyName); 30 | } 31 | 32 | public clear(): void { 33 | window.sessionStorage.clear(); 34 | } 35 | 36 | get storageLength(): number { 37 | return window.sessionStorage.length; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/app/Services/storage/session.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { SessionStorageService } from './session-storage.service'; 4 | import { Constants } from '../constants'; 5 | 6 | @Injectable() 7 | export class SessionService { 8 | private sessionUrl; 9 | 10 | constructor( 11 | private http: HttpClient, 12 | private sessionStorageService: SessionStorageService 13 | ) {} 14 | 15 | public getUrl(): string { 16 | return this.sessionUrl; 17 | } 18 | 19 | public getSession(credentials: any = null, baseUrl: string) { 20 | this.sessionUrl = baseUrl + '/ws/rest/v1/session'; 21 | 22 | if (credentials && credentials.username) { 23 | const base64 = btoa(credentials.username + ':' + credentials.password); 24 | this.sessionStorageService.setItem(Constants.CREDENTIALS_KEY, base64); 25 | } 26 | 27 | return this.http.get(this.sessionUrl); 28 | } 29 | 30 | public deleteSession() { 31 | const url = this.getUrl(); 32 | this.sessionStorageService.clear(); 33 | return this.http.delete(url, {}); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/app/Services/update-component.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { UpdateComponentService } from './update-component.service'; 4 | 5 | describe('UpdateComponentService', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [UpdateComponentService] 9 | }); 10 | }); 11 | 12 | it('should be created', inject( 13 | [UpdateComponentService], 14 | (service: UpdateComponentService) => { 15 | expect(service).toBeTruthy(); 16 | } 17 | )); 18 | }); 19 | -------------------------------------------------------------------------------- /src/app/Services/update-component.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Observable, BehaviorSubject } from 'rxjs'; 3 | 4 | interface Data { 5 | componentMetadata: any; 6 | arrayOfFormsToUpdate: any[]; 7 | } 8 | @Injectable() 9 | export class UpdateComponentService { 10 | data: BehaviorSubject = new BehaviorSubject(undefined); 11 | $data = this.data.asObservable(); 12 | constructor() {} 13 | 14 | setData(data: Data) { 15 | this.data.next(data); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/app/app-entry-point/form-builder.component.css: -------------------------------------------------------------------------------- 1 | .app { 2 | height: calc(100vh - 6rem); 3 | } 4 | 5 | .main { 6 | height: 100vh; 7 | display: flex; 8 | flex-flow: column wrap; 9 | } 10 | 11 | .footer { 12 | padding: 1rem 0rem; 13 | display: flex; 14 | justify-content: center; 15 | text-align: center; 16 | font-size: 14px; 17 | } 18 | -------------------------------------------------------------------------------- /src/app/app-entry-point/form-builder.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 | 6 | 9 |
10 | -------------------------------------------------------------------------------- /src/app/app-entry-point/form-builder.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { FormBuilderComponent } from './form-builder.component'; 4 | 5 | describe('FormBuilderComponent', () => { 6 | let component: FormBuilderComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [FormBuilderComponent] 12 | }).compileComponents(); 13 | })); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(FormBuilderComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should be created', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/app-entry-point/form-builder.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-form-builder', 5 | templateUrl: './form-builder.component.html', 6 | styleUrls: ['./form-builder.component.css'] 7 | }) 8 | export class FormBuilderComponent implements OnInit { 9 | constructor() {} 10 | 11 | ngOnInit() {} 12 | } 13 | -------------------------------------------------------------------------------- /src/app/app-material-module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { 3 | MatSidenavModule, 4 | MatButtonModule, 5 | MatIconModule, 6 | MatToolbarModule, 7 | MatCardModule, 8 | MatSnackBarModule, 9 | MatMenuModule, 10 | MatTabsModule, 11 | MatTooltipModule, 12 | MatProgressBarModule 13 | } from '@angular/material'; 14 | 15 | @NgModule({ 16 | exports: [ 17 | MatSidenavModule, 18 | MatTabsModule, 19 | MatIconModule, 20 | MatButtonModule, 21 | MatToolbarModule, 22 | MatCardModule, 23 | MatSnackBarModule, 24 | MatMenuModule, 25 | MatTooltipModule, 26 | MatProgressBarModule 27 | ] 28 | }) 29 | export class AppMaterialModule {} 30 | -------------------------------------------------------------------------------- /src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { RouterModule, Routes } from '@angular/router'; 4 | import { FormEditorComponent } from './form-editor/form-editor/form-editor.component'; 5 | import { ViewFormsComponent } from './view-forms/view-forms.component'; 6 | import { LoginComponent } from './login/login.component'; 7 | import { SaveFormsGuardService } from './Services/route-guards/save-forms-guard.service'; 8 | import { AuthGuardService } from './Services/route-guards/auth-guard.service'; 9 | import { UpdateFormsComponent } from './form-editor/update-forms/update-forms.component'; 10 | import { UpdateFormsWizardComponent } from './form-editor/update-forms-wizard/update-forms-wizard.component'; 11 | const appRoutes: Routes = [ 12 | { path: '', redirectTo: 'forms', pathMatch: 'full' }, 13 | { path: 'login', component: LoginComponent }, 14 | { 15 | path: 'forms', 16 | component: ViewFormsComponent, 17 | canActivate: [AuthGuardService] 18 | }, 19 | { 20 | path: 'edit/:uuid', 21 | component: FormEditorComponent, 22 | canDeactivate: [SaveFormsGuardService] 23 | }, 24 | { 25 | path: 'update/:oldUuid/:newUuid', 26 | component: UpdateFormsComponent, 27 | canActivate: [AuthGuardService] 28 | } 29 | ]; 30 | 31 | @NgModule({ 32 | imports: [CommonModule, RouterModule.forRoot(appRoutes, { useHash: true })], 33 | declarations: [], 34 | exports: [RouterModule], 35 | providers: [SaveFormsGuardService] 36 | }) 37 | export class AppRoutingModule {} 38 | -------------------------------------------------------------------------------- /src/app/app.component.css: -------------------------------------------------------------------------------- 1 | .app { 2 | width: 100%; 3 | } 4 | 5 | .footer { 6 | position: absolute; 7 | right: 0; 8 | bottom: 0; 9 | left: 0; 10 | padding: 1rem; 11 | background-color: #efefef; 12 | text-align: center; 13 | } 14 | -------------------------------------------------------------------------------- /src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, async } from '@angular/core/testing'; 2 | 3 | import { AppComponent } from './app.component'; 4 | 5 | describe('AppComponent', () => { 6 | beforeEach(async(() => { 7 | TestBed.configureTestingModule({ 8 | declarations: [AppComponent] 9 | }).compileComponents(); 10 | })); 11 | 12 | it('should create the app', async(() => { 13 | const fixture = TestBed.createComponent(AppComponent); 14 | const app = fixture.debugElement.componentInstance; 15 | expect(app).toBeTruthy(); 16 | })); 17 | }); 18 | -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Input } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-root', 5 | templateUrl: './app.component.html', 6 | styleUrls: ['./app.component.css'] 7 | }) 8 | export class AppComponent implements OnInit { 9 | constructor() {} 10 | 11 | ngOnInit() {} 12 | } 13 | -------------------------------------------------------------------------------- /src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule, ApplicationRef } from '@angular/core'; 2 | import { BrowserModule } from '@angular/platform-browser'; 3 | import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http'; 4 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 5 | import { MatSelectModule } from '@angular/material'; 6 | import { BootstrapModalModule } from 'ng2-bootstrap-modal'; 7 | import { NgxPaginationModule } from 'ngx-pagination'; 8 | import { AppRoutingModule } from './app-routing.module'; 9 | import { FormEditorModule } from './form-editor/form-editor/form-editor.module'; 10 | import { AuthenticationModule } from './Services/authentication/authentication.module'; 11 | import { AppMaterialModule } from './app-material-module'; 12 | import { ModalsModule } from './modals/modals-module'; 13 | import { SharedModule } from './shared-module'; 14 | import 'hammerjs'; 15 | import { AppComponent } from './app.component'; 16 | import { NotificationComponent } from './form-editor/snackbar/notification-toast'; 17 | import { ViewFormsComponent } from './view-forms/view-forms.component'; 18 | import { LoginComponent } from './login/login.component'; 19 | import { SnackbarComponent } from './form-editor/snackbar/snackbar.component'; 20 | import { AuthGuardService } from './Services/route-guards/auth-guard.service'; 21 | import { SaveFormService } from './Services/openmrs-api/save-form.service'; 22 | import { EncounterTypeService } from './Services/openmrs-api/encounter-type.service'; 23 | import { FormListService } from './Services/form-list.service'; 24 | import { SearchFormFilterPipe } from './pipes/search-form-filter.pipe'; 25 | import { BuildVersionFooterComponent } from './build-version-footer/build-version-footer.component'; 26 | import { SaveSnackbarComponent } from './form-editor/snackbar/saved-snackbar'; 27 | import { FormBuilderComponent } from './app-entry-point/form-builder.component'; 28 | import { Str2Num } from './pipes/string_to_number.pipe'; 29 | import { FormBuilderHttpInteceptor } from './Services/http-interceptor.service'; 30 | 31 | @NgModule({ 32 | declarations: [ 33 | AppComponent, 34 | BuildVersionFooterComponent, 35 | FormBuilderComponent, 36 | LoginComponent, 37 | NotificationComponent, 38 | SaveSnackbarComponent, 39 | SearchFormFilterPipe, 40 | SnackbarComponent, 41 | Str2Num, 42 | ViewFormsComponent 43 | ], 44 | 45 | imports: [ 46 | AppMaterialModule, 47 | AppRoutingModule, 48 | AuthenticationModule, 49 | BootstrapModalModule, 50 | BrowserAnimationsModule, 51 | BrowserModule, 52 | FormEditorModule, 53 | HttpClientModule, 54 | MatSelectModule, 55 | ModalsModule, 56 | NgxPaginationModule, 57 | SharedModule 58 | ], 59 | entryComponents: [ 60 | NotificationComponent, 61 | SaveSnackbarComponent, 62 | SnackbarComponent 63 | ], 64 | providers: [ 65 | AuthGuardService, 66 | EncounterTypeService, 67 | FormListService, 68 | SaveFormService, 69 | { 70 | provide: HTTP_INTERCEPTORS, 71 | useClass: FormBuilderHttpInteceptor, 72 | multi: true 73 | } 74 | ], 75 | 76 | bootstrap: [AppComponent] 77 | }) 78 | export class AppModule { 79 | constructor(applicationRef: ApplicationRef) { 80 | // for ng2-bootstrap-modal in angular 5 81 | Object.defineProperty(applicationRef, '_rootComponents', { 82 | get: () => applicationRef['components'] 83 | }); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/app/build-version-footer/build-version-footer.component.css: -------------------------------------------------------------------------------- 1 | .buildVersion { 2 | color: rgb(107 114 128); 3 | font-weight: 400; 4 | font-family: 'Inter', sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /src/app/build-version-footer/build-version-footer.component.html: -------------------------------------------------------------------------------- 1 |
2 |

3 | v{{ version }} · Built on {{ date }} 4 |

5 |
6 | -------------------------------------------------------------------------------- /src/app/build-version-footer/build-version-footer.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { BuildVersionFooterComponent } from './build-version-footer.component'; 4 | 5 | describe('BuildVersionFooterComponent', () => { 6 | let component: BuildVersionFooterComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [BuildVersionFooterComponent] 12 | }).compileComponents(); 13 | })); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(BuildVersionFooterComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should be created', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/build-version-footer/build-version-footer.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import * as env from '../../environments/environment'; 3 | @Component({ 4 | selector: 'app-build-version-footer', 5 | templateUrl: './build-version-footer.component.html', 6 | styleUrls: ['./build-version-footer.component.css'] 7 | }) 8 | export class BuildVersionFooterComponent implements OnInit { 9 | date: string; 10 | version: string; 11 | constructor() {} 12 | 13 | ngOnInit() { 14 | const d = new Date(+env.environment.date); 15 | this.date = d.toLocaleDateString() + ' ' + d.toLocaleTimeString(); 16 | this.version = env.environment.version; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/app/collections/linked-list.ts: -------------------------------------------------------------------------------- 1 | export class Node { 2 | formMetadata: any; 3 | next: Node; 4 | previous: Node; 5 | constructor(formMetadata: any) { 6 | this.formMetadata = formMetadata; 7 | this.next = null; 8 | this.previous = null; 9 | } 10 | } 11 | 12 | export class LinkedList { 13 | head: Node = null; // points to the first added list. 14 | 15 | append(formMetadata: any) { 16 | const list = new Node(formMetadata); 17 | if (this.head === null) { 18 | this.head = list; // this is the first list; 19 | } else { 20 | let temp = this.head; 21 | while (temp.next) { 22 | temp = temp.next; 23 | } 24 | list.previous = temp; 25 | list.next = null; 26 | temp.next = list; // append new list to the end of the linked list. 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/app/form-editor/audit-info/audit-info.component.css: -------------------------------------------------------------------------------- 1 | .row { 2 | display: flex; 3 | align-items: baseline; 4 | margin: 0.625rem; 5 | } 6 | -------------------------------------------------------------------------------- /src/app/form-editor/audit-info/audit-info.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Form name

4 |
5 |
6 | 12 |
13 |
14 | 15 |
16 |
17 |

Form UUID

18 |
19 |
20 | 26 |
27 |
28 | 29 |
30 |
31 |

Form version

32 |
33 |
34 | 40 |
41 |
42 | 43 |
44 |
45 |

Encounter Type

46 |
47 |
48 | 54 |
55 |
56 | 57 |
58 |
59 |

Description

60 |
61 |
62 | 67 |
68 |
69 | 70 |
71 |
72 |

Created by

73 |
74 |
75 | 81 |
82 |
83 |

Date created

84 |
85 |
86 | 97 |
98 |
99 | 100 |
101 |
102 |

Changed by

103 |
104 |
105 | 111 |
112 |
113 |

Date changed

114 |
115 |
116 | 127 |
128 |
129 | 130 |
131 |
132 |

Published

133 |
134 |
135 | 141 |
142 |
143 | -------------------------------------------------------------------------------- /src/app/form-editor/audit-info/audit-info.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { AuditInfoComponent } from './audit-info.component'; 4 | 5 | describe('AuditInfoComponent', () => { 6 | let component: AuditInfoComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [AuditInfoComponent] 12 | }).compileComponents(); 13 | })); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(AuditInfoComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should be created', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/form-editor/audit-info/audit-info.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Input } from '@angular/core'; 2 | 3 | @Component({ 4 | // tslint:disable-next-line:component-selector 5 | selector: 'audit-info', 6 | templateUrl: './audit-info.component.html', 7 | styleUrls: ['./audit-info.component.css'] 8 | }) 9 | export class AuditInfoComponent implements OnInit { 10 | formMetadata: any; 11 | 12 | @Input() set _formMetadata(formMetadata) { 13 | this.formMetadata = formMetadata; 14 | } 15 | 16 | constructor() {} 17 | 18 | ngOnInit() {} 19 | } 20 | -------------------------------------------------------------------------------- /src/app/form-editor/concept/concept.component.css: -------------------------------------------------------------------------------- 1 | h5 { 2 | font-weight: bold; 3 | font-size: 14px; 4 | color: #2196f3; 5 | } 6 | -------------------------------------------------------------------------------- /src/app/form-editor/concept/concept.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 13 | 14 | 26 | 27 |
28 |
29 |
30 | -------------------------------------------------------------------------------- /src/app/form-editor/concept/concept.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ConceptComponent } from './concept.component'; 4 | 5 | describe('ConceptComponent', () => { 6 | let component: ConceptComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ConceptComponent] 12 | }).compileComponents(); 13 | })); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(ConceptComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should be created', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/form-editor/element-editor/dynamic-question/dynamic-question.component.css: -------------------------------------------------------------------------------- 1 | .label { 2 | font-weight: bold; 3 | font-size: 14px; 4 | color: black; 5 | } 6 | 7 | .panel-body { 8 | padding: 0px 15px 0px 15px; 9 | } 10 | .form-control.ng-invalid.ng-touched { 11 | border: 1px solid red; 12 | } 13 | -------------------------------------------------------------------------------- /src/app/form-editor/element-editor/dynamic-question/dynamic-question.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 6 | This field is required. 7 | 8 | 9 |
10 |
11 |
12 | 14 |
15 | 16 |
17 | 23 |
24 | 25 |
26 | 28 |
31 |
32 | 33 |
34 |
35 | 37 | 38 |
39 |
40 |
41 |
42 |
43 |
44 | -------------------------------------------------------------------------------- /src/app/form-editor/element-editor/dynamic-question/dynamic-question.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { DynamicQuestionComponent } from './dynamic-question.component'; 4 | 5 | describe('DynamicQuestionComponent', () => { 6 | let component: DynamicQuestionComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [DynamicQuestionComponent] 12 | }).compileComponents(); 13 | })); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(DynamicQuestionComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should be created', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/form-editor/element-editor/dynamic-question/dynamic-question.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; 2 | import { PropertyModel } from '../../models/property-model'; 3 | import { FormGroup, FormControl } from '@angular/forms'; 4 | import { ConceptComponent } from '../../concept/concept.component'; 5 | 6 | @Component({ 7 | selector: 'app-dynamic-question', 8 | templateUrl: './dynamic-question.component.html', 9 | styleUrls: ['./dynamic-question.component.css'] 10 | }) 11 | export class DynamicQuestionComponent implements OnInit { 12 | @Input() question: PropertyModel; 13 | @Output() answers = new EventEmitter(); 14 | @Output() type = new EventEmitter(); 15 | @Output() rendering = new EventEmitter(); 16 | @Output() showDate = new EventEmitter(); 17 | 18 | form: FormGroup; 19 | 20 | @Input() set _form(form) { 21 | this.form = form; 22 | } 23 | constructor() {} 24 | 25 | ngOnInit() {} 26 | 27 | isControlValid(controlName: string) { 28 | if ( 29 | this.form.controls[controlName].valid || 30 | this.form.controls[controlName].untouched 31 | ) { 32 | return true; 33 | } else { 34 | return false; 35 | } 36 | } 37 | 38 | typeSelected(selectBox: string) { 39 | if (selectBox === 'type') { 40 | const value = this.form.controls['type'].value; 41 | this.type.emit(value); 42 | } 43 | 44 | if (selectBox === 'questionOptions.rendering') { 45 | const value = this.form.controls['questionOptions.rendering'].value; 46 | this.rendering.emit(value); 47 | } 48 | 49 | if (selectBox === 'questionOptions.showDate') { 50 | const value = this.form.controls['questionOptions.showDate'].value; 51 | this.showDate.emit(value); 52 | } 53 | } 54 | 55 | isValid() { 56 | return this.form.controls[this.question.key].valid; 57 | } 58 | 59 | emitAnswers(answers: any) { 60 | this.answers.emit(answers); 61 | } 62 | 63 | onTextAreaChanged($event, question) { 64 | this.form.controls[question].setValue($event); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/app/form-editor/element-editor/element-editor.component.css: -------------------------------------------------------------------------------- 1 | .btn-primary { 2 | width: 100%; 3 | } 4 | 5 | .dropdown { 6 | margin: 0px 0px 20px 0px; 7 | } 8 | .dropdown-menu { 9 | font-family: 'Roboto', serif; 10 | font-size: 14px; 11 | } 12 | a { 13 | cursor: pointer; 14 | } 15 | 16 | .dropdown-menu > li > a:hover { 17 | color: white; 18 | background-color: #2196f3; 19 | } 20 | .disabled, 21 | .disabled:hover { 22 | cursor: text; 23 | background-color: white !important; 24 | color: red !important; 25 | } 26 | 27 | .mat-card > :first-child { 28 | margin-top: -27px; 29 | margin-right: -20px; 30 | } 31 | -------------------------------------------------------------------------------- /src/app/form-editor/element-editor/element-editor.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 8 |
9 |
10 | 11 |
12 | 13 | 14 | 15 |
16 | 26 | 39 |
40 | 41 |
42 | 43 | 44 |
45 |
46 |
47 | 48 | 54 | 55 |
56 | 57 |
58 | 59 | 65 | 66 |
67 | 68 | 70 | 71 |
72 | 73 |
74 |
75 | 78 |
79 |
80 |
81 |
82 |
83 | -------------------------------------------------------------------------------- /src/app/form-editor/element-editor/element-editor.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ElementEditorComponent } from './element-editor.component'; 4 | 5 | describe('ElementEditorComponent', () => { 6 | let component: ElementEditorComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ElementEditorComponent] 12 | }).compileComponents(); 13 | })); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(ElementEditorComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should be created', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/form-editor/form-editor/assets/ampath-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AMPATH/ngx-openmrs-formbuilder/beeca40af54d73cb289e085d7012969c05094083/src/app/form-editor/form-editor/assets/ampath-logo.png -------------------------------------------------------------------------------- /src/app/form-editor/form-editor/assets/loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AMPATH/ngx-openmrs-formbuilder/beeca40af54d73cb289e085d7012969c05094083/src/app/form-editor/form-editor/assets/loader.gif -------------------------------------------------------------------------------- /src/app/form-editor/form-editor/form-editor-menu.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | // tslint:disable-next-line:component-selector 5 | selector: 'form-editor-menu', 6 | templateUrl: './form-editor-menu.html', 7 | styleUrls: [] 8 | }) 9 | export class MenuComponent implements OnInit { 10 | constructor() {} 11 | 12 | ngOnInit() {} 13 | } 14 | -------------------------------------------------------------------------------- /src/app/form-editor/form-editor/form-editor-menu.html: -------------------------------------------------------------------------------- 1 |
2 | 5 | 17 |
18 | 19 |
20 | 23 | 35 |
36 | 37 |
38 | 41 | 53 |
54 | 55 |
56 | 59 | 69 |
70 | 71 |
72 | 75 |
76 |
77 | 80 |
81 | -------------------------------------------------------------------------------- /src/app/form-editor/form-editor/form-editor.component.css: -------------------------------------------------------------------------------- 1 | .h-100 { 2 | height: 100vh; 3 | } 4 | 5 | .my-4 { 6 | margin: 0rem 1rem; 7 | } 8 | 9 | .ml-2 { 10 | margin-left: 0.5rem; 11 | } 12 | 13 | .mr-2 { 14 | margin-right: 0.5rem; 15 | } 16 | 17 | .menuItem { 18 | display: flex; 19 | padding: 1rem; 20 | align-items: center; 21 | } 22 | 23 | #create-form { 24 | height: 500px; 25 | border-right: 1px solid lightgray; 26 | } 27 | 28 | a { 29 | margin: auto; 30 | } 31 | 32 | h6 { 33 | color: #2196f3; 34 | } 35 | 36 | .menu { 37 | flex: 1 1 auto; 38 | } 39 | 40 | /* .mat-toolbar{ 41 | background:#337ab7; 42 | } 43 | .mat-tab-labels{ 44 | background-color:#337ab7 !important; 45 | 46 | } */ 47 | .mat-sidenav { 48 | width: 650px; 49 | background: #fafafa; 50 | } 51 | /* .mat-sidenav-content { 52 | position: relative; 53 | transform: translate3d(0,0,0); 54 | display: block; 55 | height: fill-available !important; 56 | overflow-x:hidden !important; 57 | } */ 58 | a { 59 | cursor: pointer; 60 | } 61 | 62 | .row { 63 | display: flex; 64 | max-width: 100vw; 65 | } 66 | 67 | .btn-default { 68 | color: #fff; 69 | background: rgba(0, 0, 0, 0); 70 | border: none; 71 | } 72 | 73 | .editor { 74 | flex-grow: 1; 75 | } 76 | #loadingDiv { 77 | height: 85vh; 78 | position: relative; 79 | background: transparent; 80 | } 81 | 82 | .loader { 83 | position: relative; 84 | top: 50%; 85 | left: 50%; 86 | transform: translate(-50%, -50%); 87 | } 88 | 89 | .element-editor { 90 | margin: auto; 91 | max-width: 760px; 92 | } 93 | 94 | .bold { 95 | font-weight: 700; 96 | color: black; 97 | } 98 | 99 | .schema-editor { 100 | margin: auto; 101 | height: auto; 102 | max-width: 100vw; 103 | /* width:1600px; */ 104 | } 105 | .errorToolbar { 106 | background: darkcyan; 107 | } 108 | 109 | .menu { 110 | margin-left: 10px; 111 | } 112 | 113 | .hr { 114 | border-bottom: 1px solid lightgray; 115 | } 116 | 117 | .audit-content { 118 | margin-top: 20px; 119 | margin-bottom: 10px; 120 | } 121 | 122 | .app-content { 123 | border-top: 1px solid lightgray; 124 | } 125 | 126 | .space { 127 | margin-right: 10px; 128 | margin-left: 10px; 129 | } 130 | 131 | .show-on-hover:hover > ul.dropdown-menu { 132 | display: block; 133 | } 134 | 135 | .fb-menu-item { 136 | font-size: 15px; 137 | font-weight: 600; 138 | color: white; 139 | } 140 | 141 | .btn-default.active, 142 | .btn-default:active, 143 | .open > .dropdown-toggle.btn-default { 144 | box-shadow: none; 145 | background-color: rgba(0, 0, 0, 0.555); 146 | } 147 | .dropdown-menu:before { 148 | position: absolute; 149 | top: -7px; 150 | left: 9px; 151 | display: inline-block; 152 | border-right: 7px solid transparent; 153 | border-bottom: 7px solid #ccc; 154 | border-left: 7px solid transparent; 155 | border-bottom-color: rgba(0, 0, 0, 0.2); 156 | content: ''; 157 | } 158 | 159 | .dropdown-menu:after { 160 | position: absolute; 161 | top: -6px; 162 | left: 10px; 163 | display: inline-block; 164 | border-right: 6px solid transparent; 165 | border-bottom: 6px solid #ffffff; 166 | border-left: 6px solid transparent; 167 | content: ''; 168 | } 169 | 170 | .docs-link { 171 | text-decoration: none; 172 | } 173 | -------------------------------------------------------------------------------- /src/app/form-editor/form-editor/form-editor.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { FormEditorComponent } from './form-editor.component'; 4 | 5 | describe('FormEditorComponent', () => { 6 | let component: FormEditorComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [FormEditorComponent] 12 | }).compileComponents(); 13 | })); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(FormEditorComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should be created', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/form-editor/form-editor/form-editor.module.ts: -------------------------------------------------------------------------------- 1 | // Modules 2 | import { NgModule } from '@angular/core'; 3 | import { CommonModule } from '@angular/common'; 4 | import { FormEntryModule } from 'ngx-openmrs-formentry/dist/ngx-formentry'; 5 | import { SharedModule } from '../../shared-module'; 6 | // Services 7 | import { FetchFormDetailService } from '../../Services/openmrs-api/fetch-form-detail.service'; 8 | import { NavigatorService } from '../../Services/navigator.service'; 9 | import { QuestionControlService } from '../../Services/question-control.service'; 10 | import { PropertyFactory } from '../models/property-factory'; 11 | import { FormElementFactory } from '../form-elements/form-element-factory'; 12 | import { QuestionIdService } from '../../Services/question-id.service'; 13 | import { ConceptService } from '../../Services/openmrs-api/concept.service'; 14 | import { FormFactory } from '../form-elements/form-factory.service'; 15 | import { FetchAllFormsService } from '../../Services/openmrs-api/fetch-all-forms.service'; 16 | import { ElementEditorService } from '../../Services/element-editor.service'; 17 | import { FormSchemaCompiler } from '../../Services/schema-compiler.service'; 18 | import { UpdateComponentService } from '../../Services/update-component.service'; 19 | 20 | // Components 21 | import { ReferenceFormsComponent } from '../reference-forms/reference-forms.component'; 22 | import { SchemaEditorComponent } from '../schema-editor/schema-editor.component'; 23 | import { ElementEditorComponent } from '../element-editor/element-editor.component'; 24 | import { FormRendererComponent } from '../form-renderer/form-renderer.component'; 25 | import { DynamicQuestionComponent } from '../element-editor/dynamic-question/dynamic-question.component'; 26 | import { ConceptComponent } from '../concept/concept.component'; 27 | import { FormEditorComponent } from './form-editor.component'; 28 | import { AuditInfoComponent } from '../audit-info/audit-info.component'; 29 | import { UpdateFormsComponent } from '../update-forms/update-forms.component'; 30 | import { OrderComponent } from '../order/order.component'; 31 | @NgModule({ 32 | imports: [CommonModule, FormEntryModule, SharedModule], 33 | 34 | declarations: [ 35 | SchemaEditorComponent, 36 | ElementEditorComponent, 37 | FormRendererComponent, 38 | DynamicQuestionComponent, 39 | ConceptComponent, 40 | FormEditorComponent, 41 | ReferenceFormsComponent, 42 | AuditInfoComponent, 43 | UpdateFormsComponent, 44 | OrderComponent 45 | ], 46 | 47 | providers: [ 48 | FetchFormDetailService, 49 | NavigatorService, 50 | QuestionControlService, 51 | PropertyFactory, 52 | FormElementFactory, 53 | QuestionIdService, 54 | ConceptService, 55 | FormFactory, 56 | FetchAllFormsService, 57 | ElementEditorService, 58 | FormSchemaCompiler, 59 | UpdateComponentService 60 | ], 61 | 62 | exports: [FormEntryModule] 63 | }) 64 | export class FormEditorModule {} 65 | -------------------------------------------------------------------------------- /src/app/form-editor/form-elements/Form.ts: -------------------------------------------------------------------------------- 1 | export class Form { 2 | name: string; 3 | uuid: string; 4 | processor: string; 5 | referencedForms: any; 6 | pages: any; 7 | 8 | constructor(options: {} = {}) { 9 | this.name = options['name']; 10 | this.pages = options['pages']; 11 | this.processor = options['processor']; 12 | this.uuid = options['uuid']; 13 | this.referencedForms = options['referencedForms']; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/app/form-editor/form-elements/FormElement.ts: -------------------------------------------------------------------------------- 1 | export class FormElement { 2 | label: string; 3 | 4 | constructor(options: { label?: string }) { 5 | this.label = options.label; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/app/form-editor/form-elements/Page.ts: -------------------------------------------------------------------------------- 1 | import { FormElement } from './FormElement'; 2 | export class Page extends FormElement { 3 | sections: Array<{}> = []; 4 | 5 | constructor(options: {} = {}) { 6 | super(options); 7 | this.sections = options['sections'] || []; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/app/form-editor/form-elements/Question.ts: -------------------------------------------------------------------------------- 1 | import { FormElement } from './FormElement'; 2 | export class QuestionOptions { 3 | rendering: string; 4 | answers: any[]; 5 | max: string; 6 | min: string; 7 | concept: string; 8 | conceptMappings: any[]; 9 | attritubeType: string; 10 | calculate: any; 11 | rows: number; 12 | conceptId: string; 13 | } 14 | 15 | export class Question extends FormElement { 16 | id = ''; 17 | type: string; 18 | questionOptions: QuestionOptions = new QuestionOptions(); 19 | questions: Question[]; 20 | 21 | constructor(options: {} = {}) { 22 | super(options); 23 | this.type = options['type'] || ''; 24 | this.questionOptions.rendering = options['questionOptions.rendering'] || ''; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/app/form-editor/form-elements/Section.ts: -------------------------------------------------------------------------------- 1 | import { FormElement } from './FormElement'; 2 | export class Section extends FormElement { 3 | questions: Array<{}>; 4 | isExpanded: boolean; 5 | 6 | constructor(options: {} = {}) { 7 | super(options); 8 | this.isExpanded = options['isExpanded'] || false; 9 | this.questions = options['questions'] || []; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/app/form-editor/form-elements/form-element-factory.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { FormElementFactory } from './form-element-factory'; 4 | 5 | describe('FormElementFactoryService', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [FormElementFactory] 9 | }); 10 | }); 11 | 12 | it('should be created', inject( 13 | [FormElementFactory], 14 | (service: FormElementFactory) => { 15 | expect(service).toBeTruthy(); 16 | } 17 | )); 18 | }); 19 | -------------------------------------------------------------------------------- /src/app/form-editor/form-elements/form-element-factory.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { FormElement } from './FormElement'; 3 | import { Page } from './Page'; 4 | import { Section } from './Section'; 5 | import { Question } from './Question'; 6 | 7 | @Injectable() 8 | export class FormElementFactory { 9 | constructor() {} 10 | 11 | createFormElement(type: string, options: any): FormElement { 12 | if (type.toLowerCase() === 'page') { 13 | return new Page(options); 14 | } 15 | 16 | if (type.toLowerCase() === 'section') { 17 | return new Section(options); 18 | } 19 | 20 | if (type.toLowerCase() === 'question') { 21 | return new Question(options); 22 | } else { 23 | console.log(type + ' Element not supported'); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/app/form-editor/form-elements/form-factory.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { FormFactory } from './form-factory.service'; 4 | 5 | describe('FormFactoryService', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [FormFactory] 9 | }); 10 | }); 11 | 12 | it('should be created', inject([FormFactory], (service: FormFactory) => { 13 | expect(service).toBeTruthy(); 14 | })); 15 | }); 16 | -------------------------------------------------------------------------------- /src/app/form-editor/form-elements/form-factory.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Form } from './Form'; 3 | @Injectable() 4 | export class FormFactory { 5 | constructor() {} 6 | 7 | createForm(options) { 8 | return new Form(options); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/app/form-editor/form-renderer/form-renderer.component.css: -------------------------------------------------------------------------------- 1 | .btn { 2 | margin: 20px 10px 10px 10px; 3 | } 4 | .form { 5 | height: 100%; 6 | } 7 | -------------------------------------------------------------------------------- /src/app/form-editor/form-renderer/form-renderer.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 |
5 | 12 |
13 |
14 | -------------------------------------------------------------------------------- /src/app/form-editor/form-renderer/form-renderer.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { FormRendererComponent } from './form-renderer.component'; 4 | 5 | describe('FormRendererComponent', () => { 6 | let component: FormRendererComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [FormRendererComponent] 12 | }).compileComponents(); 13 | })); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(FormRendererComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should be created', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/form-editor/form-renderer/mock-data-source.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Observable, Subject } from 'rxjs'; 3 | @Injectable() 4 | export class MockDataSourceService { 5 | constructor() {} 6 | sampleSearch(): Observable { 7 | const items: Array = [ 8 | { value: '0', label: 'Aech' }, 9 | { value: '5b6e58ea-1359-11df-a1f1-0026b9348838', label: 'Art3mis' }, 10 | { value: '2', label: 'Daito' }, 11 | { value: '3', label: 'Parzival' }, 12 | { value: '4', label: 'Shoto' } 13 | ]; 14 | return Observable.create((observer: Subject) => { 15 | setTimeout(() => { 16 | observer.next(items); 17 | }, 1000); 18 | }); 19 | } 20 | 21 | sampleProviderSearch(): Observable { 22 | const items: Array = [ 23 | { 24 | value: 'p28f1f96-baae-42b5-8df9-d4d98a0f42e3', 25 | label: '1552-9 - MELIBA MELIBA MELIBA' 26 | }, 27 | { 28 | value: 'p5ff5ba7-e16e-4b6a-b5cd-00f0382377ab', 29 | label: '1973-7 - Shihati Shihati Shihati' 30 | }, 31 | { 32 | value: 'p589c677-5b0b-47a2-a0ba-cc43963b9267', 33 | label: '2363-0 - ROSESIS ROSESIS ROSESIS' 34 | }, 35 | { 36 | value: 'ec07fc28-812f-4450-a8d1-4ebc3ec5d876', 37 | label: 'Ampath Test Provider' 38 | } 39 | ]; 40 | return Observable.create((observer: Subject) => { 41 | setTimeout(() => { 42 | observer.next(items); 43 | }, 1000); 44 | }); 45 | } 46 | 47 | sampleResolve(): Observable { 48 | const item = { value: '1', label: 'Art3mis' }; 49 | return Observable.create((observer: Subject) => { 50 | setTimeout(() => { 51 | observer.next(item); 52 | }, 1000); 53 | }); 54 | } 55 | 56 | sampleProviderResolve(): Observable { 57 | const item = { value: '0', label: 'Ampath Test' }; 58 | return Observable.create((observer: Subject) => { 59 | setTimeout(() => { 60 | observer.next(item); 61 | }, 1000); 62 | }); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/app/form-editor/models/property-factory.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { PropertyFactory } from './property-factory'; 4 | 5 | describe('PropertyFactory', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [PropertyFactory] 9 | }); 10 | }); 11 | 12 | it('should be created', inject( 13 | [PropertyFactory], 14 | (service: PropertyFactory) => { 15 | expect(service).toBeTruthy(); 16 | } 17 | )); 18 | }); 19 | -------------------------------------------------------------------------------- /src/app/form-editor/models/property-factory.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { TextboxProperty } from './textbox-property'; 3 | import { SelectProperty } from './select-property'; 4 | import { SearchboxProperty } from './searchbox-property'; 5 | import { TextAreaProperty } from './textarea-property'; 6 | import { PropertyModel } from './property-model'; 7 | 8 | @Injectable() 9 | export class PropertyFactory { 10 | constructor() {} 11 | 12 | createProperty(type: string, options: {}): PropertyModel { 13 | if (type.toLowerCase() === 'textbox') { 14 | return new TextboxProperty(options); 15 | } else if (type.toLowerCase() === 'select') { 16 | return new SelectProperty(options); 17 | } else if (type.toLowerCase() === 'searchbox') { 18 | return new SearchboxProperty(options); 19 | } else if (type.toLowerCase() === 'textarea') { 20 | return new TextAreaProperty(options); 21 | } else { 22 | console.log('No such property exists'); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/app/form-editor/models/property-model.ts: -------------------------------------------------------------------------------- 1 | export class PropertyModel { 2 | value: T; 3 | key: string; 4 | label: string; 5 | required: boolean; 6 | controlType: string; 7 | parentPath: string; 8 | order: number; 9 | 10 | constructor( 11 | options: { 12 | controlType?: string; 13 | key?: string; 14 | label?: string; 15 | value?: T; 16 | required?: boolean; 17 | parentPath?: string; 18 | order?: number; 19 | } = {} 20 | ) { 21 | this.value = options.value || null; 22 | this.key = options.key || ''; 23 | this.label = options.label || ''; 24 | this.controlType = options.controlType || ''; 25 | this.required = options.required || false; 26 | this.parentPath = options.parentPath || ''; 27 | this.order = options.order || 9; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/app/form-editor/models/searchbox-property.ts: -------------------------------------------------------------------------------- 1 | import { PropertyModel } from './property-model'; 2 | export class SearchboxProperty extends PropertyModel { 3 | controlType = 'searchbox'; 4 | searchData: string; 5 | type: string; 6 | 7 | constructor(options: {} = {}) { 8 | super(options); 9 | this.type = options['type'] || ''; 10 | this.searchData = options['searchData']; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/app/form-editor/models/select-property.ts: -------------------------------------------------------------------------------- 1 | import { PropertyModel } from './property-model'; 2 | export class SelectProperty extends PropertyModel { 3 | controlType = 'select'; 4 | options: { key: string; value: string }[] = []; 5 | 6 | constructor(options: {} = {}) { 7 | super(options); 8 | this.key = options['key'] || ''; 9 | this.options = options['options'] || []; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/app/form-editor/models/textarea-property.ts: -------------------------------------------------------------------------------- 1 | import { PropertyModel } from './property-model'; 2 | export class TextAreaProperty extends PropertyModel { 3 | controlType = 'textarea'; 4 | type: string; 5 | placeholder: string; 6 | rows: number; 7 | 8 | constructor(options: {} = {}) { 9 | super(options); 10 | this.type = options['type'] || ''; 11 | this.placeholder = options['placeholder'] || ''; 12 | this.rows = options['rows'] || 5; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/app/form-editor/models/textbox-property.ts: -------------------------------------------------------------------------------- 1 | import { PropertyModel } from './property-model'; 2 | export class TextboxProperty extends PropertyModel { 3 | controlType = 'textbox'; 4 | type: string; 5 | placeholder: string; 6 | 7 | constructor(options: {} = {}) { 8 | super(options); 9 | this.type = options['type'] || 'text'; 10 | this.placeholder = options['placeholder'] || ''; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/app/form-editor/navigator/navigator.component.css: -------------------------------------------------------------------------------- 1 | #schema { 2 | cursor: pointer; 3 | font-size: 18px; 4 | font-weight: bolder; 5 | text-decoration: none; 6 | display: inline-block; 7 | } 8 | 9 | .element { 10 | font-size: 16px; 11 | font-weight: 600; 12 | text-decoration: none; 13 | display: inline-block; 14 | cursor: pointer; 15 | } 16 | 17 | .element:hover { 18 | color: #008cba; 19 | } 20 | 21 | a { 22 | color: white; 23 | font-size: 14px; 24 | font-weight: bold; 25 | cursor: pointer; 26 | text-decoration: none; 27 | } 28 | 29 | .fa-chevron-down, 30 | .fa-pencil, 31 | .fa-toggle-down { 32 | color: #008cba !important; 33 | } 34 | 35 | .well { 36 | border: none; 37 | background: none; 38 | margin: 0.5rem 0; 39 | } 40 | 41 | .formName { 42 | width: 100%; 43 | margin-bottom: 3rem; 44 | } 45 | 46 | .display { 47 | display: none; 48 | } 49 | 50 | label { 51 | font-size: 14px; 52 | margin-bottom: 15px; 53 | } 54 | 55 | .myEditors { 56 | margin-bottom: 15px; 57 | padding: 10px 10px 10px 10px; 58 | } 59 | 60 | .selectMode { 61 | display: inline-block; 62 | } 63 | 64 | .pages { 65 | margin: 5px 2px 2px 0px; 66 | padding: 5px 5px 5px 5px; 67 | border-bottom: 1px solid rgba(211, 211, 211, 0.23); 68 | } 69 | 70 | .ref { 71 | margin-left: 20px !important; 72 | } 73 | 74 | .create { 75 | margin-right: 10px !important; 76 | } 77 | 78 | .badge { 79 | border-radius: 3px; 80 | box-shadow: 0px 4px 3px #888888; 81 | width: 100%; 82 | } 83 | 84 | .badge.sec { 85 | width: auto; 86 | background: transparent; 87 | } 88 | 89 | .ref:hover, 90 | .create:hover { 91 | color: #123456 !important; 92 | } 93 | 94 | .badge.sec > a { 95 | color: #008cba !important; 96 | } 97 | 98 | .material-icons { 99 | color: #008cba; 100 | font-size: 18px; 101 | } 102 | 103 | .yellow { 104 | color: green; 105 | } 106 | 107 | .question-area { 108 | display: flex; 109 | justify-content: space-between; 110 | align-items: center; 111 | } 112 | -------------------------------------------------------------------------------- /src/app/form-editor/navigator/navigator.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { NavigatorComponent } from './navigator.component'; 4 | 5 | describe('NavigatorComponent', () => { 6 | let component: NavigatorComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [NavigatorComponent] 12 | }).compileComponents(); 13 | })); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(NavigatorComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should be created', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/form-editor/order/order.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AMPATH/ngx-openmrs-formbuilder/beeca40af54d73cb289e085d7012969c05094083/src/app/form-editor/order/order.component.css -------------------------------------------------------------------------------- /src/app/form-editor/order/order.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 12 | 13 | 25 | 26 |
27 |
28 |
29 | -------------------------------------------------------------------------------- /src/app/form-editor/order/order.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { OrderComponent } from './order.component'; 4 | 5 | describe('OrderComponent', () => { 6 | let component: OrderComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [OrderComponent] 12 | }).compileComponents(); 13 | })); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(OrderComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should be created', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/form-editor/order/order.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Input } from '@angular/core'; 2 | import { PropertyModel } from '../models/property-model'; 3 | import { FormGroup } from '@angular/forms'; 4 | @Component({ 5 | selector: 'app-order', 6 | templateUrl: './order.component.html', 7 | styleUrls: ['./order.component.css'] 8 | }) 9 | export class OrderComponent implements OnInit { 10 | question: PropertyModel; 11 | form: FormGroup; 12 | @Input() set _question(question: PropertyModel) { 13 | this.question = question; 14 | } 15 | 16 | @Input() set _form(form: FormGroup) { 17 | this.form = form; 18 | } 19 | constructor() {} 20 | 21 | ngOnInit() {} 22 | } 23 | -------------------------------------------------------------------------------- /src/app/form-editor/reference-forms/reference-form-model.ts: -------------------------------------------------------------------------------- 1 | export class ReferenceForm { 2 | constructor( 3 | public formName: string, 4 | public alias: string, 5 | public uuid: string 6 | ) {} 7 | } 8 | -------------------------------------------------------------------------------- /src/app/form-editor/reference-forms/reference-forms.component.css: -------------------------------------------------------------------------------- 1 | .badge { 2 | font-weight: bold; 3 | } 4 | -------------------------------------------------------------------------------- /src/app/form-editor/reference-forms/reference-forms.component.html: -------------------------------------------------------------------------------- 1 |
2 | 5 | 6 |
7 | 12 |
13 |
Referenced Forms
14 | 21 |
22 |
23 |
24 | -------------------------------------------------------------------------------- /src/app/form-editor/reference-forms/reference-forms.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ReferenceFormsComponent } from './reference-forms.component'; 4 | 5 | describe('ReferenceFormsComponent', () => { 6 | let component: ReferenceFormsComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ReferenceFormsComponent] 12 | }).compileComponents(); 13 | })); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(ReferenceFormsComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should be created', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/form-editor/schema-editor/schema-editor.component.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Roboto Mono'; 3 | font-style: normal; 4 | font-weight: 400; 5 | src: local(''), url('../../../assets/roboto-mono.woff') format('woff'); 6 | } 7 | 8 | .editorContainer { 9 | padding: 0rem 1rem 1rem; 10 | background-color: rgb(243 244 246); 11 | } 12 | 13 | .m-4 { 14 | margin: 1rem; 15 | } 16 | 17 | .mr-2 { 18 | margin-right: 0.5rem; 19 | } 20 | 21 | .ml-2 { 22 | margin-left: 0.5rem; 23 | } 24 | 25 | .text-base { 26 | font-size: 16px; 27 | } 28 | 29 | .errorContainer { 30 | display: flex; 31 | align-items: center; 32 | justify-content: center; 33 | } 34 | 35 | .flexCenter { 36 | display: flex; 37 | align-items: center; 38 | } 39 | 40 | .errorIcon { 41 | font-size: 16px; 42 | margin-right: 0.5rem; 43 | } 44 | 45 | .editor { 46 | height: 100vh; 47 | width: 100%; 48 | overflow: auto; 49 | } 50 | 51 | .actionButtons { 52 | display: flex; 53 | align-items: center; 54 | justify-content: space-between; 55 | text-align: center; 56 | padding: 1rem; 57 | } 58 | -------------------------------------------------------------------------------- /src/app/form-editor/schema-editor/schema-editor.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 | error{{ errorMessage }} 7 |
8 |
9 |
10 |
11 | 21 |
22 |
23 |
24 | 37 | 46 |
47 |
48 |
49 |
50 |
56 |
57 |
58 | -------------------------------------------------------------------------------- /src/app/form-editor/schema-editor/schema-editor.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { SchemaEditorComponent } from './schema-editor.component'; 4 | 5 | describe('SchemaEditorComponent', () => { 6 | let component: SchemaEditorComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [SchemaEditorComponent] 12 | }).compileComponents(); 13 | })); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(SchemaEditorComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should be created', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/form-editor/snackbar/notification-toast.ts: -------------------------------------------------------------------------------- 1 | import { Component, Inject } from '@angular/core'; 2 | import { MAT_SNACK_BAR_DATA } from '@angular/material'; 3 | 4 | @Component({ 5 | selector: 'app-snackbar', 6 | template: '{{ data }}' 7 | }) 8 | export class NotificationComponent { 9 | constructor(@Inject(MAT_SNACK_BAR_DATA) public data: any) {} 10 | } 11 | -------------------------------------------------------------------------------- /src/app/form-editor/snackbar/saved-snackbar.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | // tslint:disable-next-line:component-selector 5 | selector: 'snackbar', 6 | template: `
7 | 8 |
Schema Saved Succesfully!
9 |
` 10 | }) 11 | export class SaveSnackbarComponent {} 12 | -------------------------------------------------------------------------------- /src/app/form-editor/snackbar/snackbar.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Inject } from '@angular/core'; 2 | import { MAT_SNACK_BAR_DATA } from '@angular/material'; 3 | @Component({ 4 | // tslint:disable-next-line:component-selector 5 | selector: 'snackbar', 6 | template: `
7 | 8 |
{{ data }}
9 |
` 10 | }) 11 | export class SnackbarComponent { 12 | constructor(@Inject(MAT_SNACK_BAR_DATA) public data: any) {} 13 | } 14 | -------------------------------------------------------------------------------- /src/app/form-editor/update-forms-wizard/update-forms-wizard.component.css: -------------------------------------------------------------------------------- 1 | .fill-space { 2 | flex: 1 1 auto; 3 | } 4 | 5 | .progressbar { 6 | margin: 50px; 7 | } 8 | 9 | span { 10 | display: inline-block; 11 | } 12 | -------------------------------------------------------------------------------- /src/app/form-editor/update-forms-wizard/update-forms-wizard.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 | 6 |

{{ currentSchemaMetadata.name }}

7 | 8 | 9 |
10 | 11 |
12 |
13 | 21 |
22 |
23 |
24 | 25 | 26 | 34 | 43 | 44 | 53 | 54 |
55 |
56 |
57 |
58 | {{ errorMessage }} 59 |
60 |
61 | 62 |
63 |
64 | The following are sections that reference the new component. Please 66 | customize or click next. 68 |
69 | 70 | 76 |
77 | -------------------------------------------------------------------------------- /src/app/form-editor/update-forms-wizard/update-forms-wizard.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { UpdateFormsWizardComponent } from './update-forms-wizard.component'; 4 | 5 | describe('UpdateFormsWizardComponent', () => { 6 | let component: UpdateFormsWizardComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [UpdateFormsWizardComponent] 12 | }).compileComponents(); 13 | })); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(UpdateFormsWizardComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should be created', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/form-editor/update-forms/update-forms.component.css: -------------------------------------------------------------------------------- 1 | .conatiner { 2 | padding-top: 20px; 3 | } 4 | 5 | th { 6 | color: #fff; 7 | background-color: #3f51b5; 8 | border-color: #808080; 9 | } 10 | 11 | mat-card { 12 | color: #3c763d; 13 | background-color: #dff0d8; 14 | border-color: #d6e9c6; 15 | } 16 | 17 | table { 18 | background-color: #fff; 19 | box-shadow: 0px 2px 2px #aaa; 20 | border-color: lightgray; 21 | } 22 | 23 | .flex { 24 | flex: 1 1 auto; 25 | } 26 | 27 | .version-input { 28 | width: 30%; 29 | } 30 | 31 | .name { 32 | font-family: 'Roboto'; 33 | color: #fff; 34 | font-size: 16px; 35 | } 36 | .well { 37 | height: 85vh; 38 | } 39 | -------------------------------------------------------------------------------- /src/app/form-editor/update-forms/update-forms.component.html: -------------------------------------------------------------------------------- 1 | 2 | Update Forms 3 | 4 | 12 | 13 |
14 |
15 | 16 |

17 | {{ oldComponentMetadata.name }} has been successfully updated! The 19 | forms below reference the component. Please preview and update each 20 | form. 22 |

23 |
24 |
25 |
26 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 43 | 44 | 45 | 55 | 63 | 64 | 65 |
FormVersionComponentPublishedUpdate
41 | {{ form.metadata.name }} 42 | {{ form.metadata.version }}{{ oldComponentMetadata.name }} 46 | 54 | 56 |
57 | 61 |
62 |
66 |
67 |
68 | -------------------------------------------------------------------------------- /src/app/form-editor/update-forms/update-forms.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { UpdateFormsComponent } from './update-forms.component'; 4 | 5 | describe('UpdateFormsComponent', () => { 6 | let component: UpdateFormsComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [UpdateFormsComponent] 12 | }).compileComponents(); 13 | })); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(UpdateFormsComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should be created', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/login/login.component.css: -------------------------------------------------------------------------------- 1 | .loginPage { 2 | margin-top: 10rem; 3 | } 4 | 5 | .container { 6 | height: calc(100vh - 6rem); 7 | } 8 | 9 | .card { 10 | padding: 3.5rem 3rem; 11 | box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); 12 | } 13 | 14 | .logoContainer { 15 | display: flex; 16 | justify-content: center; 17 | } 18 | 19 | .logo { 20 | width: 350px; 21 | height: 70px; 22 | margin: 2rem 0; 23 | } 24 | 25 | .form-control { 26 | margin: 1.5rem 0; 27 | min-height: 44px; 28 | } 29 | 30 | .loginForm { 31 | padding: 2rem; 32 | } 33 | 34 | .selectInput { 35 | -webkit-appearance: none; 36 | background-image: url('../../assets/select.svg'); 37 | background-position: right 0.5rem center; 38 | background-repeat: no-repeat; 39 | background-size: 1.5em 1.5em; 40 | padding-right: 2.5rem; 41 | } 42 | 43 | .loginButton { 44 | display: flex; 45 | justify-content: flex-start; 46 | margin-top: 3rem; 47 | } 48 | -------------------------------------------------------------------------------- /src/app/login/login.component.html: -------------------------------------------------------------------------------- 1 |
2 |
6 | 7 |
8 |
9 | 10 |
11 | 12 |
17 | 32 | 48 |
49 | 50 | 64 |
65 |
66 | 69 | 77 |
78 |
79 | 80 | 88 |
89 |
90 | 91 | 100 |
101 |
102 | 110 |
111 |
112 |
113 |
114 |
115 |
116 | -------------------------------------------------------------------------------- /src/app/login/login.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { LoginComponent } from './login.component'; 4 | 5 | describe('LoginComponent', () => { 6 | let component: LoginComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [LoginComponent] 12 | }).compileComponents(); 13 | })); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(LoginComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should be created', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/login/login.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { Router } from '@angular/router'; 3 | import { catchError } from 'rxjs/operators'; 4 | import { AuthenticationService } from '../Services/authentication/authentication.service'; 5 | 6 | @Component({ 7 | selector: 'app-login', 8 | templateUrl: './login.component.html', 9 | styleUrls: ['./login.component.css'] 10 | }) 11 | export class LoginComponent implements OnInit { 12 | constructor(private auth: AuthenticationService, private router: Router) { 13 | this.setMessage(); 14 | } 15 | 16 | authenticated: boolean; 17 | hasConnectionProblem = false; 18 | hasInvalidCredentials = false; 19 | selectedValue: string; 20 | message: string; 21 | selectedBaseUrl = ''; 22 | baseUrls: string[] = ['Enter a custom URL', 'https://ngx.ampath.or.ke/amrs']; 23 | 24 | ngOnInit() {} 25 | 26 | login(credentials) { 27 | if (credentials.customServerUrl) { 28 | credentials.baseUrl = credentials.customServerUrl; 29 | } 30 | 31 | this.authenticated = false; 32 | this.auth.setBaseUrl(credentials.baseUrl); 33 | this.auth.setCredentialsSubject(credentials.username, credentials.password); 34 | this.auth 35 | .authenticate( 36 | credentials.username, 37 | credentials.password, 38 | credentials.baseUrl 39 | ) 40 | .pipe( 41 | catchError((error) => { 42 | if (error.status === 0) { 43 | this.hasConnectionProblem = true; 44 | } 45 | return error; 46 | }) 47 | ) 48 | .subscribe((res: any) => { 49 | if (res.sessionId && !res.authenticated) { 50 | this.hasInvalidCredentials = true; 51 | } 52 | this.setMessage(); 53 | if (this.auth.isLoggedIn) { 54 | this.authenticated = true; 55 | this.message = 'Success!'; 56 | const redirectUrl = this.auth.redirectUrl 57 | ? this.auth.redirectUrl 58 | : '/forms'; 59 | this.router.navigate([redirectUrl]); 60 | } else { 61 | this.message = undefined; 62 | this.authenticated = false; 63 | } 64 | }); 65 | } 66 | 67 | setMessage() { 68 | const str = this.authenticated ? 'in' : undefined; 69 | if (str) { 70 | this.message = 'Already logged ' + str; 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/app/modals/alert.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { DialogComponent, DialogService } from 'ng2-bootstrap-modal'; 3 | 4 | export interface AlertModel { 5 | message: string; 6 | } 7 | 8 | @Component({ 9 | // tslint:disable-next-line:component-selector 10 | selector: 'alert', 11 | template: ``, 26 | styles: [ 27 | ` 28 | .msg { 29 | white-space: pre-line; 30 | } 31 | ` 32 | ] 33 | }) 34 | export class AlertComponent 35 | extends DialogComponent 36 | implements AlertModel { 37 | message: string; 38 | constructor(dialogService: DialogService) { 39 | super(dialogService); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/app/modals/answers-modal/answers-modal.css: -------------------------------------------------------------------------------- 1 | .modal-dialog { 2 | overflow-y: initial !important; 3 | } 4 | .modal-content { 5 | height: 550px; 6 | overflow-y: auto; 7 | } 8 | /* Hiding the checkbox, but allowing it to be focused */ 9 | .badgebox { 10 | opacity: 0; 11 | } 12 | 13 | .badgebox + .badge { 14 | /* Move the check mark away when unchecked */ 15 | text-indent: -999999px; 16 | /* Makes the badge's width stay the same checked and unchecked */ 17 | width: 27px; 18 | } 19 | 20 | .badgebox:focus + .badge { 21 | /* Set something to make the badge looks focused */ 22 | /* This really depends on the application, in my case it was: */ 23 | 24 | /* Adding a light border */ 25 | box-shadow: inset 0px 0px 5px; 26 | /* Taking the difference out of the padding */ 27 | } 28 | 29 | .badgebox:checked + .badge { 30 | /* Move the check mark back when checked */ 31 | text-indent: 0; 32 | } 33 | -------------------------------------------------------------------------------- /src/app/modals/answers-modal/answers-modal.html: -------------------------------------------------------------------------------- 1 | 61 | -------------------------------------------------------------------------------- /src/app/modals/answers-modal/answers.modal.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Component, 3 | Input, 4 | OnInit, 5 | AfterViewChecked, 6 | ChangeDetectorRef 7 | } from '@angular/core'; 8 | import { DialogComponent, DialogService } from 'ng2-bootstrap-modal'; 9 | import { FormGroup, FormBuilder, FormControl } from '@angular/forms'; 10 | export interface AnswersModel { 11 | answers: any; 12 | } 13 | 14 | @Component({ 15 | // tslint:disable-next-line:component-selector 16 | selector: 'prompt', 17 | templateUrl: './answers-modal.html', 18 | styleUrls: ['./answers-modal.css'] 19 | }) 20 | export class AnswersComponent 21 | extends DialogComponent 22 | implements AnswersModel, OnInit, AfterViewChecked { 23 | answers: any; 24 | checkboxes = []; 25 | checked = false; 26 | 27 | constructor( 28 | dialogService: DialogService, 29 | private fb: FormBuilder, 30 | private cdRef: ChangeDetectorRef 31 | ) { 32 | super(dialogService); 33 | } 34 | 35 | ngOnInit() {} 36 | 37 | ngAfterViewChecked() { 38 | this.cdRef.detectChanges(); 39 | } 40 | 41 | save(value) { 42 | this.result = JSON.stringify(this.checkboxes); 43 | this.close(); 44 | } 45 | 46 | setCheckboxes(event, i) { 47 | if (event.target.checked) { 48 | this.checkboxes.push(event.target.getAttribute('value')); 49 | } else { 50 | this.checkboxes.splice(i, 1); 51 | } 52 | } 53 | 54 | selectAll(event) { 55 | if (event.target.checked) { 56 | this.answers.forEach((answer, index) => { 57 | this.checked = event.target.checked; 58 | this.checkboxes.push(answer.uuid); 59 | }); 60 | } else { 61 | this.answers.forEach((answer, index) => { 62 | this.checked = false; 63 | }); 64 | this.checkboxes = []; 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/app/modals/concept.modal.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Component, 3 | Input, 4 | OnInit, 5 | AfterViewChecked, 6 | ChangeDetectorRef 7 | } from '@angular/core'; 8 | import { DialogComponent, DialogService } from 'ng2-bootstrap-modal'; 9 | import { FormGroup, FormBuilder, FormControl } from '@angular/forms'; 10 | 11 | export interface ConceptsModel { 12 | title: string; 13 | concepts: any; 14 | } 15 | 16 | @Component({ 17 | // tslint:disable-next-line:component-selector 18 | selector: 'prompt', 19 | template: ``, 61 | styles: [ 62 | ` 63 | .modal-dialog { 64 | overflow-y: initial !important; 65 | } 66 | .modal-body { 67 | overflow-y: auto; 68 | } 69 | ` 70 | ] 71 | }) 72 | export class ConceptsModalComponent 73 | extends DialogComponent 74 | implements ConceptsModel, OnInit, AfterViewChecked { 75 | title: string; 76 | concepts: any; 77 | pconcept: string; 78 | 79 | constructor( 80 | dialogService: DialogService, 81 | private fb: FormBuilder, 82 | private cdRef: ChangeDetectorRef 83 | ) { 84 | super(dialogService); 85 | } 86 | 87 | ngOnInit() {} 88 | 89 | ngAfterViewChecked() { 90 | this.cdRef.detectChanges(); 91 | } 92 | 93 | save(value) { 94 | this.result = value; 95 | this.close(); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/app/modals/confirm.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { DialogComponent, DialogService } from 'ng2-bootstrap-modal'; 3 | export interface ConfirmModel { 4 | title: string; 5 | message: string; 6 | buttonText: string; 7 | } 8 | 9 | @Component({ 10 | selector: 'app-modals', 11 | template: ``, 39 | styles: [ 40 | ` 41 | .actions { 42 | display: flex; 43 | justify-content: flex-end; 44 | } 45 | .actions button { 46 | margin: 0rem 0.5rem; 47 | } 48 | ` 49 | ] 50 | }) 51 | export class ConfirmComponent 52 | extends DialogComponent 53 | implements ConfirmModel { 54 | title: string; 55 | message: string; 56 | buttonText: string; 57 | cancelButtonText: string; 58 | constructor(dialogService: DialogService) { 59 | super(dialogService); 60 | } 61 | 62 | close() { 63 | super.close(); 64 | if (!this.result) { 65 | this.result = 0; 66 | } 67 | } 68 | 69 | confirm() { 70 | this.result = 1; 71 | this.close(); 72 | return this.result; 73 | } 74 | 75 | confirmNewVersion() { 76 | this.result = 2; 77 | this.close(); 78 | return this.result; 79 | } 80 | 81 | saveModal() { 82 | if (this.buttonText) { 83 | if (this.buttonText.indexOf('Update') > -1) { 84 | return true; 85 | } 86 | } 87 | 88 | return false; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/app/modals/encounter-details.modal.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { DialogComponent, DialogService } from 'ng2-bootstrap-modal'; 3 | import { PatientResourceService } from '../Services/openmrs-api/patient-resource.service'; 4 | import { LocationResourceService } from '../Services/openmrs-api/location-resource.service'; 5 | import * as _ from 'lodash'; 6 | @Component({ 7 | selector: 'app-modals', 8 | template: ``, 51 | providers: [PatientResourceService, LocationResourceService] 52 | }) 53 | export class EncounterModalDetailsComponent 54 | extends DialogComponent 55 | implements OnInit { 56 | patients: any; 57 | form: any = { patient: '', location: '' }; 58 | constructor( 59 | dialogService: DialogService, 60 | private patientResourceService: PatientResourceService, 61 | private locationResourceService: LocationResourceService 62 | ) { 63 | super(dialogService); 64 | } 65 | 66 | public ngOnInit() { 67 | this.patientResourceService 68 | .searchPatientByName('test') 69 | .subscribe((patients) => { 70 | const p = _.filter(patients.results, (patient) => !_.isEmpty(patient)); 71 | this.patients = p; 72 | }); 73 | } 74 | 75 | public onSelectPatient($patient) { 76 | this.form.patient = $patient.item.uuid; 77 | } 78 | 79 | public onSelectLocation($location) { 80 | this.form.location = $location.item.uuid; 81 | } 82 | 83 | public submit() { 84 | this.result = this.form; 85 | this.close(); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/app/modals/encounter-viewer-modal.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { DialogComponent, DialogService } from 'ng2-bootstrap-modal'; 3 | 4 | export interface EncounterViewerModel { 5 | form: any; 6 | encounter: any; 7 | } 8 | 9 | @Component({ 10 | // tslint:disable-next-line:component-selector 11 | selector: 'encounter-viewer-modal', 12 | template: ``, 31 | styles: [ 32 | ` 33 | .msg { 34 | white-space: pre-line; 35 | } 36 | ` 37 | ] 38 | }) 39 | export class EncounterViewerModalComponent 40 | extends DialogComponent 41 | implements EncounterViewerModel, OnInit { 42 | form: any; 43 | encounter: any; 44 | constructor(dialogService: DialogService) { 45 | super(dialogService); 46 | } 47 | ngOnInit() {} 48 | } 49 | -------------------------------------------------------------------------------- /src/app/modals/insert-reference-form-modal/insert-reference-forms.modal.css: -------------------------------------------------------------------------------- 1 | .modal-dialog { 2 | overflow-y: initial !important; 3 | } 4 | .modal-body { 5 | overflow-y: auto; 6 | } 7 | option { 8 | padding: 10px; 9 | border-bottom: 1px solid lightgray; 10 | } 11 | .data-list { 12 | max-height: 50px; 13 | } 14 | -------------------------------------------------------------------------------- /src/app/modals/insert-reference-form-modal/insert-reference-forms.modal.html: -------------------------------------------------------------------------------- 1 | 70 | -------------------------------------------------------------------------------- /src/app/modals/insert-reference-form-modal/insert-reference-forms.modal.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Component, 3 | Input, 4 | OnInit, 5 | AfterViewChecked, 6 | ChangeDetectorRef 7 | } from '@angular/core'; 8 | import { DialogComponent, DialogService } from 'ng2-bootstrap-modal'; 9 | import { FormGroup, FormBuilder, FormControl } from '@angular/forms'; 10 | import * as _ from 'lodash'; 11 | export interface ReferenceModel { 12 | title: string; 13 | forms: any; 14 | } 15 | 16 | @Component({ 17 | // tslint:disable-next-line:component-selector 18 | selector: 'prompt', 19 | templateUrl: './insert-reference-forms.modal.html', 20 | styleUrls: ['./insert-reference-forms.modal.css'] 21 | }) 22 | export class InsertReferenceComponent 23 | extends DialogComponent 24 | implements ReferenceModel, OnInit, AfterViewChecked { 25 | title: string; 26 | forms: any; 27 | selected = false; 28 | errorMessage: string; 29 | 30 | constructor( 31 | dialogService: DialogService, 32 | private fb: FormBuilder, 33 | private cdRef: ChangeDetectorRef 34 | ) { 35 | super(dialogService); 36 | } 37 | 38 | ngOnInit() {} 39 | 40 | ngAfterViewChecked() { 41 | this.cdRef.detectChanges(); 42 | } 43 | 44 | save(value) { 45 | const uuid = this.findFormUUID(value.refForm); 46 | 47 | if (_.isUndefined(uuid)) { 48 | this.errorMessage = 'Invalid form.'; 49 | } else { 50 | this.errorMessage = undefined; 51 | value.form = uuid + ' ' + value.refForm; 52 | this.result = value; 53 | this.close(); 54 | } 55 | } 56 | 57 | optionSelected($event) { 58 | this.selected = true; 59 | } 60 | 61 | findFormUUID(formName) { 62 | let uuid; 63 | this.forms.forEach((form) => { 64 | if (form.name === formName) { 65 | uuid = form.uuid; 66 | } 67 | }); 68 | return uuid; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/app/modals/modals-module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { UpdateFormsWizardModalComponent } from './update-forms-wizard-modal/update-forms-wizard-modal.component'; 4 | import { ConfirmComponent } from './confirm.component'; 5 | import { AlertComponent } from './alert.component'; 6 | import { InsertReferenceComponent } from './insert-reference-form-modal/insert-reference-forms.modal'; 7 | import { SchemaModalComponent } from './schema-editor.modal'; 8 | import { PromptComponent } from './prompt.component'; 9 | import { SetMembersModalComponent } from './set-members-modal/set-members-modal.component'; 10 | import { AnswersComponent } from './answers-modal/answers.modal'; 11 | import { ConceptsModalComponent } from './concept.modal'; 12 | import { ReferenceModalComponent } from './reference-form-modal/reference-form.modal'; 13 | import { NavigatorModalComponent } from './navigator.modal'; 14 | import { SaveFormsComponent } from './save-form-modal/save-form-modal'; 15 | import { UpdateFormsWizardComponent } from '../form-editor/update-forms-wizard/update-forms-wizard.component'; 16 | import { EncounterModalDetailsComponent } from './encounter-details.modal'; 17 | import { EncounterViewerModalComponent } from './encounter-viewer-modal'; 18 | import { SharedModule } from '../shared-module'; 19 | @NgModule({ 20 | declarations: [ 21 | ConfirmComponent, 22 | AlertComponent, 23 | PromptComponent, 24 | AnswersComponent, 25 | SaveFormsComponent, 26 | ConceptsModalComponent, 27 | ReferenceModalComponent, 28 | NavigatorModalComponent, 29 | SetMembersModalComponent, 30 | UpdateFormsWizardModalComponent, 31 | UpdateFormsWizardComponent, 32 | InsertReferenceComponent, 33 | SchemaModalComponent, 34 | EncounterModalDetailsComponent, 35 | EncounterViewerModalComponent 36 | ], 37 | entryComponents: [ 38 | ConfirmComponent, 39 | AlertComponent, 40 | PromptComponent, 41 | AnswersComponent, 42 | SaveFormsComponent, 43 | ConceptsModalComponent, 44 | ReferenceModalComponent, 45 | NavigatorModalComponent, 46 | SetMembersModalComponent, 47 | UpdateFormsWizardModalComponent, 48 | InsertReferenceComponent, 49 | SchemaModalComponent, 50 | EncounterModalDetailsComponent, 51 | EncounterViewerModalComponent 52 | ], 53 | imports: [CommonModule, SharedModule], 54 | exports: [ 55 | ConfirmComponent, 56 | AlertComponent, 57 | PromptComponent, 58 | AnswersComponent, 59 | SaveFormsComponent, 60 | ConceptsModalComponent, 61 | ReferenceModalComponent, 62 | NavigatorModalComponent, 63 | SetMembersModalComponent, 64 | UpdateFormsWizardModalComponent, 65 | InsertReferenceComponent, 66 | SchemaModalComponent, 67 | EncounterModalDetailsComponent 68 | ], 69 | providers: [] 70 | }) 71 | export class ModalsModule {} 72 | -------------------------------------------------------------------------------- /src/app/modals/navigator.modal.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from '@angular/core'; 2 | import { DialogComponent, DialogService } from 'ng2-bootstrap-modal'; 3 | import { 4 | FormGroup, 5 | FormControl, 6 | FormBuilder, 7 | Validators 8 | } from '@angular/forms'; 9 | import { FetchFormDetailService } from '../Services/openmrs-api/fetch-form-detail.service'; 10 | import { Observable } from 'rxjs'; 11 | // Observable class extensions 12 | 13 | export interface NavigatorModalModel { 14 | title: string; 15 | referenceElement: string; 16 | schema: any; 17 | prechecked: any; 18 | } 19 | 20 | @Component({ 21 | // tslint:disable-next-line:component-selector 22 | selector: 'prompt', 23 | template: ``, 52 | styles: [ 53 | ` 54 | .modal-body { 55 | position: relative; 56 | padding: 20px; 57 | max-height: 550px; 58 | overflow-y: auto; 59 | } 60 | .title { 61 | display: inline-block; 62 | } 63 | ` 64 | ] 65 | }) 66 | export class NavigatorModalComponent 67 | extends DialogComponent 68 | implements NavigatorModalModel { 69 | title: string; 70 | schema: any; 71 | referenceElement: string; 72 | checkedRefElements: any = []; 73 | res: string; 74 | prechecked: any; 75 | 76 | constructor( 77 | dialogService: DialogService, 78 | private fb: FormBuilder, 79 | private fs: FetchFormDetailService 80 | ) { 81 | super(dialogService); 82 | } 83 | 84 | rfEmitted(refElements: any[]) { 85 | this.checkedRefElements = refElements; 86 | } 87 | 88 | close() { 89 | super.close(); 90 | this.res = ''; 91 | } 92 | 93 | save() { 94 | this.res = JSON.stringify(this.checkedRefElements); 95 | this.result = this.res; 96 | this.close(); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/app/modals/prompt.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from '@angular/core'; 2 | import { DialogComponent, DialogService } from 'ng2-bootstrap-modal'; 3 | import { FormGroup } from '@angular/forms'; 4 | export interface PromptModel { 5 | title: string; 6 | questions: any; 7 | form: FormGroup; 8 | } 9 | 10 | @Component({ 11 | // tslint:disable-next-line:component-selector 12 | selector: 'prompt', 13 | template: `` 70 | }) 71 | export class PromptComponent 72 | extends DialogComponent 73 | implements PromptModel { 74 | title: string; 75 | questions: any; 76 | message = ''; 77 | form: FormGroup; 78 | 79 | constructor(dialogService: DialogService) { 80 | super(dialogService); 81 | } 82 | save() { 83 | this.result = this.form.value; 84 | this.close(); 85 | } 86 | 87 | keyDownFunction($event) { 88 | if ($event.keyCode === 13 && this.form.valid) { 89 | this.save(); 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/app/modals/reference-form-modal/reference-form.madal.css: -------------------------------------------------------------------------------- 1 | .stylish-input-group .input-group-addon { 2 | background: white !important; 3 | } 4 | .stylish-input-group .form-control { 5 | border-right: 0; 6 | box-shadow: 0 0 0; 7 | border-color: #ccc; 8 | } 9 | .stylish-input-group button { 10 | border: 0; 11 | background: transparent; 12 | } 13 | -------------------------------------------------------------------------------- /src/app/modals/reference-form-modal/reference-form.modal.html: -------------------------------------------------------------------------------- 1 |
2 | 44 |
45 | -------------------------------------------------------------------------------- /src/app/modals/reference-form-modal/reference-form.modal.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, OnInit, OnDestroy } from '@angular/core'; 2 | import { DialogComponent, DialogService } from 'ng2-bootstrap-modal'; 3 | import { 4 | FormGroup, 5 | FormControl, 6 | FormBuilder, 7 | Validators 8 | } from '@angular/forms'; 9 | import { FetchFormDetailService } from '../../Services/openmrs-api/fetch-form-detail.service'; 10 | import { NavigatorModalComponent } from './../navigator.modal'; 11 | import { ReferenceForm } from '../../form-editor/reference-forms/reference-form-model'; 12 | import { Observable, Subscription } from 'rxjs'; 13 | 14 | // Observable class extensions 15 | 16 | export interface ReferenceFormModalModel { 17 | title: string; 18 | refElement: string; 19 | } 20 | 21 | @Component({ 22 | // tslint:disable-next-line:component-selector 23 | selector: 'prompt', 24 | templateUrl: './reference-form.modal.html', 25 | styleUrls: ['./reference-form.madal.css'] 26 | }) 27 | export class ReferenceModalComponent 28 | extends DialogComponent 29 | implements ReferenceFormModalModel, OnInit, OnDestroy { 30 | title: string; 31 | refElement: string; // new element to be refd 32 | form: FormGroup; 33 | formAlias: string; // the form alias selected 34 | refForms: any[]; 35 | searchValue = ''; 36 | filteredForms: Observable; 37 | selectField: FormControl = new FormControl('', Validators.required); 38 | errorMessage: string; 39 | subscription: Subscription; 40 | constructor( 41 | dialogService: DialogService, 42 | private fb: FormBuilder, 43 | private fs: FetchFormDetailService 44 | ) { 45 | super(dialogService); 46 | this.form = fb.group({ selectField: this.selectField }); 47 | } 48 | 49 | ngOnInit() { 50 | this.fs.getReferencedFormsDetails().subscribe((res) => { 51 | this.refForms = res; 52 | }); 53 | 54 | // this.filteredForms = this.selectField.valueChanges.map((ref) => { 55 | // return ref ? this.filter(ref) : []; 56 | // }) 57 | } 58 | 59 | // filter(ref){ 60 | // return this.refForms.filter(form => 61 | // form.formName.toLowerCase().indexOf(ref.toLowerCase()) !==-1); 62 | // } 63 | 64 | save(value) { 65 | let selectedForm; 66 | this.refForms.forEach((form) => { 67 | if (form['formName'] === value) { 68 | selectedForm = form; 69 | this.formAlias = form['alias']; 70 | } 71 | }); 72 | 73 | if (selectedForm === undefined) { 74 | this.errorMessage = 'Please select a valid form'; 75 | return; 76 | } 77 | if (selectedForm.ref.uuid) { 78 | this.errorMessage = undefined; 79 | this.fs 80 | .fetchFormMetadata(selectedForm.ref.uuid, true) 81 | .then((res) => 82 | this.fs 83 | .fetchForm(res.resources[0].valueReference, true) 84 | .then((schema) => 85 | this.showNavigatorDialog( 86 | schema, 87 | this.refElement, 88 | `Select ${this.refElement} to reference` 89 | ) 90 | ) 91 | ); 92 | } else { 93 | console.error('formName is undefined!'); 94 | } 95 | } 96 | 97 | showNavigatorDialog(schema, refElement: string, title: string) { 98 | this.subscription = this.dialogService 99 | .addDialog( 100 | NavigatorModalComponent, 101 | { 102 | title: title, 103 | schema: schema, 104 | referenceElement: refElement.toLowerCase() 105 | }, 106 | { backdropColor: 'rgba(0, 0, 0, 0.8)' } 107 | ) 108 | .subscribe((formValue) => { 109 | const i = {}; 110 | i['form'] = this.formAlias; 111 | i[refElement + 's'] = formValue; 112 | 113 | if (formValue !== undefined) { 114 | this.result = JSON.stringify(i); 115 | this.close(); 116 | } 117 | }); 118 | } 119 | 120 | keyDownFunction($event) { 121 | // validate! 122 | if ($event.keyCode === 13 && this.form.valid) { 123 | this.save(this.selectField.value); 124 | } 125 | } 126 | 127 | filterForms(searchString: string) { 128 | searchString = searchString.toLowerCase(); 129 | return this.refForms.filter((form) => { 130 | return form.name.toLowerCase().indexOf(searchString) !== -1; 131 | }); 132 | } 133 | 134 | typeaheadOnSelect(e) { 135 | this.save(e.value); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/app/modals/save-form-modal/save-form-modal.css: -------------------------------------------------------------------------------- 1 | .actions { 2 | display: flex; 3 | justify-content: flex-end; 4 | } 5 | 6 | .actions button { 7 | margin: 0rem 0.5rem; 8 | } 9 | -------------------------------------------------------------------------------- /src/app/modals/save-form-modal/save-form-modal.html: -------------------------------------------------------------------------------- 1 | 61 | -------------------------------------------------------------------------------- /src/app/modals/schema-editor.modal.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, OnInit, ViewChild } from '@angular/core'; 2 | import { DialogComponent, DialogService } from 'ng2-bootstrap-modal'; 3 | import { FormGroup, FormBuilder, FormControl } from '@angular/forms'; 4 | 5 | export interface SchemaEditorModel { 6 | title: string; 7 | schema: any; 8 | } 9 | 10 | @Component({ 11 | // tslint:disable-next-line:component-selector 12 | selector: 'prompt', 13 | template: ``, 27 | styles: [ 28 | ` 29 | .modal-dialog { 30 | overflow-y: initial !important; 31 | } 32 | .modal-body { 33 | overflow-y: auto; 34 | } 35 | ` 36 | ] 37 | }) 38 | export class SchemaModalComponent 39 | extends DialogComponent 40 | implements SchemaEditorModel, OnInit { 41 | title: string; 42 | schema: string; 43 | @ViewChild('editor') editor; 44 | 45 | constructor(dialogService: DialogService, private fb: FormBuilder) { 46 | super(dialogService); 47 | } 48 | 49 | ngOnInit() { 50 | this.editor.getEditor().setOptions({ 51 | printMargin: false, 52 | readOnly: true 53 | }); 54 | this.editor.setTheme('chrome'); 55 | this.editor.setMode('json'); 56 | this.editor.getEditor().setFontSize(16); 57 | this.editor.setText(this.schema); 58 | this.editor.getEditor().scrollToLine(0); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/app/modals/set-members-modal/set-members-modal.component.css: -------------------------------------------------------------------------------- 1 | .modal-dialog { 2 | overflow-y: initial !important; 3 | width: 700px; 4 | } 5 | .modal-content { 6 | height: 550px; 7 | overflow-y: auto; 8 | } 9 | /* Hiding the checkbox, but allowing it to be focused */ 10 | .badgebox { 11 | opacity: 0; 12 | } 13 | 14 | .badgebox + .badge { 15 | /* Move the check mark away when unchecked */ 16 | text-indent: -999999px; 17 | /* Makes the badge's width stay the same checked and unchecked */ 18 | width: 27px; 19 | } 20 | 21 | .badgebox:focus + .badge { 22 | /* Set something to make the badge looks focused */ 23 | /* This really depends on the application, in my case it was: */ 24 | 25 | /* Adding a light border */ 26 | box-shadow: inset 0px 0px 5px; 27 | /* Taking the difference out of the padding */ 28 | } 29 | 30 | .badgebox:checked + .badge { 31 | /* Move the check mark back when checked */ 32 | text-indent: 0; 33 | } 34 | -------------------------------------------------------------------------------- /src/app/modals/set-members-modal/set-members-modal.component.html: -------------------------------------------------------------------------------- 1 | 91 | -------------------------------------------------------------------------------- /src/app/modals/set-members-modal/set-members-modal.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { SetMembersModalComponent } from './set-members-modal.component'; 4 | 5 | describe('SetMembersModalComponent', () => { 6 | let component: SetMembersModalComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [SetMembersModalComponent] 12 | }).compileComponents(); 13 | })); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(SetMembersModalComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should be created', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/modals/set-members-modal/set-members-modal.component.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Component, 3 | Input, 4 | OnInit, 5 | AfterViewChecked, 6 | ChangeDetectorRef 7 | } from '@angular/core'; 8 | import { DialogComponent, DialogService } from 'ng2-bootstrap-modal'; 9 | import { FormGroup, FormBuilder, FormControl } from '@angular/forms'; 10 | import { ConceptService } from '../../Services/openmrs-api/concept.service'; 11 | export interface SetMembersModel { 12 | setMembers: any[]; 13 | } 14 | 15 | interface Questions { 16 | label: string; 17 | concept: string; 18 | answers: Answer[]; 19 | } 20 | 21 | interface Answer { 22 | label: string; 23 | concept: string; 24 | conceptMappings: any[]; 25 | } 26 | 27 | @Component({ 28 | // tslint:disable-next-line:component-selector 29 | selector: 'set-members-modal', 30 | templateUrl: './set-members-modal.component.html', 31 | styleUrls: ['./set-members-modal.component.css'] 32 | }) 33 | export class SetMembersModalComponent 34 | extends DialogComponent 35 | implements SetMembersModel, OnInit, AfterViewChecked { 36 | setMembersModel: any; 37 | setMembers: any[]; 38 | questionsChecked: Questions[] = []; 39 | 40 | constructor( 41 | dialogService: DialogService, 42 | private fb: FormBuilder, 43 | private cs: ConceptService, 44 | private cdRef: ChangeDetectorRef 45 | ) { 46 | super(dialogService); 47 | } 48 | 49 | ngOnInit() {} 50 | 51 | ngAfterViewChecked() { 52 | this.cdRef.detectChanges(); 53 | } 54 | 55 | save() { 56 | this.result = JSON.stringify(this.questionsChecked); 57 | this.questionsChecked = []; 58 | this.close(); 59 | } 60 | 61 | setQuestions(event, i) { 62 | if (event.target.checked) { 63 | const question: Questions = { 64 | label: this.setMembers[i].display, 65 | concept: this.setMembers[i].uuid, 66 | answers: [] 67 | }; 68 | this.questionsChecked.push(question); 69 | } else { 70 | this.questionsChecked.forEach((question, index) => { 71 | if (question.concept === this.setMembers[i].uuid) { 72 | this.questionsChecked.splice(index, 1); 73 | return; 74 | } 75 | }); 76 | } 77 | } 78 | 79 | setAnswers(event, i, j) { 80 | const $ans = this.setMembers[i].answers[j]; 81 | if (event.target.checked) { 82 | const answer: Answer = { 83 | label: $ans.display, 84 | concept: $ans.uuid, 85 | conceptMappings: this.cs.createMappingsValue($ans.mappings) 86 | }; 87 | 88 | this.questionsChecked.forEach((question, index) => { 89 | if ( 90 | question.concept === this.setMembers[i].uuid && 91 | question.answers.indexOf(answer) === -1 92 | ) { 93 | this.questionsChecked[index]['answers'].push(answer); 94 | return; 95 | } 96 | }); 97 | } else { 98 | this.questionsChecked.forEach((question, qIndex) => { 99 | question.answers.forEach((answer, aIndex) => { 100 | if (answer.concept === this.setMembers[i].answers[j].uuid) { 101 | this.questionsChecked[qIndex].answers.splice(aIndex, 1); 102 | return; 103 | } 104 | }); 105 | }); 106 | } 107 | } 108 | 109 | isQuestionChecked(index) { 110 | let isChecked = false; 111 | this.questionsChecked.forEach((question) => { 112 | if (this.setMembers[index].display === question.label) { 113 | isChecked = true; 114 | } 115 | }); 116 | return isChecked; 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/app/modals/update-forms-wizard-modal/update-forms-wizard-modal.component.css: -------------------------------------------------------------------------------- 1 | @media (min-width: 768px) { 2 | .modal-xl { 3 | width: 90%; 4 | margin: auto; 5 | max-width: 1200px; 6 | } 7 | } 8 | 9 | .modal-body { 10 | overflow-y: scroll; 11 | max-height: 800px; 12 | } 13 | -------------------------------------------------------------------------------- /src/app/modals/update-forms-wizard-modal/update-forms-wizard-modal.component.html: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /src/app/modals/update-forms-wizard-modal/update-forms-wizard-modal.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { UpdateFormsWizardModalComponent } from './update-forms-wizard-modal.component'; 4 | 5 | describe('UpdateFormsWizardModalComponent', () => { 6 | let component: UpdateFormsWizardModalComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [UpdateFormsWizardModalComponent] 12 | }).compileComponents(); 13 | })); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(UpdateFormsWizardModalComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should be created', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/modals/update-forms-wizard-modal/update-forms-wizard-modal.component.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Component, 3 | Input, 4 | OnInit, 5 | AfterViewChecked, 6 | ChangeDetectorRef 7 | } from '@angular/core'; 8 | import { DialogComponent, DialogService } from 'ng2-bootstrap-modal'; 9 | import { FormGroup, FormBuilder, FormControl } from '@angular/forms'; 10 | 11 | export interface UpdateFormsWizardModel { 12 | oldComponentUUID: any; 13 | componentMetadata: any; 14 | selectedForms: any; 15 | } 16 | 17 | @Component({ 18 | // tslint:disable-next-line:component-selector 19 | selector: 'prompt', 20 | templateUrl: './update-forms-wizard-modal.component.html', 21 | styleUrls: ['./update-forms-wizard-modal.component.css'] 22 | }) 23 | export class UpdateFormsWizardModalComponent 24 | extends DialogComponent 25 | implements UpdateFormsWizardModel, OnInit, AfterViewChecked { 26 | selectedForms: any; 27 | componentMetadata: any; 28 | oldComponentUUID: any; 29 | constructor( 30 | dialogService: DialogService, 31 | private fb: FormBuilder, 32 | private cdRef: ChangeDetectorRef 33 | ) { 34 | super(dialogService); 35 | } 36 | 37 | ngOnInit() {} 38 | 39 | ngAfterViewChecked() { 40 | this.cdRef.detectChanges(); 41 | } 42 | 43 | finish($event) { 44 | this.result = 'finished'; 45 | if ($event) { 46 | super.close(); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/app/pipes/filter.pipe.spec.ts: -------------------------------------------------------------------------------- 1 | import { FilterPipe } from './filter.pipe'; 2 | 3 | describe('FilterPipe', () => { 4 | it('create an instance', () => { 5 | const pipe = new FilterPipe(); 6 | expect(pipe).toBeTruthy(); 7 | }); 8 | }); 9 | -------------------------------------------------------------------------------- /src/app/pipes/filter.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | import * as _ from 'lodash'; 3 | @Pipe({ 4 | name: 'filter' 5 | }) 6 | export class FilterPipe implements PipeTransform { 7 | transform(value: any[], searchValue: string, searchObject: string): any { 8 | if (!value || searchValue === '') { 9 | return value; 10 | } 11 | const results = []; 12 | searchValue = searchValue.toLowerCase(); 13 | _.forEach(value, (val) => { 14 | if (_.includes(val[searchObject].toLowerCase(), searchValue)) { 15 | results.push(val); 16 | } 17 | }); 18 | return results; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/app/pipes/search-form-filter.pipe.spec.ts: -------------------------------------------------------------------------------- 1 | import { SearchFormFilterPipe } from './search-form-filter.pipe'; 2 | 3 | describe('SearchFormFilterPipe', () => { 4 | it('create an instance', () => { 5 | const pipe = new SearchFormFilterPipe(); 6 | expect(pipe).toBeTruthy(); 7 | }); 8 | }); 9 | -------------------------------------------------------------------------------- /src/app/pipes/search-form-filter.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | import * as _ from 'lodash'; 3 | @Pipe({ 4 | name: 'formfilter' 5 | }) 6 | export class SearchFormFilterPipe implements PipeTransform { 7 | transform(forms: any[], value: string, filter: string): any { 8 | if (!forms || value === '') { 9 | return forms; 10 | } 11 | 12 | value = value.toLowerCase(); 13 | if (_.isUndefined(filter) || _.isEqual(filter, 'name')) { 14 | return _.filter(forms, (form) => 15 | _.includes(form['name'].toLowerCase(), value) 16 | ); 17 | } else if (_.isEqual(filter, 'published')) { 18 | return _.filter( 19 | forms, 20 | (form) => 21 | _.includes(form['name'].toLowerCase(), value) && 22 | form['published'] === true 23 | ); 24 | } else if (_.isEqual(filter, 'version')) { 25 | return _.filter(forms, (form) => 26 | _.includes(form['name'].toLowerCase(), value) 27 | ); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/app/pipes/string_to_number.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | 3 | @Pipe({ name: 'str2num' }) 4 | export class Str2Num implements PipeTransform { 5 | transform(input: string): number { 6 | const num = parseFloat(input); 7 | if (num === 0.01) { 8 | return parseFloat('1.0'); 9 | } else { 10 | return num + 0.1; 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/app/shared-module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { NavigatorComponent } from '../app/form-editor/navigator/navigator.component'; 4 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; 5 | import { AppMaterialModule } from './app-material-module'; 6 | import { AceEditorModule } from 'ng2-ace-editor'; 7 | import { ClipboardModule } from 'ngx-clipboard'; 8 | import { TypeaheadModule } from 'ngx-bootstrap'; 9 | import { LocalStorageService } from './Services/storage/local-storage.service'; 10 | import { SessionStorageService } from './Services/storage/session-storage.service'; 11 | import { SessionService } from './Services/storage/session.service'; 12 | import { FormEntryModule } from 'ngx-openmrs-formentry/dist/ngx-formentry'; 13 | @NgModule({ 14 | declarations: [NavigatorComponent], 15 | imports: [ 16 | CommonModule, 17 | FormsModule, 18 | ReactiveFormsModule, 19 | AppMaterialModule, 20 | AceEditorModule, 21 | ClipboardModule, 22 | TypeaheadModule.forRoot(), 23 | FormEntryModule 24 | ], 25 | exports: [ 26 | NavigatorComponent, 27 | ReactiveFormsModule, 28 | FormsModule, 29 | AppMaterialModule, 30 | AceEditorModule, 31 | ClipboardModule, 32 | TypeaheadModule, 33 | FormEntryModule 34 | ], 35 | providers: [LocalStorageService, SessionStorageService, SessionService] 36 | }) 37 | export class SharedModule {} 38 | -------------------------------------------------------------------------------- /src/app/view-forms/view-forms.component.css: -------------------------------------------------------------------------------- 1 | .addButton > span { 2 | display: flex !important; 3 | align-items: center !important; 4 | } 5 | 6 | .container-f { 7 | margin-top: 4rem; 8 | } 9 | 10 | .panel { 11 | background-color: #fff; 12 | box-shadow: 0px 2px 2px #aaa; 13 | border-color: lightgray; 14 | font-family: 'Inter', sans-serif; 15 | } 16 | 17 | .panel-table .panel-body { 18 | padding: 0; 19 | /* background-color:#fff; 20 | box-shadow:0px 2px 2px #aaa; */ 21 | } 22 | 23 | .panel-table .panel-body .table-bordered { 24 | border-style: none; 25 | margin: 0; 26 | } 27 | 28 | .panel-table .panel-body .table-bordered > thead > tr { 29 | font-weight: 900 !important; 30 | } 31 | 32 | .panel-table .panel-body .table-bordered > thead > tr > th:first-of-type { 33 | text-align: left; 34 | width: 100px; 35 | } 36 | 37 | .panel-table .panel-body .table-bordered > thead > tr > th:last-of-type, 38 | .panel-table .panel-body .table-bordered > tbody > tr > td:last-of-type { 39 | border-right: 0px; 40 | } 41 | 42 | .panel-table .panel-body .table-bordered > thead > tr > th:first-of-type, 43 | .panel-table .panel-body .table-bordered > tbody > tr > td:first-of-type { 44 | border-left: 0px; 45 | } 46 | 47 | .panel-table .panel-body .table-bordered > tbody > tr:first-of-type > td { 48 | border-bottom: 0px; 49 | } 50 | 51 | .panel-table .panel-body .table-bordered > thead > tr:first-of-type > th { 52 | border-top: 0px; 53 | } 54 | 55 | .panel-table .panel-footer .pagination { 56 | margin: 0; 57 | } 58 | 59 | .panel-primary > .panel-heading { 60 | color: #fff; 61 | /* background-color: #3f51b5; */ 62 | background-color: rgb(243 244 246); 63 | /* background-color: rgb(229 231 235); */ 64 | /* border-color: #808080; */ 65 | border-color: lightgray; 66 | } 67 | 68 | /* 69 | used to vertically center elements, may need modification if you're not using default sizes. 70 | */ 71 | .panel-table .panel-footer .col { 72 | line-height: 34px; 73 | height: 34px; 74 | } 75 | 76 | .panel-table .panel-heading .col h3 { 77 | line-height: 30px; 78 | height: 30px; 79 | } 80 | 81 | .panel-table .panel-body .table-bordered > tbody > tr > td { 82 | line-height: 34px; 83 | } 84 | 85 | .formName { 86 | font-size: 14px; 87 | } 88 | 89 | .stylish-input-group .input-group-addon { 90 | background: white !important; 91 | } 92 | 93 | .stylish-input-group .form-control { 94 | border-right: 0; 95 | box-shadow: 0 0 0; 96 | border-color: #ccc; 97 | } 98 | 99 | .stylish-input-group button { 100 | border: 0; 101 | background: transparent; 102 | } 103 | 104 | .material-icons.edit { 105 | color: green; 106 | } 107 | 108 | .material-icons.folder_open { 109 | color: rgb(209 213 219); 110 | } 111 | 112 | .navbar-btn { 113 | margin: 5px 5px 0px 5px; 114 | } 115 | 116 | .btn.btn-circle { 117 | border-radius: 5px; 118 | } 119 | 120 | .btn-create { 121 | color: #1ac91a; 122 | } 123 | 124 | /* .navbar-nav.navbar-right .btn:not(.collapsed) { 125 | background-color: rgb(111, 84, 153); 126 | border-color: rgb(111, 84, 153); 127 | color: rgb(255, 255, 255); 128 | } */ 129 | 130 | a:hover { 131 | text-decoration: none; 132 | } 133 | 134 | .panel-title { 135 | color: black; 136 | } 137 | 138 | .restoreMsg { 139 | display: flex; 140 | justify-content: center; 141 | margin: 2rem; 142 | } 143 | 144 | .menuItem { 145 | font-weight: 500; 146 | color: white; 147 | font-family: 'Roboto'; 148 | font-size: 14px; 149 | } 150 | 151 | .name { 152 | font-size: 18px; 153 | color: white; 154 | font-weight: 600; 155 | } 156 | 157 | .space { 158 | margin-left: 50px; 159 | } 160 | 161 | .draftForm { 162 | color: #3c763d; 163 | background-color: #dff0d8; 164 | border-color: #d6e9c6; 165 | margin: 5px 5px 5px 50px; 166 | } 167 | 168 | .fill { 169 | flex: 1 1 auto; 170 | } 171 | 172 | .dropdown-menu { 173 | color: black; 174 | font-family: 'Roboto'; 175 | font-weight: 500; 176 | } 177 | 178 | .dropdown-menu > li { 179 | margin-left: 5px; 180 | display: flex; 181 | align-items: center; 182 | } 183 | 184 | .logoutLink:hover { 185 | background-color: rgba(243, 244, 246); 186 | } 187 | 188 | .emptyState { 189 | display: flex; 190 | padding: 2rem; 191 | align-items: center; 192 | flex-flow: column wrap; 193 | justify-content: space-between; 194 | } 195 | 196 | .helperText { 197 | color: rgb(107 114 128); 198 | margin: 0.5rem 0rem; 199 | } 200 | -------------------------------------------------------------------------------- /src/app/view-forms/view-forms.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ViewFormsComponent } from './view-forms.component'; 4 | 5 | describe('ViewFormsComponent', () => { 6 | let component: ViewFormsComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ViewFormsComponent] 12 | }).compileComponents(); 13 | })); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(ViewFormsComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should be created', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AMPATH/ngx-openmrs-formbuilder/beeca40af54d73cb289e085d7012969c05094083/src/assets/.gitkeep -------------------------------------------------------------------------------- /src/assets/inter.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AMPATH/ngx-openmrs-formbuilder/beeca40af54d73cb289e085d7012969c05094083/src/assets/inter.woff -------------------------------------------------------------------------------- /src/assets/loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AMPATH/ngx-openmrs-formbuilder/beeca40af54d73cb289e085d7012969c05094083/src/assets/loader.gif -------------------------------------------------------------------------------- /src/assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/assets/roboto-mono.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AMPATH/ngx-openmrs-formbuilder/beeca40af54d73cb289e085d7012969c05094083/src/assets/roboto-mono.woff -------------------------------------------------------------------------------- /src/assets/screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AMPATH/ngx-openmrs-formbuilder/beeca40af54d73cb289e085d7012969c05094083/src/assets/screen.png -------------------------------------------------------------------------------- /src/assets/select.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // The file contents for the current environment will overwrite these during build. 2 | // The build system defaults to the dev environment which uses `environment.ts`, but if you do 3 | // `ng build --env=prod` then `environment.prod.ts` will be used instead. 4 | // The list of which env maps to which file can be found in `.angular-cli.json`. 5 | 6 | export const environment = { 7 | production: false, 8 | date: '1656417287254', 9 | version: '1.0' 10 | }; 11 | -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AMPATH/ngx-openmrs-formbuilder/beeca40af54d73cb289e085d7012969c05094083/src/favicon.ico -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Ampath Form Builder 6 | 7 | 8 | 9 | 10 | 16 | 21 | 22 | 27 | 28 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppModule); 12 | -------------------------------------------------------------------------------- /src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE9, IE10 and IE11 requires all of the following polyfills. **/ 22 | // import 'core-js/es6/symbol'; 23 | // import 'core-js/es6/object'; 24 | // import 'core-js/es6/function'; 25 | // import 'core-js/es6/parse-int'; 26 | // import 'core-js/es6/parse-float'; 27 | // import 'core-js/es6/number'; 28 | // import 'core-js/es6/math'; 29 | // import 'core-js/es6/string'; 30 | // import 'core-js/es6/date'; 31 | // import 'core-js/es6/array'; 32 | // import 'core-js/es6/regexp'; 33 | // import 'core-js/es6/map'; 34 | // import 'core-js/es6/weak-map'; 35 | // import 'core-js/es6/set'; 36 | 37 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 38 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 39 | 40 | /** Evergreen browsers require these. **/ 41 | import 'core-js/es6/reflect'; 42 | 43 | /** 44 | * Required to support Web Animations `@angular/animation`. 45 | * Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation 46 | **/ 47 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 48 | 49 | /*************************************************************************************************** 50 | * Zone JS is required by Angular itself. 51 | */ 52 | import 'zone.js/dist/zone'; // Included with Angular CLI. 53 | 54 | /*************************************************************************************************** 55 | * APPLICATION IMPORTS 56 | */ 57 | 58 | /** 59 | * Date, currency, decimal and percent pipes. 60 | * Needed for: All but Chrome, Firefox, Edge, IE11 and Safari 10 61 | */ 62 | // import 'intl'; // Run `npm install --save intl`. 63 | /** 64 | * Need to import at least one locale-data with intl. 65 | */ 66 | // import 'intl/locale-data/jsonp/en'; 67 | -------------------------------------------------------------------------------- /src/styles.scss: -------------------------------------------------------------------------------- 1 | @import '~@angular/material/prebuilt-themes/indigo-pink.css'; 2 | @import url('https://fonts.googleapis.com/icon?family=Material+Icons'); 3 | @import url('https://fonts.googleapis.com/css?family=Roboto'); 4 | @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500&display=swap'); 5 | @import '../node_modules/bootstrap/less/variables.less'; 6 | @import '~@angular/material/theming'; 7 | 8 | $custom-typography: mat-typography-config( 9 | $font-family: 'Inter' 10 | ); 11 | 12 | @include mat-core($custom-typography); 13 | 14 | $font-family-sans-serif: 'Inter', 'Helvetica Neue', Helvetica, Arial, sans-serif; 15 | 16 | .mat-sidenav-content { 17 | position: relative; 18 | transform: translate3d(0, 0, 0); 19 | display: block; 20 | /* height: fill-available !important; */ 21 | overflow-x: hidden !important; 22 | } 23 | 24 | html { 25 | height: 100%; 26 | box-sizing: border-box; 27 | } 28 | 29 | *, 30 | *:before, 31 | *:after { 32 | box-sizing: inherit; 33 | } 34 | 35 | body { 36 | position: relative; 37 | margin: 0; 38 | min-height: 100%; 39 | background: #f9fafc; 40 | } 41 | 42 | .select-input { 43 | -webkit-appearance: none; 44 | background-image: url('./assets/select.svg'); 45 | background-position: right 0.5rem center; 46 | background-repeat: no-repeat; 47 | background-size: 1.5em 1.5em; 48 | padding-right: 2.5rem; 49 | } 50 | -------------------------------------------------------------------------------- /src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/long-stack-trace-zone'; 4 | import 'zone.js/dist/proxy.js'; 5 | import 'zone.js/dist/sync-test'; 6 | import 'zone.js/dist/jasmine-patch'; 7 | import 'zone.js/dist/async-test'; 8 | import 'zone.js/dist/fake-async-test'; 9 | import { getTestBed } from '@angular/core/testing'; 10 | import { 11 | BrowserDynamicTestingModule, 12 | platformBrowserDynamicTesting 13 | } from '@angular/platform-browser-dynamic/testing'; 14 | 15 | // Unfortunately there's no typing for the `__karma__` variable. Just declare it as any. 16 | declare const __karma__: any; 17 | declare const require: any; 18 | 19 | // Prevent Karma from running prematurely. 20 | __karma__.loaded = function () {}; 21 | 22 | // First, initialize the Angular testing environment. 23 | getTestBed().initTestEnvironment( 24 | BrowserDynamicTestingModule, 25 | platformBrowserDynamicTesting() 26 | ); 27 | // Then we find all the tests. 28 | const context = require.context('./', true, /\.spec\.ts$/); 29 | // And load the modules. 30 | context.keys().map(context); 31 | // Finally, start Karma to run the tests. 32 | __karma__.start(); 33 | -------------------------------------------------------------------------------- /src/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "baseUrl": "./", 6 | "module": "es2015", 7 | "types": [] 8 | }, 9 | "exclude": ["test.ts", "**/*.spec.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /src/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/spec", 5 | "baseUrl": "./", 6 | "module": "commonjs", 7 | "target": "es5", 8 | "types": ["jasmine", "node"] 9 | }, 10 | "files": ["test.ts", "polyfills.ts"], 11 | "include": ["**/*.spec.ts", "**/*.d.ts"] 12 | } 13 | -------------------------------------------------------------------------------- /src/typings.d.ts: -------------------------------------------------------------------------------- 1 | /* SystemJS module definition */ 2 | declare var module: NodeModule; 3 | interface NodeModule { 4 | id: string; 5 | } 6 | 7 | declare module '*.json' { 8 | const value: any; 9 | export default value; 10 | } 11 | -------------------------------------------------------------------------------- /src/version.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": { 3 | "name": "Ampath_OpenMRS_Form_Builder", 4 | "buildDate": "Mon Oct 30 2017 17:05:29 GMT+0300 (EAT)", 5 | "version": "1.1-alpha" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "paths": { 5 | "@angular/*": ["node_modules/@angular/*"] 6 | }, 7 | "importHelpers": true, 8 | "outDir": "./dist/out-tsc", 9 | "sourceMap": true, 10 | "declaration": false, 11 | "moduleResolution": "node", 12 | "emitDecoratorMetadata": true, 13 | "experimentalDecorators": true, 14 | "target": "es5", 15 | "types": ["node", "jasmine"], 16 | "typeRoots": ["./node_modules/@types"], 17 | "lib": ["es2016", "dom"], 18 | "module": "es2015", 19 | "baseUrl": "./" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": ["node_modules/codelyzer"], 3 | "rules": { 4 | "arrow-return-shorthand": true, 5 | "callable-types": true, 6 | "class-name": true, 7 | "comment-format": [true, "check-space"], 8 | "curly": true, 9 | "eofline": true, 10 | "forin": true, 11 | "import-blacklist": [true], 12 | "import-spacing": true, 13 | "indent": [true, "spaces"], 14 | "interface-over-type-literal": true, 15 | "label-position": true, 16 | "max-line-length": [true, 140], 17 | "member-access": false, 18 | "member-ordering": [ 19 | true, 20 | { 21 | "order": [ 22 | "static-field", 23 | "instance-field", 24 | "static-method", 25 | "instance-method" 26 | ] 27 | } 28 | ], 29 | "no-arg": true, 30 | "no-bitwise": true, 31 | "no-console": [true, "debug", "info", "time", "timeEnd", "trace"], 32 | "no-construct": true, 33 | "no-debugger": true, 34 | "no-duplicate-super": true, 35 | "no-empty": false, 36 | "no-empty-interface": true, 37 | "no-eval": true, 38 | "no-inferrable-types": [true, "ignore-params"], 39 | "no-misused-new": true, 40 | "no-non-null-assertion": true, 41 | "no-shadowed-variable": true, 42 | "no-string-literal": false, 43 | "no-string-throw": true, 44 | "no-switch-case-fall-through": true, 45 | "no-trailing-whitespace": true, 46 | "no-unnecessary-initializer": true, 47 | "no-unused-expression": true, 48 | "no-var-keyword": true, 49 | "object-literal-sort-keys": false, 50 | "one-line": [ 51 | true, 52 | "check-open-brace", 53 | "check-catch", 54 | "check-else", 55 | "check-whitespace" 56 | ], 57 | "prefer-const": true, 58 | "quotemark": [true, "single"], 59 | "radix": true, 60 | "semicolon": [true, "always"], 61 | "triple-equals": [true, "allow-null-check"], 62 | "typedef-whitespace": [ 63 | true, 64 | { 65 | "call-signature": "nospace", 66 | "index-signature": "nospace", 67 | "parameter": "nospace", 68 | "property-declaration": "nospace", 69 | "variable-declaration": "nospace" 70 | } 71 | ], 72 | "unified-signatures": true, 73 | "variable-name": false, 74 | "whitespace": [ 75 | true, 76 | "check-branch", 77 | "check-decl", 78 | "check-operator", 79 | "check-separator", 80 | "check-type" 81 | ], 82 | "directive-selector": [true, "attribute", "app", "camelCase"], 83 | "component-selector": [true, "element", "app", "kebab-case"], 84 | "use-input-property-decorator": true, 85 | "use-output-property-decorator": true, 86 | "use-host-property-decorator": true, 87 | "no-input-rename": true, 88 | "no-output-rename": true, 89 | "use-life-cycle-interface": true, 90 | "use-pipe-transform-interface": true, 91 | "component-class-suffix": true, 92 | "directive-class-suffix": true 93 | } 94 | } 95 | --------------------------------------------------------------------------------