├── .gitignore ├── README.md ├── pom.xml └── src └── main ├── angular-app ├── .editorconfig ├── .gitignore ├── README.md ├── angular.json ├── e2e │ ├── protractor.conf.js │ ├── src │ │ ├── app.e2e-spec.ts │ │ └── app.po.ts │ └── tsconfig.e2e.json ├── package-lock.json ├── package.json ├── proxy.config.js ├── src │ ├── .browserslistrc │ ├── app │ │ ├── app-routing.module.ts │ │ ├── app.component.html │ │ ├── app.component.scss │ │ ├── app.component.spec.ts │ │ ├── app.component.ts │ │ ├── app.config.ts │ │ ├── app.module.ts │ │ ├── home │ │ │ ├── home.module.ts │ │ │ ├── home.routing.module.ts │ │ │ └── home │ │ │ │ ├── home.component.html │ │ │ │ ├── home.component.scss │ │ │ │ ├── home.component.spec.ts │ │ │ │ └── home.component.ts │ │ ├── models │ │ │ ├── ajax-request-data.interface.ts │ │ │ ├── products.interface.ts │ │ │ └── purchase-request.interface.ts │ │ ├── purchase-order │ │ │ ├── purchase-order.module.ts │ │ │ ├── purchase-order.routing.module.ts │ │ │ └── purchase-order │ │ │ │ ├── purchase-order.component.html │ │ │ │ ├── purchase-order.component.scss │ │ │ │ ├── purchase-order.component.spec.ts │ │ │ │ └── purchase-order.component.ts │ │ ├── services │ │ │ ├── fluig-oauth.service.ts │ │ │ ├── process-management.service.ts │ │ │ └── products-converter.service.ts │ │ └── supplier-quotation │ │ │ ├── supplier-quotation.module.ts │ │ │ ├── supplier-quotation.routing.module.ts │ │ │ └── supplier-quotation │ │ │ ├── supplier-quotation.component.html │ │ │ ├── supplier-quotation.component.scss │ │ │ ├── supplier-quotation.component.spec.ts │ │ │ └── supplier-quotation.component.ts │ ├── assets │ │ └── .gitkeep │ ├── browserslist │ ├── environments │ │ ├── environment.prod.ts │ │ └── environment.ts │ ├── favicon.ico │ ├── index.html │ ├── karma.conf.js │ ├── main.ts │ ├── polyfills.ts │ ├── scripts │ │ └── oauth │ │ │ ├── enc-base64-min.js │ │ │ ├── hmac-sha1.js │ │ │ ├── hmac-sha256.js │ │ │ └── oauth-1.0a.js │ ├── styles.scss │ ├── test.ts │ ├── tsconfig.app.json │ ├── tsconfig.spec.json │ └── tslint.json ├── tsconfig.json └── tslint.json ├── resources ├── app_angular_poui.properties ├── app_angular_poui_en_US.properties ├── app_angular_poui_es.properties ├── app_angular_poui_pt_BR.properties ├── app_angular_thf.properties ├── app_angular_thf_en_US.properties ├── app_angular_thf_es.properties ├── app_angular_thf_pt_BR.properties ├── application.info └── view.ftl └── webapp └── WEB-INF ├── jboss-web.xml └── web.xml /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | dist/ 3 | node/ 4 | node_modules/ 5 | src/main/webapp/resources/ 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Angular + PO UI no fluig 2 | 3 | O objetivo desse repositório é mostrar uma **técnica** de como adicionar uma aplicação com Angular + PO UI no fluig. 4 | 5 | Essa técnica consiste em criar uma *widget* e adicionar uma aplicação angular dentro dela. Posteriormente podemos criar ou editar uma página e adicionar essa *widget* nela. 6 | 7 | Abaixo estão os passos necessários para realizar essa técnica com todas as configurações necessárias. 8 | 9 | --- 10 | **NOTA** 11 | 12 | Nesta documentação contém um exemplo de construção de widget com framework Angular, que também pode ser usada nos demais frameworks similares. 13 | Essa é uma **versão beta** liberada apenas para parceiros de desenvolvimento Fluig. Caso encontre algum problema, orientamos reportar para a equipe de Parceiros do Fluig. 14 | --- 15 | 16 | 17 | ## Pré requisitos 18 | 19 | O projeto foi testado e validado com as versões abaixo. Versões superiores ou inferiores podem ser utilizadas porém erros/problemas podem acontecer. 20 | 21 | - Node 10.15.x 22 | - NPM 6.4.x 23 | - Angular CLI: 7.1.3 24 | - Java 7 ou superior 25 | - Maven 3.6.0 26 | 27 | 28 | ## Executando o projeto 29 | 30 | Para executar esse projeto de exemplo, devemos executar somente o comando a seguir: 31 | 32 | ```bash 33 | mvn clean install 34 | ``` 35 | 36 | Após a execução, é necessário realizar o deploy do artefato **app_angular_poui.war** pela [Central de Componentes](http://tdn.totvs.com/display/public/fluig/Central+de+componentes) do fluig. Depois, basta criar ou editar uma página já existente e adicionar a widget **Angular APP PO UI**. 37 | 38 | Abaixo foi disponibilizado uma documentação detalhada de como criar esse projeto. 39 | 40 | 41 | ## Passo a passo 42 | 43 | 44 | ### Passo 1 - Criando a widget 45 | 46 | Podemos criar uma widget de duas formas. Pela **documentação oficial** ou através do *plugin* para a IDE Eclipse, **fluig Studio**. 47 | 48 | Documentação de criação de um widget: 49 | http://tdn.totvs.com/display/public/fluig/Widgets 50 | 51 | Guia de instalação fluig Studio: 52 | http://tdn.totvs.com/pages/releaseview.action?pageId=73078179 53 | 54 | 55 | ### Passo 2 - Adicionando o APP Angular 56 | 57 | Caso você ainda não tenha instalado o pacote @angular/cli, instale-o via npm. 58 | 59 | ```bash 60 | npm i -g @angular/cli@9.1.0 61 | ``` 62 | 63 | Essa próxima etapa é relativamente simples. No terminal, dentro do diretório `src/main/` devemos executar o comando: 64 | 65 | ```javascript 66 | ng new --skip-install 67 | ``` 68 | 69 | O parâmetro `--skip-install` permite criar o projeto, contudo, não instalará as dependências automaticamente. Isso porque devemos alterar a versão do Angular antes. Faremos a instalação posteriormente. 70 | 71 | 72 | ### Passo 3 - Configurando o plugin maven 73 | 74 | Após adicionar o APP Angular, devemos configurar o plugin [frontend-maven-plugin](https://github.com/eirslett/frontend-maven-plugin) para realizar o gerenciamento da versão do Node, instalação e compilação do projeto. 75 | 76 | ```xml 77 | com.github.eirslett 78 | frontend-maven-plugin 79 | ``` 80 | 81 | O primeiro `execution` deve realizar a instalação do node e do npm. Então devemos adicionar, dentro da *tag* `executions` devemos adicionar as seguintes instruções: 82 | 83 | ```xml 84 | 85 | install node and npm 86 | 87 | install-node-and-npm 88 | 89 | generate-resources 90 | 91 | ``` 92 | 93 | Depois devemos adicionar todos os passos para executar a instalação e compilação do nosso projeto, chamando os `scripts` npm configurados no **package.json**. 94 | 95 | ```xml 96 | 97 | 98 | npm install 99 | 100 | npm 101 | 102 | 103 | install 104 | 105 | 106 | 107 | 108 | 109 | npm run 110 | 111 | npm 112 | 113 | 114 | run build-prod 115 | 116 | 117 | ``` 118 | 119 | [Veja a configuração final aqui.](https://github.com/fluig/app-angular-poui/blob/master/pom.xml#L14) 120 | 121 | 122 | ### Passo 4 - Configurando APP Angular 123 | 124 | 125 | Devemos agora configurar o APP Angular para funcionar dentro da widget, para isso, vamos alterar a confiruração dos parâmetros `outputPath` e `deployUrl` do arquivo [angular.json](https://github.com/fluig/app-angular-poui/blob/master/src/main/angular-app/angular.json). 126 | 127 | **outputPath**: 128 | 129 | ```json 130 | "outputPath": "../webapp/resources", 131 | ``` 132 | 133 | **deployUrl**: 134 | 135 | ```json 136 | "deployUrl": "/app_angular_poui/resources/", 137 | ``` 138 | 139 | **Atenção:** O valor **app_angular_poui** obrigatoriamente precisa ser o **code** da widget. 140 | 141 | 142 | ### Passo 5 - Configurando APP para Paths dinâmicos 143 | 144 | 145 | Precisamos primeiro adicionar o arquivo [app.config.ts](https://github.com/fluig/app-angular-poui/blob/master/src/main/angular-app/src/app/app.config.ts) no projeto e depois configurar no [módulo principal](https://github.com/fluig/app-angular-poui/blob/master/src/main/angular-app/src/app/app.module.ts) da nossa aplicação. 146 | 147 | ```ts 148 | ... 149 | import { APP_CONFIG } from './app.config'; 150 | import { APP_BASE_HREF } from '@angular/common'; 151 | ... 152 | providers: [ 153 | ... 154 | { provide: APP_BASE_HREF, useValue: APP_CONFIG.APP_BASE || '/' } 155 | ], 156 | ``` 157 | 158 | Isso garante que o APP funcione em qualquer rota ou página do fluig, inclusive em páginas públicas. 159 | 160 | 161 | ### Passo 6 - Configurando view.ftl 162 | 163 | 164 | Agora precisamos adicionar a tag principal da aplicação na **view.ftl** da widget. Esse arquivo se comportará como o **index.html** de uma aplicação Angular padrão. 165 | 166 | **Nota** 167 | 168 | Deve ser incluído somente a **tag principal** da sua aplicação, `` e os scripts e estilos gerados. Tags como ``, ``, ``, `` e etc **não** devem ser incluídas na **view.ftl** da widget. 169 | 170 | ```html 171 | ... 172 | <app-root></app-root> 173 | ... 174 | ``` 175 | 176 | Devemos adicionar também os bundles padrões do Angular. 177 | 178 | ```html 179 | <!-- CSS --> 180 | <link href="/${coreContext}/resources/styles.css" rel="stylesheet"/> 181 | 182 | ... 183 | <!-- JS --> 184 | <script src="/${coreContext}/resources/runtime.js" defer></script> 185 | <script src="/${coreContext}/resources/polyfills-es5.js" nomodule defer></script> 186 | <script src="/${coreContext}/resources/polyfills.js" defer></script> 187 | <!-- scripts.js será usado somente se for adicionado algum script no angular.json do APP --> 188 | <script src="/${coreContext}/resources/scripts.js" defer></script> 189 | <script src="/${coreContext}/resources/main.js" defer></script> 190 | ``` 191 | 192 | Precisamos também configurar alguns parâmentros do fluig para que sejam enviados para o APP angular. 193 | 194 | ```html 195 | <#assign coreContext='app_angular_poui'> 196 | <script> 197 | /** 198 | * The script below sets some enviroment variables to be used inside 199 | * Angular application. (see: app.config.ts) 200 | */ 201 | (function setEnvironmentParams() { 202 | var protectedContextPath = '${protectedContextPath!""}'; 203 | var contextPath = '${contextPath!""}'; 204 | // base url for frontend application 205 | var baseUrl = protectedContextPath + '/${tenantCode!""}'; 206 | // replace '/p' for public pages 207 | if (window.location.href.indexOf(protectedContextPath) === -1) { 208 | baseUrl = baseUrl.replace(protectedContextPath, contextPath); 209 | } 210 | // base url for frontend application 211 | window['_app_baseUrl'] = baseUrl; 212 | // get page code 213 | window['_app_pageCode'] = '${(pageCode!"")}'; 214 | })(); 215 | </script> 216 | ``` 217 | 218 | Esses parâmetros serão obtidos pelo arquivo [app.config.ts](https://github.com/fluig/app-angular-poui/blob/master/src/main/angular-app/src/app/app.config.ts). [Veja a configuração final aqui.](https://github.com/fluig/app-angular-poui/blob/master/src/main/resources/view.ftl) 219 | 220 | ### Passo 7 - Adicionando PO UI na aplicação 221 | 222 | Agora podemos adicionar o PO UI em nosso APP. Para mais detalhes, acesse a [documentação oficial de instalação do PO UI](https://po-ui.io/guides/getting-started). 223 | 224 | #### Configurando as dependências 225 | 226 | Antes de executar a instalação, é necessário que todas as dependências do projeto estejam declaradas de acordo com a versão do PO UI e Angular no arquivo **package.json**, localizado na raiz da aplicação. 227 | 228 | ```json 229 | ... 230 | "dependencies": { 231 | "@angular/animations": "~9.1.0", 232 | "@angular/common": "~9.1.0", 233 | "@angular/compiler": "~9.1.0", 234 | "@angular/core": "~9.1.0", 235 | "@angular/forms": "~9.1.0", 236 | "@angular/platform-browser": "~9.1.0", 237 | "@angular/platform-browser-dynamic": "~9.1.0", 238 | "@angular/router": "~9.1.0", 239 | "rxjs": "~6.5.4", 240 | "tslib": "^1.11.1", 241 | "zone.js": "~0.10.3" 242 | ... 243 | }, 244 | "devDependencies": { 245 | "@angular-devkit/build-angular": "~0.901.0", 246 | "@angular/cli": "~9.1.0", 247 | "@angular/compiler-cli": "~9.1.0", 248 | "@angular/language-service": "~9.1.0", 249 | "typescript": "~3.8.3", 250 | ... 251 | } 252 | ... 253 | ``` 254 | 255 | Após configurar seu arquivo, certifique-se de salvar as alterações realizadas e também que seu terminal esteja apontando para o caminho raiz da aplicação, então execute o comando: 256 | 257 | ```bash 258 | npm install 259 | ``` 260 | 261 | Após a execução deverá conter a pasta **node_modules** em seu projeto com as dependências necessárias. 262 | 263 | Utilizando o comando ng add do Angular CLI, vamos adicionar o Po em seu projeto e o mesmo se encarregará de configurar o tema, instalar o pacote e importar o módulo do Po. Para isso, execute o seguinte comando: 264 | 265 | ```bash 266 | ng add @po-ui/ng-components 267 | ``` 268 | 269 | Ao executar o comando acima, será perguntado se deseja incluir uma estrutura inicial em seu projeto com menu lateral, página e toolbar, utilizando componentes do Po, caso desejar, apenas informe: Y. 270 | 271 | #### Configurando o módulo principal 272 | 273 | No módulo principal da aplicação é preciso fazer a importação do módulo **PoModule** e incluí-lo nos imports do mesmo. Veja abaixo como fazer: 274 | 275 | ```ts 276 | ... 277 | import { PoModule } from '@po-ui/ng-components'; 278 | 279 | @NgModule({ 280 | ... 281 | imports: [ 282 | ... 283 | PoModule 284 | ], 285 | ... 286 | }) 287 | ... 288 | ``` 289 | 290 | #### Configurando o estilo 291 | 292 | ```json 293 | "styles": [ 294 | "./node_modules/@po-ui/style/css/po-theme-default.min.css", 295 | "styles.css" 296 | ], 297 | ``` 298 | 299 | Pronto, agora temos uma aplicação Angular com PO UI configurada. Podemos agora executar o comando `mvn clean install` na raiz do nosso projeto e depois realizar o deploy dele no fluig. 300 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8" standalone="no"?> 2 | <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 | xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4 | 5 | <modelVersion>4.0.0</modelVersion> 6 | <artifactId>app_angular_poui</artifactId> 7 | <groupId>com.fluig.poui</groupId> 8 | <version>1.0.0</version> 9 | <packaging>war</packaging> 10 | 11 | <name>TOTVS APP ANGULAR PO UI</name> 12 | <description>TOTVS APP ANGULAR PO UI</description> 13 | 14 | <build> 15 | <plugins> 16 | <plugin> 17 | <artifactId>maven-clean-plugin</artifactId> 18 | <version>3.0.0</version> 19 | <configuration> 20 | <filesets> 21 | <fileset> 22 | <directory>src/main/angular-app/node_modules</directory> 23 | <followSymlinks>false</followSymlinks> 24 | </fileset> 25 | </filesets> 26 | </configuration> 27 | </plugin> 28 | <plugin> 29 | <groupId>com.github.eirslett</groupId> 30 | <artifactId>frontend-maven-plugin</artifactId> 31 | <executions> 32 | <execution> 33 | <!-- optional: you don't really need execution ids, but it looks nice in your build log. --> 34 | <id>install node and npm</id> 35 | <goals> 36 | <goal>install-node-and-npm</goal> 37 | </goals> 38 | <!-- optional: default phase is "generate-resources" --> 39 | <phase>generate-resources</phase> 40 | </execution> 41 | <execution> 42 | <id>npm install</id> 43 | <goals> 44 | <goal>npm</goal> 45 | </goals> 46 | <configuration> 47 | <arguments>install</arguments> 48 | </configuration> 49 | </execution> 50 | <execution> 51 | <id>npm run</id> 52 | <goals> 53 | <goal>npm</goal> 54 | </goals> 55 | <configuration> 56 | <arguments>run build-prod</arguments> 57 | </configuration> 58 | </execution> 59 | </executions> 60 | <configuration> 61 | <nodeVersion>v14.19.3</nodeVersion> 62 | <workingDirectory>src/main/angular-app</workingDirectory> 63 | </configuration> 64 | </plugin> 65 | </plugins> 66 | <finalName>${project.artifactId}</finalName> 67 | </build> 68 | </project> -------------------------------------------------------------------------------- /src/main/angular-app/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://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 | -------------------------------------------------------------------------------- /src/main/angular-app/.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 | # profiling files 12 | chrome-profiler-events.json 13 | speed-measure-plugin.json 14 | 15 | # IDEs and editors 16 | /.idea 17 | .project 18 | .classpath 19 | .c9/ 20 | *.launch 21 | .settings/ 22 | *.sublime-workspace 23 | 24 | # IDE - VSCode 25 | .vscode/* 26 | !.vscode/settings.json 27 | !.vscode/tasks.json 28 | !.vscode/launch.json 29 | !.vscode/extensions.json 30 | 31 | # misc 32 | /.angular/cache 33 | /.sass-cache 34 | /connect.lock 35 | /coverage 36 | /libpeerconnection.log 37 | npm-debug.log 38 | yarn-error.log 39 | testem.log 40 | /typings 41 | 42 | # System Files 43 | .DS_Store 44 | Thumbs.db 45 | -------------------------------------------------------------------------------- /src/main/angular-app/README.md: -------------------------------------------------------------------------------- 1 | # TotvsAppAngularPOUI 2 | 3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 7.1.3. 4 | 5 | ## Development server 6 | 7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. 8 | 9 | ## Code scaffolding 10 | 11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. 12 | 13 | ## Build 14 | 15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build. 16 | 17 | ## Running unit tests 18 | 19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 20 | 21 | ## Running end-to-end tests 22 | 23 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). 24 | 25 | ## Further help 26 | 27 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). 28 | -------------------------------------------------------------------------------- /src/main/angular-app/angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "angular-app": { 7 | "root": "", 8 | "sourceRoot": "src", 9 | "projectType": "application", 10 | "prefix": "app", 11 | "schematics": { 12 | "@schematics/angular:component": { 13 | "style": "scss" 14 | } 15 | }, 16 | "architect": { 17 | "build": { 18 | "builder": "@angular-devkit/build-angular:browser", 19 | "options": { 20 | "outputPath": "../webapp/resources", 21 | "baseHref": "/app_angular_poui/resources/", 22 | "index": "src/index.html", 23 | "main": "src/main.ts", 24 | "polyfills": "src/polyfills.ts", 25 | "tsConfig": "src/tsconfig.app.json", 26 | "assets": [ 27 | "src/favicon.ico", 28 | "src/assets" 29 | ], 30 | "styles": [ 31 | "./node_modules/@po-ui/style/css/po-theme-default.min.css", 32 | "src/styles.scss" 33 | ], 34 | "scripts": [ 35 | "src/scripts/oauth/hmac-sha1.js", 36 | "src/scripts/oauth/hmac-sha256.js", 37 | "src/scripts/oauth/oauth-1.0a.js", 38 | "src/scripts/oauth/enc-base64-min.js" 39 | ], 40 | "vendorChunk": true, 41 | "extractLicenses": false, 42 | "buildOptimizer": false, 43 | "sourceMap": true, 44 | "optimization": false, 45 | "namedChunks": true 46 | }, 47 | "configurations": { 48 | "production": { 49 | "fileReplacements": [ 50 | { 51 | "replace": "src/environments/environment.ts", 52 | "with": "src/environments/environment.prod.ts" 53 | } 54 | ], 55 | "optimization": true, 56 | "outputHashing": "all", 57 | "sourceMap": false, 58 | "namedChunks": false, 59 | "extractLicenses": true, 60 | "vendorChunk": false, 61 | "buildOptimizer": true, 62 | "budgets": [ 63 | { 64 | "type": "initial", 65 | "maximumWarning": "2mb", 66 | "maximumError": "5mb" 67 | }, 68 | { 69 | "type": "anyComponentStyle", 70 | "maximumWarning": "6kb", 71 | "maximumError": "10kb" 72 | } 73 | ] 74 | } 75 | }, 76 | "defaultConfiguration": "" 77 | }, 78 | "serve": { 79 | "builder": "@angular-devkit/build-angular:dev-server", 80 | "options": { 81 | "browserTarget": "angular-app:build" 82 | }, 83 | "configurations": { 84 | "production": { 85 | "browserTarget": "angular-app:build:production" 86 | } 87 | } 88 | }, 89 | "extract-i18n": { 90 | "builder": "@angular-devkit/build-angular:extract-i18n", 91 | "options": { 92 | "browserTarget": "angular-app:build" 93 | } 94 | }, 95 | "test": { 96 | "builder": "@angular-devkit/build-angular:karma", 97 | "options": { 98 | "main": "src/test.ts", 99 | "polyfills": "src/polyfills.ts", 100 | "tsConfig": "src/tsconfig.spec.json", 101 | "karmaConfig": "src/karma.conf.js", 102 | "styles": [ 103 | "./node_modules/@po-ui/style/css/po-theme-default.min.css", 104 | "src/styles.scss" 105 | ], 106 | "scripts": [], 107 | "assets": [ 108 | "src/favicon.ico", 109 | "src/assets" 110 | ] 111 | } 112 | }, 113 | "e2e": { 114 | "builder": "@angular-devkit/build-angular:protractor", 115 | "options": { 116 | "protractorConfig": "e2e/protractor.conf.js", 117 | "devServerTarget": "angular-app:serve" 118 | }, 119 | "configurations": { 120 | "production": { 121 | "devServerTarget": "angular-app:serve:production" 122 | } 123 | } 124 | } 125 | } 126 | }, 127 | "angular-app-e2e": { 128 | "root": "e2e/", 129 | "projectType": "application", 130 | "prefix": "", 131 | "architect": { 132 | "e2e": { 133 | "builder": "@angular-devkit/build-angular:protractor", 134 | "options": { 135 | "protractorConfig": "e2e/protractor.conf.js", 136 | "devServerTarget": "angular-app:serve" 137 | }, 138 | "configurations": { 139 | "production": { 140 | "devServerTarget": "angular-app:serve:production" 141 | } 142 | } 143 | } 144 | } 145 | } 146 | }, 147 | "defaultProject": "angular-app" 148 | } 149 | -------------------------------------------------------------------------------- /src/main/angular-app/e2e/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: [ 9 | './src/**/*.e2e-spec.ts' 10 | ], 11 | capabilities: { 12 | 'browserName': 'chrome' 13 | }, 14 | directConnect: true, 15 | baseUrl: 'http://localhost:4200/', 16 | framework: 'jasmine', 17 | jasmineNodeOpts: { 18 | showColors: true, 19 | defaultTimeoutInterval: 30000, 20 | print: function() {} 21 | }, 22 | onPrepare() { 23 | require('ts-node').register({ 24 | project: require('path').join(__dirname, './tsconfig.e2e.json') 25 | }); 26 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 27 | } 28 | }; -------------------------------------------------------------------------------- /src/main/angular-app/e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | 3 | describe('workspace-project App', () => { 4 | let page: AppPage; 5 | 6 | beforeEach(() => { 7 | page = new AppPage(); 8 | }); 9 | 10 | it('should display welcome message', () => { 11 | page.navigateTo(); 12 | expect(page.getTitleText()).toEqual('Welcome to angular-app!'); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /src/main/angular-app/e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo() { 5 | return browser.get('/'); 6 | } 7 | 8 | getTitleText() { 9 | return element(by.css('app-root h1')).getText(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/angular-app/e2e/tsconfig.e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "module": "commonjs", 6 | "target": "es5", 7 | "types": [ 8 | "jasmine", 9 | "jasminewd2", 10 | "node" 11 | ] 12 | } 13 | } -------------------------------------------------------------------------------- /src/main/angular-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-app", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "ng": "./node_modules/.bin/ng", 6 | "start": "./node_modules/.bin/ng serve --host 0.0.0.0 --proxy-config proxy.config.js", 7 | "build": "./node_modules/.bin/ng build", 8 | "build-prod": "./node_modules/.bin/ng build --configuration production --output-hashing none", 9 | "test": "./node_modules/.bin/ng test", 10 | "lint": "./node_modules/.bin/ng lint", 11 | "e2e": "./node_modules/.bin/ng e2e" 12 | }, 13 | "private": true, 14 | "dependencies": { 15 | "@angular/animations": "~13.3.8", 16 | "@angular/common": "~13.3.8", 17 | "@angular/compiler": "~13.3.8", 18 | "@angular/core": "~13.3.8", 19 | "@angular/forms": "~13.3.8", 20 | "@angular/localize": "^13.3.8", 21 | "@angular/platform-browser": "~13.3.8", 22 | "@angular/platform-browser-dynamic": "~13.3.8", 23 | "@angular/router": "~13.3.8", 24 | "@po-ui/ng-components": "6.10.1", 25 | "rxjs": "~7.4.0", 26 | "tslib": "^2.0.0", 27 | "zone.js": "~0.11.4" 28 | }, 29 | "devDependencies": { 30 | "@angular-devkit/build-angular": "~13.3.5", 31 | "@angular/cli": "~13.3.5", 32 | "@angular/compiler-cli": "~13.3.8", 33 | "@angular/language-service": "~13.3.8", 34 | "@types/jasmine": "~3.5.0", 35 | "@types/jasminewd2": "~2.0.3", 36 | "@types/node": "^12.11.1", 37 | "codelyzer": "^5.1.2", 38 | "jasmine-core": "~3.5.0", 39 | "jasmine-spec-reporter": "~5.0.0", 40 | "karma": "~6.3.20", 41 | "karma-chrome-launcher": "~3.1.0", 42 | "karma-coverage-istanbul-reporter": "~3.0.2", 43 | "karma-jasmine": "~4.0.0", 44 | "karma-jasmine-html-reporter": "^1.5.0", 45 | "protractor": "~7.0.0", 46 | "ts-node": "~8.3.0", 47 | "tslint": "~6.1.0", 48 | "typescript": "~4.6.4" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/angular-app/proxy.config.js: -------------------------------------------------------------------------------- 1 | const proxy = [ 2 | { 3 | context: "/process-management/api", 4 | target: "https://universo.fluig.io", 5 | changeOrigin: true, 6 | secure: true 7 | } 8 | ]; 9 | module.exports = proxy; 10 | -------------------------------------------------------------------------------- /src/main/angular-app/src/.browserslistrc: -------------------------------------------------------------------------------- 1 | # This file is currently used by autoprefixer to adjust CSS to support the below specified browsers 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | # 5 | # For IE 9-11 support, please remove 'not' from the last line of the file and adjust as needed 6 | 7 | > 0.5% 8 | last 2 versions 9 | Firefox ESR 10 | not dead 11 | not IE 9-11 -------------------------------------------------------------------------------- /src/main/angular-app/src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | 4 | const routes: Routes = [ 5 | { path: 'dashboard', loadChildren: () => import('./home/home.module').then(m => m.HomeModule), pathMatch: 'full' }, 6 | { path: 'purchase-order', loadChildren: () => import('./purchase-order/purchase-order.module').then(m => m.PurchaseOrderModule) }, 7 | { path: 'supplier-quotation', loadChildren: () => import('./supplier-quotation/supplier-quotation.module').then(m => m.SupplierQuotationModule) }, 8 | { path: '**', redirectTo: 'dashboard' } 9 | ]; 10 | 11 | @NgModule({ 12 | imports: [RouterModule.forRoot(routes)], 13 | exports: [RouterModule] 14 | }) 15 | export class AppRoutingModule { } 16 | -------------------------------------------------------------------------------- /src/main/angular-app/src/app/app.component.html: -------------------------------------------------------------------------------- 1 | <router-outlet></router-outlet> 2 | -------------------------------------------------------------------------------- /src/main/angular-app/src/app/app.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluig/app-angular-poui/9f915cbe2b0b13b4f71c5256252a144fd99e239f/src/main/angular-app/src/app/app.component.scss -------------------------------------------------------------------------------- /src/main/angular-app/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, async } from '@angular/core/testing'; 2 | import { RouterTestingModule } from '@angular/router/testing'; 3 | import { AppComponent } from './app.component'; 4 | 5 | describe('AppComponent', () => { 6 | beforeEach(async(() => { 7 | TestBed.configureTestingModule({ 8 | imports: [ 9 | RouterTestingModule 10 | ], 11 | declarations: [ 12 | AppComponent 13 | ], 14 | }).compileComponents(); 15 | })); 16 | 17 | it('should create the app', () => { 18 | const fixture = TestBed.createComponent(AppComponent); 19 | const app = fixture.debugElement.componentInstance; 20 | expect(app).toBeTruthy(); 21 | }); 22 | 23 | it(`should have as title 'angular-app'`, () => { 24 | const fixture = TestBed.createComponent(AppComponent); 25 | const app = fixture.debugElement.componentInstance; 26 | expect(app.title).toEqual('angular-app'); 27 | }); 28 | 29 | it('should render title in a h1 tag', () => { 30 | const fixture = TestBed.createComponent(AppComponent); 31 | fixture.detectChanges(); 32 | const compiled = fixture.debugElement.nativeElement; 33 | expect(compiled.querySelector('h1').textContent).toContain('Welcome to angular-app!'); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /src/main/angular-app/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-root', 5 | templateUrl: './app.component.html', 6 | styleUrls: ['./app.component.scss'] 7 | }) 8 | export class AppComponent {} 9 | -------------------------------------------------------------------------------- /src/main/angular-app/src/app/app.config.ts: -------------------------------------------------------------------------------- 1 | abstract class AppSettings { 2 | 3 | public static APP_ROOT = window['_app_baseUrl']; 4 | public static APP_PAGE_CODE = window['_app_pageCode']; 5 | 6 | /** 7 | * base url + application prefix 8 | */ 9 | 10 | public static APP_BASE = 11 | AppSettings.APP_ROOT && AppSettings.APP_PAGE_CODE 12 | ? AppSettings.APP_ROOT + '/' + AppSettings.APP_PAGE_CODE 13 | : ''; 14 | } 15 | 16 | export { AppSettings as APP_CONFIG }; 17 | -------------------------------------------------------------------------------- /src/main/angular-app/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { NgModule } from '@angular/core'; 3 | import { APP_BASE_HREF } from '@angular/common'; 4 | 5 | import { AppRoutingModule } from './app-routing.module'; 6 | import { AppComponent } from './app.component'; 7 | import { APP_CONFIG } from './app.config'; 8 | import { ProcessManagementService } from './services/process-management.service'; 9 | import { FluigOauthService } from './services/fluig-oauth.service'; 10 | import { ProductsConverterService } from './services/products-converter.service'; 11 | import { HttpClientModule } from '@angular/common/http'; 12 | 13 | @NgModule({ 14 | declarations: [ 15 | AppComponent 16 | ], 17 | imports: [ 18 | BrowserModule, 19 | AppRoutingModule, 20 | HttpClientModule 21 | ], 22 | providers: [ 23 | { provide: APP_BASE_HREF, useValue: APP_CONFIG.APP_BASE || '/' }, 24 | ProcessManagementService, 25 | FluigOauthService, 26 | ProductsConverterService 27 | ], 28 | bootstrap: [AppComponent] 29 | }) 30 | export class AppModule { } 31 | -------------------------------------------------------------------------------- /src/main/angular-app/src/app/home/home.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; 4 | import { HomeComponent } from './home/home.component'; 5 | import { HomeRoutingModule } from './home.routing.module'; 6 | import { PoModule } from '@po-ui/ng-components'; 7 | 8 | @NgModule({ 9 | declarations: [ 10 | HomeComponent 11 | ], 12 | imports: [ 13 | CommonModule, 14 | HomeRoutingModule, 15 | FormsModule, 16 | ReactiveFormsModule, 17 | PoModule, 18 | ], 19 | exports: [ 20 | HomeComponent 21 | ] 22 | }) 23 | export class HomeModule { } 24 | -------------------------------------------------------------------------------- /src/main/angular-app/src/app/home/home.routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | import { HomeComponent } from './home/home.component'; 4 | 5 | const routes: Routes = [ 6 | { path: '', component: HomeComponent } 7 | ]; 8 | 9 | @NgModule({ 10 | imports: [RouterModule.forChild(routes)], 11 | exports: [RouterModule] 12 | }) 13 | export class HomeRoutingModule {} 14 | -------------------------------------------------------------------------------- /src/main/angular-app/src/app/home/home/home.component.html: -------------------------------------------------------------------------------- 1 | <div class="po-row"> 2 | <h1 class="po-md-10">Dashboard da empresa</h1> 3 | <po-button 4 | class="po-md-2 new-purchase-button" 5 | p-type="primary" 6 | p-label="Novo pedido" 7 | (p-click)="newPurchaseOrder()"> 8 | </po-button> 9 | </div> 10 | <br> 11 | <div class="po-row"> 12 | <po-widget class="po-lg-4"> 13 | <div class="po-font-subtitle po-text-center">33% de aumento</div> 14 | <div class="po-text-center">Aumento de 33% na compra de produtos.</div> 15 | </po-widget> 16 | 17 | <po-widget class="po-lg-4"> 18 | <div class="po-font-subtitle po-text-center">R$ 660 mil</div> 19 | <div class="po-text-center">Gastos com produtos de escrítorio no ano.</div> 20 | </po-widget> 21 | 22 | <po-widget class="po-lg-4"> 23 | <div class="po-font-subtitle po-text-center">Fornecedores</div> 24 | <div class="po-text-center">Dezenas de novos fornecedores cadastrados.</div> 25 | </po-widget> 26 | </div> 27 | 28 | <div class="po-row"> 29 | <po-chart class="po-lg-6 po-mt-2" p-title="Top 5 produtos mais caros" 30 | [p-series]="lowProducts" (p-series-click)="showMeTheProduct($event)"> 31 | </po-chart> 32 | 33 | <po-chart class="po-lg-6 po-mt-2" p-title="Top 5 produtos mais baratos" 34 | [p-series]="highProducts" (p-series-click)="showMeTheProduct($event)"> 35 | </po-chart> 36 | </div> 37 | 38 | <div class="po-row"> 39 | <po-container class="po-lg-12 po-mt-2"> 40 | <div class="po-font-text-bold"> 41 | Top 10 produtos mais solicitados para compra 42 | </div> 43 | <po-table p-container="shadow" [p-items]="items"> 44 | </po-table> 45 | </po-container> 46 | </div> 47 | -------------------------------------------------------------------------------- /src/main/angular-app/src/app/home/home/home.component.scss: -------------------------------------------------------------------------------- 1 | :host { 2 | .new-purchase-button { 3 | margin-top: 20px; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/main/angular-app/src/app/home/home/home.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { HomeComponent } from './home.component'; 4 | 5 | describe('HomeComponent', () => { 6 | let component: HomeComponent; 7 | let fixture: ComponentFixture<HomeComponent>; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ HomeComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(HomeComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/main/angular-app/src/app/home/home/home.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { Router } from '@angular/router'; 3 | import { PoChartType } from '@po-ui/ng-components'; 4 | import { PoDialogService } from '@po-ui/ng-components'; 5 | import { PoChartSerie } from '@po-ui/ng-components'; 6 | 7 | @Component({ 8 | selector: 'app-home', 9 | templateUrl: './home.component.html', 10 | styleUrls: ['./home.component.scss'] 11 | }) 12 | export class HomeComponent implements OnInit { 13 | chartType: PoChartType = PoChartType.Pie; 14 | 15 | lowProducts: Array<PoChartSerie> = [ 16 | { label: 'Envelope 16x22', data: 0.2, tooltip: 'Envelope 16x22 (Escritório)' }, 17 | { label: 'Grafite 0.5mm', data: 0.29, tooltip: 'Grafite 0.5mm (Escritório)' }, 18 | { label: 'Lápis de escrever', data: 0.32, tooltip: 'Lápis de escrever (Escritório)' }, 19 | { label: 'Caneta Azul', data: 0.58, tooltip: 'Caneta Azul (Escritório)' }, 20 | { label: 'Caneta Preta', data: 0.62, tooltip: 'Caneta Preta (Escritório)' } 21 | ]; 22 | 23 | highProducts: Array<PoChartSerie> = [ 24 | { label: 'Calculadora Científica', data: 50.29, tooltip: 'Calculadora Científica (Escritório)' }, 25 | { label: 'Apagador', data: 6.53, tooltip: 'Apagador (Escritório)' }, 26 | { label: 'Papel A4 - 500 Folhas', data: 6.22, tooltip: 'Papel A4 - 500 Folhas (Escritório)' }, 27 | { label: 'Caneta Marca Texto', data: 5.00, tooltip: 'Caneta Marca Texto (Escritório)' }, 28 | { label: 'Bloco Adesivo de Anotações - Post It', data: 3.22, tooltip: 'Bloco Adesivo de Anotações - Post It (Escritório)' } 29 | ]; 30 | 31 | items: Array<any> = [ 32 | { 33 | posicao: '1', 34 | produto: 'Caneta Azul', 35 | fornecedor: 'Paper And Crafts & Arts', 36 | valor: 'R$ 0,58' 37 | }, 38 | { 39 | posicao: '2', 40 | produto: 'Caneta Preta', 41 | fornecedor: 'Kormex Comércio e Indústria Gráfica Ltda.', 42 | valor: 'R$ 0,62' 43 | }, 44 | { 45 | posicao: '3', 46 | produto: 'Papel A4 - 500 Folhas', 47 | fornecedor: 'Kormex Comércio e Indústria Gráfica Ltda.', 48 | valor: 'R$ 6,22' 49 | }, 50 | { 51 | posicao: '4', 52 | produto: 'Lápis de escrever', 53 | fornecedor: 'Paper And Crafts & Arts', 54 | valor: 'R$ 0,32' 55 | }, 56 | { 57 | posicao: '5', 58 | produto: 'Grafite 0.5mm', 59 | fornecedor: 'Paper And Crafts & Arts', 60 | valor: 'R$ 0,29' 61 | }, 62 | { 63 | posicao: '6', 64 | produto: 'Bloco Adesivo de Anotações - Post It', 65 | fornecedor: 'Kormex Comércio e Indústria Gráfica Ltda.', 66 | valor: 'R$ 3,22' 67 | }, 68 | { 69 | posicao: '7', 70 | produto: 'Caneta Marca Texto', 71 | fornecedor: 'Sorveio e Sives S.A.', 72 | valor: 'R$ 5,00' 73 | }, 74 | { 75 | posicao: '8', 76 | produto: 'Apagador', 77 | fornecedor: 'Paper And Crafts & Arts', 78 | valor: 'R$ 6,53' 79 | }, 80 | { 81 | posicao: '9', 82 | produto: 'Calculadora Científica', 83 | fornecedor: 'Sorveio e Sives S.A.', 84 | valor: 'R$ 50,29' 85 | }, 86 | { 87 | posicao: '10', 88 | produto: 'Envelope 16x22', 89 | fornecedor: 'Sorveio e Sives S.A.', 90 | valor: 'R$ 0,20' 91 | } 92 | ]; 93 | 94 | constructor( 95 | private poAlert: PoDialogService, 96 | private router: Router 97 | ) {} 98 | 99 | ngOnInit() {} 100 | 101 | showMeTheProduct(event: any) { 102 | this.poAlert.alert({ 103 | title: 'Detalhes do produto', 104 | message: `O produto ${event.label} custa R$ ${event.data}.`, 105 | ok: () => {} 106 | }); 107 | } 108 | 109 | newPurchaseOrder() { 110 | this.router.navigateByUrl('/purchase-order'); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/main/angular-app/src/app/models/ajax-request-data.interface.ts: -------------------------------------------------------------------------------- 1 | export interface AjaxRequestData { 2 | url: string; 3 | method: string; 4 | data?: any; 5 | } 6 | -------------------------------------------------------------------------------- /src/main/angular-app/src/app/models/products.interface.ts: -------------------------------------------------------------------------------- 1 | export interface Products { 2 | description?: string; 3 | price?: string; 4 | masterid?: string; 5 | unitMeasurement?: string; 6 | documentid?: string; 7 | cardid?: string; 8 | manufacturer?: string; 9 | companyid?: string; 10 | tableid?: string; 11 | quantity?: string; 12 | product_code?: string; 13 | version?: string; 14 | } 15 | -------------------------------------------------------------------------------- /src/main/angular-app/src/app/models/purchase-request.interface.ts: -------------------------------------------------------------------------------- 1 | import { Products } from './products.interface'; 2 | 3 | export interface PurchaseRequest { 4 | code?: string; 5 | textbox0?: string; 6 | textbox1?: string; 7 | login?: string; 8 | zoomfield2?: string; 9 | companyid?: string; 10 | provider?: string; 11 | justify?: string; 12 | cardid?: string; 13 | tableid?: string; 14 | documentid?: string; 15 | hidden_provider?: string; 16 | version?: string; 17 | hidden_zoomfield2?: string; 18 | approve?: string; 19 | category?: string; 20 | providers?: string; 21 | products?: Products[]; 22 | } 23 | -------------------------------------------------------------------------------- /src/main/angular-app/src/app/purchase-order/purchase-order.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; 4 | import { PurchaseOrderComponent } from './purchase-order/purchase-order.component'; 5 | import { PurchaseOrderRoutingModule } from './purchase-order.routing.module'; 6 | import { PoModule } from '@po-ui/ng-components'; 7 | 8 | @NgModule({ 9 | declarations: [ 10 | PurchaseOrderComponent 11 | ], 12 | imports: [ 13 | CommonModule, 14 | PurchaseOrderRoutingModule, 15 | FormsModule, 16 | ReactiveFormsModule, 17 | PoModule 18 | ], 19 | exports: [ 20 | PurchaseOrderComponent 21 | ] 22 | }) 23 | export class PurchaseOrderModule { } 24 | -------------------------------------------------------------------------------- /src/main/angular-app/src/app/purchase-order/purchase-order.routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | import { PurchaseOrderComponent } from './purchase-order/purchase-order.component'; 4 | 5 | const routes: Routes = [ 6 | { path: '', component: PurchaseOrderComponent } 7 | ]; 8 | 9 | @NgModule({ 10 | imports: [RouterModule.forChild(routes)], 11 | exports: [RouterModule] 12 | }) 13 | export class PurchaseOrderRoutingModule {} 14 | -------------------------------------------------------------------------------- /src/main/angular-app/src/app/purchase-order/purchase-order/purchase-order.component.html: -------------------------------------------------------------------------------- 1 | <div class="po-row"> 2 | <h1 class="po-md-12">Cadastro de pedido de compra</h1> 3 | </div> 4 | <div class="po-row"> 5 | <p class="po-md-12">Preencha abaixo os detalhes do seu pedido de compra. Não esqueça de selecionar os fornecedores. 6 | </p> 7 | </div> 8 | <br> 9 | <form [formGroup]="reactiveForm"> 10 | 11 | <div class="po-row"> 12 | <po-input class="po-md-12" formControlName="name" p-clean p-label="Usuário" p-readonly="true"> 13 | </po-input> 14 | </div> 15 | 16 | <div class="po-row"> 17 | <po-select class="po-md-12" formControlName="category" p-clean p-label="Categoria" 18 | [p-options]="[{label: 'Option 1', value: '1'}, {label: 'Option 2', value: '2'}]"> 19 | </po-select> 20 | </div> 21 | 22 | <div class="po-row"> 23 | <po-textarea class="po-md-12" formControlName="justify" p-clean p-label="Justificativa"> 24 | </po-textarea> 25 | </div> 26 | 27 | <div class="po-row"> 28 | <po-multiselect class="po-md-12" formControlName="providers" p-clean p-label="Fornecedores" [p-options]="options"> 29 | </po-multiselect> 30 | </div> 31 | 32 | <div class="po-row"> 33 | <po-table class="po-md-12" [p-items]="[{ 34 | Codigo: 'Totvs Table', 35 | Descricao: 'Totvs Table', 36 | UM: 'Totvs Table', 37 | Fabricante: 'Totvs Table', 38 | Quantidade: 'Totvs Table' 39 | }, { 40 | Codigo: 'Totvs Table', 41 | Descricao: 'Totvs Table', 42 | UM: 'Totvs Table', 43 | Fabricante: 'Totvs Table', 44 | Quantidade: 'Totvs Table' 45 | }, { 46 | Codigo: 'Totvs Table', 47 | Descricao: 'Totvs Table', 48 | UM: 'Totvs Table', 49 | Fabricante: 'Totvs Table', 50 | Quantidade: 'Totvs Table' 51 | }]"> 52 | </po-table> 53 | </div> 54 | 55 | <div class="po-row button-area"> 56 | <po-button 57 | p-type="primary" 58 | class="po-md-2" 59 | p-label="Solicitar cotação" 60 | [p-disabled]="!reactiveForm.valid" 61 | (p-click)="saveForm()" 62 | > 63 | </po-button> 64 | <po-button 65 | class="po-md-2" 66 | p-label="Cancelar" 67 | (p-click)="cancel()" 68 | > 69 | </po-button> 70 | </div> 71 | </form> 72 | 73 | <po-modal #reactiveFormData p-title="Save successful" [p-primary-action]="modalPrimaryAction"> 74 | <div class="po-row"> 75 | <po-info class="po-md-12" p-label="Name" [p-value]="reactiveForm.controls.name.value"> 76 | </po-info> 77 | </div> 78 | 79 | <!-- <hr> 80 | 81 | <div class="po-row"> 82 | <po-info 83 | class="po-md-6" 84 | p-label="Address" 85 | [p-value]="reactiveForm.controls.address.value"> 86 | </po-info> 87 | 88 | <po-info 89 | class="po-md-6" 90 | p-label="Number" 91 | [p-value]="reactiveForm.controls.number.value"> 92 | </po-info> 93 | </div> 94 | 95 | <hr> 96 | 97 | <div class="po-row"> 98 | <po-info 99 | class="po-md-6" 100 | p-label="Email" 101 | [p-value]="reactiveForm.controls.email.value"> 102 | </po-info> 103 | 104 | <po-info 105 | class="po-md-6" 106 | p-label="Website" 107 | [p-value]="reactiveForm.controls.website.value"> 108 | </po-info> 109 | </div> --> 110 | </po-modal> -------------------------------------------------------------------------------- /src/main/angular-app/src/app/purchase-order/purchase-order/purchase-order.component.scss: -------------------------------------------------------------------------------- 1 | :host { 2 | .button-area { 3 | margin: 20px 0; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/main/angular-app/src/app/purchase-order/purchase-order/purchase-order.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { PurchaseOrderComponent } from './purchase-order.component'; 4 | 5 | describe('PurchaseOrderComponent', () => { 6 | let component: PurchaseOrderComponent; 7 | let fixture: ComponentFixture<PurchaseOrderComponent>; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ PurchaseOrderComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(PurchaseOrderComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/main/angular-app/src/app/purchase-order/purchase-order/purchase-order.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ViewChild, OnInit } from '@angular/core'; 2 | import { FormBuilder, FormGroup, Validators } from '@angular/forms'; 3 | 4 | import { PoModalAction, PoModalComponent } from '@po-ui/ng-components'; 5 | import { PoMultiselectOption } from '@po-ui/ng-components'; 6 | import { Router } from '@angular/router'; 7 | 8 | @Component({ 9 | selector: 'app-purchase-order', 10 | templateUrl: './purchase-order.component.html', 11 | styleUrls: ['./purchase-order.component.scss'] 12 | }) 13 | export class PurchaseOrderComponent implements OnInit { 14 | 15 | reactiveForm: FormGroup; 16 | options: Array<PoMultiselectOption> = [ 17 | { value: 'fornecedor1', label: 'Paper And Crafts & Arts' }, 18 | { value: 'fornecedor2', label: 'Sorveio e Sives S.A.' }, 19 | { value: 'fornecedor3', label: 'Kormex Comércio e Indústria Gráfica Ltda.' }, 20 | { value: 'fornecedor4', label: 'Contabilista' } 21 | ]; 22 | 23 | public readonly modalPrimaryAction: PoModalAction = { 24 | action: () => this.reactiveFormModal.close(), 25 | label: 'Close' 26 | }; 27 | 28 | @ViewChild('reactiveFormData') reactiveFormModal: PoModalComponent; 29 | 30 | constructor( 31 | private fb: FormBuilder, 32 | private router: Router 33 | ) { } 34 | 35 | ngOnInit() { 36 | this.createReactiveForm(); 37 | } 38 | 39 | createReactiveForm() { 40 | this.reactiveForm = this.fb.group({ 41 | name: ['Bruno Quadrotti de Freitas', Validators.compose([ 42 | Validators.required, Validators.minLength(5), Validators.maxLength(30) 43 | ])], 44 | category: ['', Validators.compose([ 45 | Validators.required 46 | ])], 47 | justify: ['', Validators.compose([ 48 | Validators.required 49 | ])], 50 | providers: ['', Validators.compose([ 51 | Validators.required 52 | ])] 53 | }); 54 | } 55 | 56 | saveForm() { 57 | this.reactiveFormModal.open(); 58 | } 59 | 60 | cancel() { 61 | this.router.navigateByUrl('/dashboard'); 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/main/angular-app/src/app/services/fluig-oauth.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { AjaxRequestData } from '../models/ajax-request-data.interface'; 3 | import { environment } from 'src/environments/environment'; 4 | declare const OAuth: any; 5 | 6 | @Injectable() 7 | export class FluigOauthService { 8 | public getOauthHeaders(requestData: AjaxRequestData) { 9 | 10 | const oauth = OAuth({ 11 | consumer: { 12 | public: environment.OAUTH_CONSUMER_PUBLIC, 13 | secret: environment.OAUTH_CONSUMER_SECRET 14 | }, 15 | signature_method: environment.OAUTH_SIGNATURE_METHOD 16 | }); 17 | 18 | const token = { 19 | public: environment.TOKEN_PUBLIC, 20 | secret: environment.TOKEN_SECRET 21 | }; 22 | 23 | return oauth.toHeader(oauth.authorize(requestData, token)); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/angular-app/src/app/services/process-management.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Observable } from 'rxjs'; 3 | import { HttpClient, HttpHeaders } from '@angular/common/http'; 4 | import { FluigOauthService } from './fluig-oauth.service'; 5 | import { ProductsConverterService } from './products-converter.service'; 6 | import { PurchaseRequest } from '../models/purchase-request.interface'; 7 | import { map } from 'rxjs/operators'; 8 | declare const WCMAPI: any; 9 | 10 | @Injectable() 11 | export class ProcessManagementService { 12 | 13 | private BASE_URL = `${window['WCMAPI'] ? WCMAPI.getServerURL() : ''}`; 14 | 15 | constructor( 16 | private http: HttpClient, 17 | private fluigOauthService: FluigOauthService, 18 | private productsConverter: ProductsConverterService 19 | ) {} 20 | 21 | public getProcessManagement(WKNumProcess): Observable<PurchaseRequest> { 22 | 23 | // tslint:disable-next-line:max-line-length 24 | const url = `${this.BASE_URL}/process-management/api/v2/requests?processInstanceId=${WKNumProcess}&page=1&pageSize=1000&expand=formFields`; 25 | 26 | const headers = this.getHeaders(url); 27 | 28 | const httpOptions = { 29 | headers: new HttpHeaders({ 30 | ...headers 31 | }) 32 | }; 33 | 34 | return this.http.get( 35 | url, 36 | httpOptions 37 | ).pipe(map(res => { 38 | return this.productsConverter.convertToObject( 39 | res['items'][0] 40 | ); 41 | })); 42 | } 43 | 44 | public moveProcessManagement(WKNumProcess, data): Observable<any> { 45 | 46 | const url = `${this.BASE_URL}/process-management/api/v2/requests/${WKNumProcess}/move`; 47 | 48 | const headers = this.getHeaders(url, 'POST'); 49 | 50 | const httpOptions = { 51 | headers: new HttpHeaders({ 52 | ...headers 53 | }) 54 | }; 55 | 56 | return this.http.post( 57 | url, 58 | data, 59 | httpOptions 60 | ); 61 | } 62 | 63 | private getHeaders(url: string, method = 'GET') { 64 | return this.fluigOauthService.getOauthHeaders({ 65 | url: url, 66 | method: method 67 | }); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/angular-app/src/app/services/products-converter.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { PurchaseRequest } from '../models/purchase-request.interface'; 3 | 4 | @Injectable() 5 | export class ProductsConverterService { 6 | 7 | public convertToObject(response): PurchaseRequest { 8 | 9 | const purchaseRequest: PurchaseRequest = {}; 10 | const productsMap = {}; 11 | const products = []; 12 | 13 | if (response && response.formFields) { 14 | response.formFields.forEach(item => { 15 | if (item.field.indexOf('___') !== -1) { 16 | const productSplit = item.field.split('___'); 17 | if (!productsMap[productSplit[1]]) { 18 | productsMap[productSplit[1]] = {}; 19 | } 20 | productsMap[productSplit[1]][productSplit[0]] = item.value; 21 | } else { 22 | purchaseRequest[item.field] = item.value; 23 | } 24 | }); 25 | 26 | Object.keys(productsMap).forEach(item => { 27 | products.push(productsMap[item]); 28 | }); 29 | } 30 | 31 | purchaseRequest.products = products; 32 | 33 | return purchaseRequest; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/angular-app/src/app/supplier-quotation/supplier-quotation.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { SupplierQuotationComponent } from './supplier-quotation/supplier-quotation.component'; 4 | import { SupplierQuotationRoutingModule } from './supplier-quotation.routing.module'; 5 | import { ProcessManagementService } from '../services/process-management.service'; 6 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; 7 | import { PoModule } from '@po-ui/ng-components'; 8 | 9 | @NgModule({ 10 | declarations: [ 11 | SupplierQuotationComponent 12 | ], 13 | imports: [ 14 | CommonModule, 15 | SupplierQuotationRoutingModule, 16 | FormsModule, 17 | ReactiveFormsModule, 18 | PoModule 19 | ], 20 | exports: [ 21 | SupplierQuotationComponent 22 | ], 23 | providers: [ 24 | ProcessManagementService 25 | ] 26 | }) 27 | export class SupplierQuotationModule { } 28 | -------------------------------------------------------------------------------- /src/main/angular-app/src/app/supplier-quotation/supplier-quotation.routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | import { SupplierQuotationComponent } from './supplier-quotation/supplier-quotation.component'; 4 | 5 | const routes: Routes = [ 6 | { path: '', component: SupplierQuotationComponent } 7 | ]; 8 | 9 | @NgModule({ 10 | imports: [RouterModule.forChild(routes)], 11 | exports: [RouterModule] 12 | }) 13 | export class SupplierQuotationRoutingModule {} 14 | -------------------------------------------------------------------------------- /src/main/angular-app/src/app/supplier-quotation/supplier-quotation/supplier-quotation.component.html: -------------------------------------------------------------------------------- 1 | <po-page-default p-title="Incluir cotação dos produtos"> 2 | <div class="po-row po-mb-2"> 3 | <ng-container *ngFor="let product of productsList; let i = index" [attr.data-index]="i"> 4 | <po-widget class="po-lg-12 po-mb-1"> 5 | <ng-container *ngFor="let item of product"> 6 | <dl *ngIf="item.key !== 'price'"> 7 | <dt>{{ literals[item.key] }}</dt> 8 | <dd>{{ item.value }}</dd> 9 | </dl> 10 | </ng-container> 11 | <ng-container *ngFor="let item of product"> 12 | <po-decimal 13 | *ngIf="item.key === 'price'" 14 | class="po-md-3 no-padding po-mt-1" 15 | name="{{ item.key }}-{{ i }}" 16 | [(ngModel)]="item.value" 17 | p-decimals-length="2" 18 | p-icon="po-icon-finance" 19 | [p-label]="literals[item.key]" 20 | p-required 21 | p-thousand-maxlength="13" 22 | [p-disabled]="isSended || !!status" 23 | > 24 | </po-decimal> 25 | </ng-container> 26 | </po-widget> 27 | </ng-container> 28 | </div> 29 | <div class="po-row po-mb-2" *ngIf="status"> 30 | <po-datepicker 31 | [(ngModel)]="sendDate" 32 | class="po-md-3" 33 | name="datepicker" 34 | p-label="Data de envio" 35 | [p-disabled]="isSended"> 36 | </po-datepicker> 37 | </div> 38 | <div class="po-row po-mb-2" *ngIf="status"> 39 | <po-textarea 40 | [(ngModel)]="trackingCode" 41 | class="po-md-3" 42 | name="textarea" 43 | p-label="Código de rastreio" 44 | [p-disabled]="isSended"> 45 | </po-textarea> 46 | </div> 47 | <div class="po-row po-mb-2"> 48 | <po-button [p-disabled]="isSended" p-type="primary" class="po-md-2" p-label="Enviar" (p-click)="saveForm()"> 49 | </po-button> 50 | </div> 51 | </po-page-default> 52 | -------------------------------------------------------------------------------- /src/main/angular-app/src/app/supplier-quotation/supplier-quotation/supplier-quotation.component.scss: -------------------------------------------------------------------------------- 1 | :host { 2 | dl { 3 | vertical-align: top; 4 | display: inline-block; 5 | width: 20%; 6 | } 7 | dt, dd { 8 | font-size: 16px; 9 | margin-bottom: 6px; 10 | } 11 | dt { 12 | font-weight: bold; 13 | } 14 | h4, h5, p { 15 | color: #4a5c60 16 | } 17 | .button-area { 18 | margin-top: 20px; 19 | } 20 | .no-padding { 21 | padding: 0; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/angular-app/src/app/supplier-quotation/supplier-quotation/supplier-quotation.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { SupplierQuotationComponent } from './supplier-quotation.component'; 4 | 5 | describe('SupplierQuotationComponent', () => { 6 | let component: SupplierQuotationComponent; 7 | let fixture: ComponentFixture<SupplierQuotationComponent>; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ SupplierQuotationComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(SupplierQuotationComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/main/angular-app/src/app/supplier-quotation/supplier-quotation/supplier-quotation.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { ActivatedRoute } from '@angular/router'; 3 | import { ProcessManagementService } from 'src/app/services/process-management.service'; 4 | import { PoNotificationService, PoDialogService, PoToasterOrientation } from '@po-ui/ng-components'; 5 | 6 | @Component({ 7 | selector: 'app-supplier-quotation', 8 | templateUrl: './supplier-quotation.component.html', 9 | styleUrls: ['./supplier-quotation.component.scss'] 10 | }) 11 | export class SupplierQuotationComponent implements OnInit { 12 | 13 | private WKNumProcess: string; 14 | status: string; 15 | sendDate: string; 16 | trackingCode: string; 17 | isSended = false; 18 | objectKeys = Object.keys; 19 | literals = { 20 | description: 'Descrição', 21 | unitMeasurement: 'Unidade de medida', 22 | manufacturer: 'Fabricante', 23 | quantity: 'Quantidade', 24 | product_code: 'Código do produto', 25 | price: 'Valor do produto', 26 | warning: 'Atenção', 27 | error_get_process: 'Não foi possível carregar essa solicitação.', 28 | error_empty_fields: 'Preencha todos os valores dos produtos.', 29 | success_send_process: 'Cotação enviada com sucesso.', 30 | error_send_process: 'Não foi possível enviar a cotação.', 31 | error_empty_send_options: 'Preencha os dados de envio dos produtos.' 32 | }; 33 | products: Array<any>; 34 | productsList: Array<any> = []; 35 | whiteListItems = [ 36 | 'description', 37 | 'unitMeasurement', 38 | 'manufacturer', 39 | 'quantity', 40 | 'product_code', 41 | 'price' 42 | ]; 43 | 44 | constructor( 45 | private route: ActivatedRoute, 46 | private poNotification: PoNotificationService, 47 | private poDialog: PoDialogService, 48 | private processManagementService: ProcessManagementService 49 | ) { } 50 | 51 | ngOnInit() { 52 | this.route.queryParams.subscribe(params => { 53 | 54 | this.WKNumProcess = params.WKNumProcess; 55 | this.status = params.status; 56 | 57 | if (this.WKNumProcess) { 58 | this.getProcessManagement(); 59 | } 60 | }); 61 | } 62 | 63 | getProcessManagement() { 64 | 65 | this.processManagementService.getProcessManagement( 66 | this.WKNumProcess 67 | ).subscribe(res => { 68 | this.products = res.products; 69 | 70 | if (!this.products.length) { 71 | this.showToasterMessage( 72 | this.literals.error_get_process, 73 | 'error' 74 | ); 75 | return false; 76 | } 77 | 78 | this.products.forEach(product => { 79 | 80 | product = Object.keys(product).filter(item => { 81 | if (!this.isWhiteListItem(item)) { 82 | return false; 83 | } 84 | return item; 85 | }).map(item => { 86 | return { 87 | key: item, 88 | value: product[item] 89 | }; 90 | }); 91 | 92 | this.productsList.push( 93 | product 94 | ); 95 | }); 96 | }, error => { 97 | console.error('error', error); 98 | this.showToasterMessage( 99 | this.literals.error_get_process, 100 | 'error' 101 | ); 102 | }); 103 | } 104 | 105 | showToasterMessage(message, type = 'success'): void { 106 | this.poNotification[type]({ 107 | message: message, 108 | orientation: PoToasterOrientation.Top 109 | }); 110 | } 111 | 112 | isWhiteListItem(item): boolean { 113 | return this.whiteListItems.includes(item); 114 | } 115 | 116 | saveForm() { 117 | 118 | if (this.hasFieldEmpty()) { 119 | this.poDialog.alert({ 120 | title: this.literals.warning, 121 | message: this.literals.error_empty_fields 122 | }); 123 | return false; 124 | } 125 | 126 | if (this.status && this.hasSendOptionsEmpty()) { 127 | this.poDialog.alert({ 128 | title: this.literals.warning, 129 | message: this.literals.error_empty_send_options 130 | }); 131 | return false; 132 | } 133 | 134 | this.processManagementService.moveProcessManagement( 135 | this.WKNumProcess, 136 | this.getProcessData() 137 | ).subscribe(res => { 138 | this.showToasterMessage( 139 | this.literals.success_send_process 140 | ); 141 | this.isSended = true; 142 | }, error => { 143 | console.error('error', error); 144 | this.showToasterMessage( 145 | this.literals.error_send_process, 146 | 'error' 147 | ); 148 | }); 149 | } 150 | 151 | hasSendOptionsEmpty() { 152 | return !this.sendDate || !this.trackingCode; 153 | } 154 | 155 | getProcessData() { 156 | 157 | const data: any = {}; 158 | data.formFields = {}; 159 | 160 | this.productsList.map((product, idx) => product.map(item => { 161 | data.formFields[`${item.key}___${idx + 1}`] = `${item.value}`; 162 | })); 163 | 164 | return data; 165 | } 166 | 167 | hasFieldEmpty(): boolean { 168 | let hasEmpty = false; 169 | this.productsList.map(product => product.map(item => { 170 | if (item.key === 'price' && !item.value) { 171 | hasEmpty = true; 172 | } 173 | })); 174 | return hasEmpty; 175 | } 176 | 177 | } 178 | -------------------------------------------------------------------------------- /src/main/angular-app/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluig/app-angular-poui/9f915cbe2b0b13b4f71c5256252a144fd99e239f/src/main/angular-app/src/assets/.gitkeep -------------------------------------------------------------------------------- /src/main/angular-app/src/browserslist: -------------------------------------------------------------------------------- 1 | # This file is currently used by autoprefixer to adjust CSS to support the below specified browsers 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | # 5 | # For IE 9-11 support, please remove 'not' from the last line of the file and adjust as needed 6 | 7 | > 0.5% 8 | last 2 versions 9 | Firefox ESR 10 | not dead 11 | not IE 9-11 -------------------------------------------------------------------------------- /src/main/angular-app/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true, 3 | OAUTH_CONSUMER_PUBLIC: '<YOUR_OAUTH_CONSUMER_PUBLIC>', 4 | OAUTH_CONSUMER_SECRET: '<YOUR_OAUTH_CONSUMER_SECRET>', 5 | OAUTH_SIGNATURE_METHOD: '<YOUR_OAUTH_SIGNATURE_METHOD>', 6 | TOKEN_PUBLIC: '<YOUR_TOKEN_PUBLIC>', 7 | TOKEN_SECRET: '<YOUR_TOKEN_SECRET>', 8 | }; 9 | -------------------------------------------------------------------------------- /src/main/angular-app/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false, 7 | OAUTH_CONSUMER_PUBLIC: '<YOUR_OAUTH_CONSUMER_PUBLIC>', 8 | OAUTH_CONSUMER_SECRET: '<YOUR_OAUTH_CONSUMER_SECRET>', 9 | OAUTH_SIGNATURE_METHOD: '<YOUR_OAUTH_SIGNATURE_METHOD>', 10 | TOKEN_PUBLIC: '<YOUR_TOKEN_PUBLIC>', 11 | TOKEN_SECRET: '<YOUR_TOKEN_SECRET>', 12 | }; 13 | 14 | /* 15 | * For easier debugging in development mode, you can import the following file 16 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. 17 | * 18 | * This import should be commented out in production mode because it will have a negative impact 19 | * on performance if an error is thrown. 20 | */ 21 | // import 'zone.js/plugins/zone-error'; // Included with Angular CLI. 22 | -------------------------------------------------------------------------------- /src/main/angular-app/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluig/app-angular-poui/9f915cbe2b0b13b4f71c5256252a144fd99e239f/src/main/angular-app/src/favicon.ico -------------------------------------------------------------------------------- /src/main/angular-app/src/index.html: -------------------------------------------------------------------------------- 1 | <!doctype html> 2 | <html lang="en"> 3 | <head> 4 | <meta charset="utf-8"> 5 | <title>TotvsAppAngularPOUI 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/main/angular-app/src/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/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', 'text-summary'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false 30 | }); 31 | }; -------------------------------------------------------------------------------- /src/main/angular-app/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 | .catch(err => console.error(err)); 13 | -------------------------------------------------------------------------------- /src/main/angular-app/src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /*************************************************************************************************** 2 | * Load `$localize` onto the global scope - used if i18n tags appear in Angular templates. 3 | */ 4 | import '@angular/localize/init'; 5 | /** 6 | * This file includes polyfills needed by Angular and is loaded before the app. 7 | * You can add your own extra polyfills to this file. 8 | * 9 | * This file is divided into 2 sections: 10 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 11 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 12 | * file. 13 | * 14 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 15 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 16 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 17 | * 18 | * Learn more in https://angular.io/guide/browser-support 19 | */ 20 | 21 | /*************************************************************************************************** 22 | * BROWSER POLYFILLS 23 | */ 24 | 25 | /** IE9, IE10 and IE11 requires all of the following polyfills. **/ 26 | // import 'core-js/es6/symbol'; 27 | // import 'core-js/es6/object'; 28 | // import 'core-js/es6/function'; 29 | // import 'core-js/es6/parse-int'; 30 | // import 'core-js/es6/parse-float'; 31 | // import 'core-js/es6/number'; 32 | // import 'core-js/es6/math'; 33 | // import 'core-js/es6/string'; 34 | // import 'core-js/es6/date'; 35 | // import 'core-js/es6/array'; 36 | // import 'core-js/es6/regexp'; 37 | // import 'core-js/es6/map'; 38 | // import 'core-js/es6/weak-map'; 39 | // import 'core-js/es6/set'; 40 | 41 | /** 42 | * If the application will be indexed by Google Search, the following is required. 43 | * Googlebot uses a renderer based on Chrome 41. 44 | * https://developers.google.com/search/docs/guides/rendering 45 | **/ 46 | // import 'core-js/es6/array'; 47 | 48 | /** IE10 and IE11 requires the following for the Reflect API. */ 49 | // import 'core-js/es6/reflect'; 50 | 51 | /** 52 | * By default, zone.js will patch all possible macroTask and DomEvents 53 | * user can disable parts of macroTask/DomEvents patch by setting following flags 54 | * because those flags need to be set before `zone.js` being loaded, and webpack 55 | * will put import in the top of bundle, so user need to create a separate file 56 | * in this directory (for example: zone-flags.ts), and put the following flags 57 | * into that file, and then add the following code before importing zone.js. 58 | * import './zone-flags.ts'; 59 | * 60 | * The flags allowed in zone-flags.ts are listed here. 61 | * 62 | * The following flags will work for all browsers. 63 | * 64 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 65 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 66 | * (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 67 | * 68 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 69 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 70 | * 71 | * (window as any).__Zone_enable_cross_context_check = true; 72 | * 73 | */ 74 | 75 | /*************************************************************************************************** 76 | * Zone JS is required by default for Angular itself. 77 | */ 78 | import 'zone.js'; // Included with Angular CLI. 79 | 80 | 81 | /*************************************************************************************************** 82 | * APPLICATION IMPORTS 83 | */ 84 | -------------------------------------------------------------------------------- /src/main/angular-app/src/scripts/oauth/enc-base64-min.js: -------------------------------------------------------------------------------- 1 | /* 2 | CryptoJS v3.1.2 3 | code.google.com/p/crypto-js 4 | (c) 2009-2013 by Jeff Mott. All rights reserved. 5 | code.google.com/p/crypto-js/wiki/License 6 | */ 7 | (function(){var h=CryptoJS,j=h.lib.WordArray;h.enc.Base64={stringify:function(b){var e=b.words,f=b.sigBytes,c=this._map;b.clamp();b=[];for(var a=0;a>>2]>>>24-8*(a%4)&255)<<16|(e[a+1>>>2]>>>24-8*((a+1)%4)&255)<<8|e[a+2>>>2]>>>24-8*((a+2)%4)&255,g=0;4>g&&a+0.75*g>>6*(3-g)&63));if(e=c.charAt(64))for(;b.length%4;)b.push(e);return b.join("")},parse:function(b){var e=b.length,f=this._map,c=f.charAt(64);c&&(c=b.indexOf(c),-1!=c&&(e=c));for(var c=[],a=0,d=0;d< 8 | e;d++)if(d%4){var g=f.indexOf(b.charAt(d-1))<<2*(d%4),h=f.indexOf(b.charAt(d))>>>6-2*(d%4);c[a>>>2]|=(g|h)<<24-8*(a%4);a++}return j.create(c,a)},_map:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="}})(); 9 | -------------------------------------------------------------------------------- /src/main/angular-app/src/scripts/oauth/hmac-sha1.js: -------------------------------------------------------------------------------- 1 | /* 2 | CryptoJS v3.1.2 3 | code.google.com/p/crypto-js 4 | (c) 2009-2013 by Jeff Mott. All rights reserved. 5 | code.google.com/p/crypto-js/wiki/License 6 | */ 7 | var CryptoJS=CryptoJS||function(g,l){var e={},d=e.lib={},m=function(){},k=d.Base={extend:function(a){m.prototype=this;var c=new m;a&&c.mixIn(a);c.hasOwnProperty("init")||(c.init=function(){c.$super.init.apply(this,arguments)});c.init.prototype=c;c.$super=this;return c},create:function(){var a=this.extend();a.init.apply(a,arguments);return a},init:function(){},mixIn:function(a){for(var c in a)a.hasOwnProperty(c)&&(this[c]=a[c]);a.hasOwnProperty("toString")&&(this.toString=a.toString)},clone:function(){return this.init.prototype.extend(this)}}, 8 | p=d.WordArray=k.extend({init:function(a,c){a=this.words=a||[];this.sigBytes=c!=l?c:4*a.length},toString:function(a){return(a||n).stringify(this)},concat:function(a){var c=this.words,q=a.words,f=this.sigBytes;a=a.sigBytes;this.clamp();if(f%4)for(var b=0;b>>2]|=(q[b>>>2]>>>24-8*(b%4)&255)<<24-8*((f+b)%4);else if(65535>>2]=q[b>>>2];else c.push.apply(c,q);this.sigBytes+=a;return this},clamp:function(){var a=this.words,c=this.sigBytes;a[c>>>2]&=4294967295<< 9 | 32-8*(c%4);a.length=g.ceil(c/4)},clone:function(){var a=k.clone.call(this);a.words=this.words.slice(0);return a},random:function(a){for(var c=[],b=0;b>>2]>>>24-8*(f%4)&255;b.push((d>>>4).toString(16));b.push((d&15).toString(16))}return b.join("")},parse:function(a){for(var c=a.length,b=[],f=0;f>>3]|=parseInt(a.substr(f, 10 | 2),16)<<24-4*(f%8);return new p.init(b,c/2)}},j=b.Latin1={stringify:function(a){var c=a.words;a=a.sigBytes;for(var b=[],f=0;f>>2]>>>24-8*(f%4)&255));return b.join("")},parse:function(a){for(var c=a.length,b=[],f=0;f>>2]|=(a.charCodeAt(f)&255)<<24-8*(f%4);return new p.init(b,c)}},h=b.Utf8={stringify:function(a){try{return decodeURIComponent(escape(j.stringify(a)))}catch(c){throw Error("Malformed UTF-8 data");}},parse:function(a){return j.parse(unescape(encodeURIComponent(a)))}}, 11 | r=d.BufferedBlockAlgorithm=k.extend({reset:function(){this._data=new p.init;this._nDataBytes=0},_append:function(a){"string"==typeof a&&(a=h.parse(a));this._data.concat(a);this._nDataBytes+=a.sigBytes},_process:function(a){var c=this._data,b=c.words,f=c.sigBytes,d=this.blockSize,e=f/(4*d),e=a?g.ceil(e):g.max((e|0)-this._minBufferSize,0);a=e*d;f=g.min(4*a,f);if(a){for(var k=0;ka;a++){if(16>a)m[a]=d[e+a]|0;else{var c=m[a-3]^m[a-8]^m[a-14]^m[a-16];m[a]=c<<1|c>>>31}c=(n<<5|n>>>27)+l+m[a];c=20>a?c+((j&h|~j&g)+1518500249):40>a?c+((j^h^g)+1859775393):60>a?c+((j&h|j&g|h&g)-1894007588):c+((j^h^ 15 | g)-899497514);l=g;g=h;h=j<<30|j>>>2;j=n;n=c}b[0]=b[0]+n|0;b[1]=b[1]+j|0;b[2]=b[2]+h|0;b[3]=b[3]+g|0;b[4]=b[4]+l|0},_doFinalize:function(){var d=this._data,e=d.words,b=8*this._nDataBytes,g=8*d.sigBytes;e[g>>>5]|=128<<24-g%32;e[(g+64>>>9<<4)+14]=Math.floor(b/4294967296);e[(g+64>>>9<<4)+15]=b;d.sigBytes=4*e.length;this._process();return this._hash},clone:function(){var e=d.clone.call(this);e._hash=this._hash.clone();return e}});g.SHA1=d._createHelper(l);g.HmacSHA1=d._createHmacHelper(l)})(); 16 | (function(){var g=CryptoJS,l=g.enc.Utf8;g.algo.HMAC=g.lib.Base.extend({init:function(e,d){e=this._hasher=new e.init;"string"==typeof d&&(d=l.parse(d));var g=e.blockSize,k=4*g;d.sigBytes>k&&(d=e.finalize(d));d.clamp();for(var p=this._oKey=d.clone(),b=this._iKey=d.clone(),n=p.words,j=b.words,h=0;h>>2]|=(d[e>>>2]>>>24-8*(e%4)&255)<<24-8*((b+e)%4);else if(65535>>2]=d[e>>>2];else c.push.apply(c,d);this.sigBytes+=a;return this},clamp:function(){var a=this.words,c=this.sigBytes;a[c>>>2]&=4294967295<< 9 | 32-8*(c%4);a.length=h.ceil(c/4)},clone:function(){var a=m.clone.call(this);a.words=this.words.slice(0);return a},random:function(a){for(var c=[],d=0;d>>2]>>>24-8*(b%4)&255;d.push((e>>>4).toString(16));d.push((e&15).toString(16))}return d.join("")},parse:function(a){for(var c=a.length,d=[],b=0;b>>3]|=parseInt(a.substr(b, 10 | 2),16)<<24-4*(b%8);return new r.init(d,c/2)}},n=l.Latin1={stringify:function(a){var c=a.words;a=a.sigBytes;for(var d=[],b=0;b>>2]>>>24-8*(b%4)&255));return d.join("")},parse:function(a){for(var c=a.length,d=[],b=0;b>>2]|=(a.charCodeAt(b)&255)<<24-8*(b%4);return new r.init(d,c)}},j=l.Utf8={stringify:function(a){try{return decodeURIComponent(escape(n.stringify(a)))}catch(c){throw Error("Malformed UTF-8 data");}},parse:function(a){return n.parse(unescape(encodeURIComponent(a)))}}, 11 | u=g.BufferedBlockAlgorithm=m.extend({reset:function(){this._data=new r.init;this._nDataBytes=0},_append:function(a){"string"==typeof a&&(a=j.parse(a));this._data.concat(a);this._nDataBytes+=a.sigBytes},_process:function(a){var c=this._data,d=c.words,b=c.sigBytes,e=this.blockSize,f=b/(4*e),f=a?h.ceil(f):h.max((f|0)-this._minBufferSize,0);a=f*e;b=h.min(4*a,b);if(a){for(var g=0;gn;){var j;a:{j=k;for(var u=h.sqrt(j),t=2;t<=u;t++)if(!(j%t)){j=!1;break a}j=!0}j&&(8>n&&(m[n]=l(h.pow(k,0.5))),r[n]=l(h.pow(k,1/3)),n++);k++}var a=[],f=f.SHA256=q.extend({_doReset:function(){this._hash=new g.init(m.slice(0))},_doProcessBlock:function(c,d){for(var b=this._hash.words,e=b[0],f=b[1],g=b[2],j=b[3],h=b[4],m=b[5],n=b[6],q=b[7],p=0;64>p;p++){if(16>p)a[p]= 15 | c[d+p]|0;else{var k=a[p-15],l=a[p-2];a[p]=((k<<25|k>>>7)^(k<<14|k>>>18)^k>>>3)+a[p-7]+((l<<15|l>>>17)^(l<<13|l>>>19)^l>>>10)+a[p-16]}k=q+((h<<26|h>>>6)^(h<<21|h>>>11)^(h<<7|h>>>25))+(h&m^~h&n)+r[p]+a[p];l=((e<<30|e>>>2)^(e<<19|e>>>13)^(e<<10|e>>>22))+(e&f^e&g^f&g);q=n;n=m;m=h;h=j+k|0;j=g;g=f;f=e;e=k+l|0}b[0]=b[0]+e|0;b[1]=b[1]+f|0;b[2]=b[2]+g|0;b[3]=b[3]+j|0;b[4]=b[4]+h|0;b[5]=b[5]+m|0;b[6]=b[6]+n|0;b[7]=b[7]+q|0},_doFinalize:function(){var a=this._data,d=a.words,b=8*this._nDataBytes,e=8*a.sigBytes; 16 | d[e>>>5]|=128<<24-e%32;d[(e+64>>>9<<4)+14]=h.floor(b/4294967296);d[(e+64>>>9<<4)+15]=b;a.sigBytes=4*d.length;this._process();return this._hash},clone:function(){var a=q.clone.call(this);a._hash=this._hash.clone();return a}});s.SHA256=q._createHelper(f);s.HmacSHA256=q._createHmacHelper(f)})(Math); 17 | (function(){var h=CryptoJS,s=h.enc.Utf8;h.algo.HMAC=h.lib.Base.extend({init:function(f,g){f=this._hasher=new f.init;"string"==typeof g&&(g=s.parse(g));var h=f.blockSize,m=4*h;g.sigBytes>m&&(g=f.finalize(g));g.clamp();for(var r=this._oKey=g.clone(),l=this._iKey=g.clone(),k=r.words,n=l.words,j=0;j merge with oauth data 122 | * -> percent encode key & value 123 | * -> sort 124 | * 125 | * @param {Object} request data 126 | * @param {Object} OAuth data 127 | * @return {Object} Parameter string data 128 | */ 129 | OAuth.prototype.getParameterString = function(request, oauth_data) { 130 | var base_string_data = this.sortObject(this.percentEncodeData(this.mergeObject(oauth_data, this.mergeObject(request.data, this.deParamUrl(request.url))))); 131 | 132 | var data_str = ''; 133 | 134 | //base_string_data to string 135 | for(var key in base_string_data) { 136 | var value = base_string_data[key]; 137 | // check if the value is an array 138 | // this means that this key has multiple values 139 | if (value && Array.isArray(value)){ 140 | // sort the array first 141 | value.sort(); 142 | 143 | var valString = ""; 144 | // serialize all values for this key: e.g. formkey=formvalue1&formkey=formvalue2 145 | value.forEach((function(item, i){ 146 | valString += key + '=' + item; 147 | if (i < value.length){ 148 | valString += "&"; 149 | } 150 | }).bind(this)); 151 | data_str += valString; 152 | } else { 153 | data_str += key + '=' + value + '&'; 154 | } 155 | } 156 | 157 | //remove the last character 158 | data_str = data_str.substr(0, data_str.length - 1); 159 | return data_str; 160 | }; 161 | 162 | /** 163 | * Create a Signing Key 164 | * @param {String} token_secret Secret Token 165 | * @return {String} Signing Key 166 | */ 167 | OAuth.prototype.getSigningKey = function(token_secret) { 168 | token_secret = token_secret || ''; 169 | 170 | if(!this.last_ampersand && !token_secret) { 171 | return this.percentEncode(this.consumer.secret); 172 | } 173 | 174 | return this.percentEncode(this.consumer.secret) + '&' + this.percentEncode(token_secret); 175 | }; 176 | 177 | /** 178 | * Get base url 179 | * @param {String} url 180 | * @return {String} 181 | */ 182 | OAuth.prototype.getBaseUrl = function(url) { 183 | return url.split('?')[0]; 184 | }; 185 | 186 | /** 187 | * Get data from String 188 | * @param {String} string 189 | * @return {Object} 190 | */ 191 | OAuth.prototype.deParam = function(string) { 192 | var arr = string.split('&'); 193 | var data = {}; 194 | 195 | for(var i = 0; i < arr.length; i++) { 196 | var item = arr[i].split('='); 197 | data[item[0]] = decodeURIComponent(item[1]); 198 | } 199 | return data; 200 | }; 201 | 202 | /** 203 | * Get data from url 204 | * @param {String} url 205 | * @return {Object} 206 | */ 207 | OAuth.prototype.deParamUrl = function(url) { 208 | var tmp = url.split('?'); 209 | 210 | if (tmp.length === 1) 211 | return {}; 212 | 213 | return this.deParam(tmp[1]); 214 | }; 215 | 216 | /** 217 | * Percent Encode 218 | * @param {String} str 219 | * @return {String} percent encoded string 220 | */ 221 | OAuth.prototype.percentEncode = function(str) { 222 | return encodeURIComponent(str) 223 | .replace(/\!/g, "%21") 224 | .replace(/\*/g, "%2A") 225 | .replace(/\'/g, "%27") 226 | .replace(/\(/g, "%28") 227 | .replace(/\)/g, "%29"); 228 | }; 229 | 230 | /** 231 | * Percent Encode Object 232 | * @param {Object} data 233 | * @return {Object} percent encoded data 234 | */ 235 | OAuth.prototype.percentEncodeData = function(data) { 236 | var result = {}; 237 | 238 | for(var key in data) { 239 | var value = data[key]; 240 | // check if the value is an array 241 | if (value && Array.isArray(value)){ 242 | var newValue = []; 243 | // percentEncode every value 244 | value.forEach((function(val){ 245 | newValue.push(this.percentEncode(val)); 246 | }).bind(this)); 247 | value = newValue; 248 | } else { 249 | value = this.percentEncode(value); 250 | } 251 | result[this.percentEncode(key)] = value; 252 | } 253 | 254 | return result; 255 | }; 256 | 257 | /** 258 | * Get OAuth data as Header 259 | * @param {Object} oauth_data 260 | * @return {String} Header data key - value 261 | */ 262 | OAuth.prototype.toHeader = function(oauth_data) { 263 | oauth_data = this.sortObject(oauth_data); 264 | 265 | var header_value = 'OAuth '; 266 | 267 | for(var key in oauth_data) { 268 | if (key.indexOf('oauth_') === -1) 269 | continue; 270 | header_value += this.percentEncode(key) + '="' + this.percentEncode(oauth_data[key]) + '"' + this.parameter_seperator; 271 | } 272 | 273 | return { 274 | Authorization: header_value.substr(0, header_value.length - this.parameter_seperator.length) //cut the last chars 275 | }; 276 | }; 277 | 278 | /** 279 | * Create a random word characters string with input length 280 | * @return {String} a random word characters string 281 | */ 282 | OAuth.prototype.getNonce = function() { 283 | var word_characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; 284 | var result = ''; 285 | 286 | for(var i = 0; i < this.nonce_length; i++) { 287 | result += word_characters[parseInt(Math.random() * word_characters.length, 10)]; 288 | } 289 | 290 | return result; 291 | }; 292 | 293 | /** 294 | * Get Current Unix TimeStamp 295 | * @return {Int} current unix timestamp 296 | */ 297 | OAuth.prototype.getTimeStamp = function() { 298 | return parseInt(new Date().getTime()/1000, 10); 299 | }; 300 | 301 | ////////////////////// HELPER FUNCTIONS ////////////////////// 302 | 303 | /** 304 | * Merge object 305 | * @param {Object} obj1 306 | * @param {Object} obj2 307 | * @return {Object} 308 | */ 309 | OAuth.prototype.mergeObject = function(obj1, obj2) { 310 | var merged_obj = obj1; 311 | for(var key in obj2) { 312 | merged_obj[key] = obj2[key]; 313 | } 314 | return merged_obj; 315 | }; 316 | 317 | /** 318 | * Sort object by key 319 | * @param {Object} data 320 | * @return {Object} sorted object 321 | */ 322 | OAuth.prototype.sortObject = function(data) { 323 | var keys = Object.keys(data); 324 | var result = {}; 325 | 326 | keys.sort(); 327 | 328 | for(var i = 0; i < keys.length; i++) { 329 | var key = keys[i]; 330 | result[key] = data[key]; 331 | } 332 | 333 | return result; 334 | }; 335 | -------------------------------------------------------------------------------- /src/main/angular-app/src/styles.scss: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | 3 | /** 4 | * Overwrites .wcm-all-content padding when 5 | * in fullscreen mode (without legacy header/menu) 6 | */ 7 | .wcm-wrapper-content.fs .wcm-all-content { 8 | 9 | padding: 0px; 10 | 11 | #wcm-content { 12 | padding: 0 !important; 13 | } 14 | } -------------------------------------------------------------------------------- /src/main/angular-app/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/testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: any; 11 | 12 | // First, initialize the Angular testing environment. 13 | getTestBed().initTestEnvironment( 14 | BrowserDynamicTestingModule, 15 | platformBrowserDynamicTesting(), { 16 | teardown: { destroyAfterEach: false } 17 | } 18 | ); 19 | // Then we find all the tests. 20 | const context = require.context('./', true, /\.spec\.ts$/); 21 | // And load the modules. 22 | context.keys().map(context); 23 | -------------------------------------------------------------------------------- /src/main/angular-app/src/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "types": [] 6 | }, 7 | "exclude": [ 8 | "test.ts", 9 | "**/*.spec.ts" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /src/main/angular-app/src/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "test.ts", 12 | "polyfills.ts" 13 | ], 14 | "include": [ 15 | "**/*.spec.ts", 16 | "**/*.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /src/main/angular-app/src/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tslint.json", 3 | "rules": { 4 | "directive-selector": [ 5 | true, 6 | "attribute", 7 | "app", 8 | "camelCase" 9 | ], 10 | "component-selector": [ 11 | true, 12 | "element", 13 | "app", 14 | "kebab-case" 15 | ] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/angular-app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "module": "CommonJS", 9 | "moduleResolution": "node", 10 | "experimentalDecorators": true, 11 | "importHelpers": true, 12 | "target": "es5", 13 | "typeRoots": [ 14 | "node_modules/@types" 15 | ], 16 | "lib": [ 17 | "es2018", 18 | "dom" 19 | ] 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/angular-app/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": [ 3 | "codelyzer" 4 | ], 5 | "rules": { 6 | "arrow-return-shorthand": true, 7 | "callable-types": true, 8 | "class-name": true, 9 | "comment-format": [ 10 | true, 11 | "check-space" 12 | ], 13 | "curly": true, 14 | "deprecation": { 15 | "severity": "warn" 16 | }, 17 | "eofline": true, 18 | "forin": true, 19 | "import-blacklist": [ 20 | true, 21 | "rxjs/Rx" 22 | ], 23 | "import-spacing": true, 24 | "indent": [ 25 | true, 26 | "spaces" 27 | ], 28 | "interface-over-type-literal": true, 29 | "label-position": true, 30 | "max-line-length": [ 31 | true, 32 | 140 33 | ], 34 | "member-access": false, 35 | "member-ordering": [ 36 | true, 37 | { 38 | "order": [ 39 | "static-field", 40 | "instance-field", 41 | "static-method", 42 | "instance-method" 43 | ] 44 | } 45 | ], 46 | "no-arg": true, 47 | "no-bitwise": true, 48 | "no-console": [ 49 | true, 50 | "debug", 51 | "info", 52 | "time", 53 | "timeEnd", 54 | "trace" 55 | ], 56 | "no-construct": true, 57 | "no-debugger": true, 58 | "no-duplicate-super": true, 59 | "no-empty": false, 60 | "no-empty-interface": true, 61 | "no-eval": true, 62 | "no-inferrable-types": [ 63 | true, 64 | "ignore-params" 65 | ], 66 | "no-misused-new": true, 67 | "no-non-null-assertion": true, 68 | "no-redundant-jsdoc": true, 69 | "no-shadowed-variable": true, 70 | "no-string-literal": false, 71 | "no-string-throw": true, 72 | "no-switch-case-fall-through": true, 73 | "no-trailing-whitespace": true, 74 | "no-unnecessary-initializer": true, 75 | "no-unused-expression": true, 76 | "no-var-keyword": true, 77 | "object-literal-sort-keys": false, 78 | "one-line": [ 79 | true, 80 | "check-open-brace", 81 | "check-catch", 82 | "check-else", 83 | "check-whitespace" 84 | ], 85 | "prefer-const": true, 86 | "quotemark": [ 87 | true, 88 | "single" 89 | ], 90 | "radix": true, 91 | "semicolon": [ 92 | true, 93 | "always" 94 | ], 95 | "triple-equals": [ 96 | true, 97 | "allow-null-check" 98 | ], 99 | "typedef-whitespace": [ 100 | true, 101 | { 102 | "call-signature": "nospace", 103 | "index-signature": "nospace", 104 | "parameter": "nospace", 105 | "property-declaration": "nospace", 106 | "variable-declaration": "nospace" 107 | } 108 | ], 109 | "unified-signatures": true, 110 | "variable-name": false, 111 | "whitespace": [ 112 | true, 113 | "check-branch", 114 | "check-decl", 115 | "check-operator", 116 | "check-separator", 117 | "check-type" 118 | ], 119 | "no-output-on-prefix": true, 120 | "use-input-property-decorator": true, 121 | "use-output-property-decorator": true, 122 | "use-host-property-decorator": true, 123 | "no-input-rename": true, 124 | "no-output-rename": true, 125 | "use-life-cycle-interface": true, 126 | "use-pipe-transform-interface": true, 127 | "component-class-suffix": true, 128 | "directive-class-suffix": true 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/main/resources/app_angular_poui.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluig/app-angular-poui/9f915cbe2b0b13b4f71c5256252a144fd99e239f/src/main/resources/app_angular_poui.properties -------------------------------------------------------------------------------- /src/main/resources/app_angular_poui_en_US.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluig/app-angular-poui/9f915cbe2b0b13b4f71c5256252a144fd99e239f/src/main/resources/app_angular_poui_en_US.properties -------------------------------------------------------------------------------- /src/main/resources/app_angular_poui_es.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluig/app-angular-poui/9f915cbe2b0b13b4f71c5256252a144fd99e239f/src/main/resources/app_angular_poui_es.properties -------------------------------------------------------------------------------- /src/main/resources/app_angular_poui_pt_BR.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluig/app-angular-poui/9f915cbe2b0b13b4f71c5256252a144fd99e239f/src/main/resources/app_angular_poui_pt_BR.properties -------------------------------------------------------------------------------- /src/main/resources/app_angular_thf.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluig/app-angular-poui/9f915cbe2b0b13b4f71c5256252a144fd99e239f/src/main/resources/app_angular_thf.properties -------------------------------------------------------------------------------- /src/main/resources/app_angular_thf_en_US.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluig/app-angular-poui/9f915cbe2b0b13b4f71c5256252a144fd99e239f/src/main/resources/app_angular_thf_en_US.properties -------------------------------------------------------------------------------- /src/main/resources/app_angular_thf_es.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluig/app-angular-poui/9f915cbe2b0b13b4f71c5256252a144fd99e239f/src/main/resources/app_angular_thf_es.properties -------------------------------------------------------------------------------- /src/main/resources/app_angular_thf_pt_BR.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluig/app-angular-poui/9f915cbe2b0b13b4f71c5256252a144fd99e239f/src/main/resources/app_angular_thf_pt_BR.properties -------------------------------------------------------------------------------- /src/main/resources/application.info: -------------------------------------------------------------------------------- 1 | application.type=widget 2 | application.code=app_angular_poui 3 | application.title=Angular APP PO UI 4 | application.description=Angular APP PO UI 5 | application.category=SYSTEM 6 | application.renderer=freemarker 7 | application.icon=icon.png 8 | application.uiwidget=true 9 | developer.code=TOTVS 10 | developer.name=TOTVS S.A. 11 | developer.url=http\://www.totvs.com 12 | view.file=view.ftl 13 | locale.file.base.name=app_angular_poui 14 | simple.deploy=true 15 | application.mobileapp=true 16 | -------------------------------------------------------------------------------- /src/main/resources/view.ftl: -------------------------------------------------------------------------------- 1 | <#assign coreContext='app_angular_poui'> 2 | 3 | 4 | 5 | 6 | 7 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/jboss-web.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | /app_angular_poui 4 | false 5 | 6 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | 30 10 | 11 | 12 | 13 | --------------------------------------------------------------------------------