# Arquitetura - Admin 3.0
Este documento visa explicar a nova estrutura do admin para o b2b. Mudando a arquitetura do frontend para vuejs, os nomes e estruturas das pastas foram escolhidos para manter padrões de trabalho do nosso dia a dia.
# Sumário
- [Plugins e versões](#Plugins-e-versões)
- [Vue Auto Routing](#Vue-Auto-Routing)
- [Vue Router Layout](#Vue-Router-Layout)
- [Estrutura da aplicação](#Estrutura-da-aplicação)
- [Vue Router](#Vue-Router)
- [Vuetify](#Vuetify)
- [Axios](#Axios)
- [Vuex](#Vuex)
- [Helpers customizaveis do sistema](#Helpers-customizaveis-do-sistema)
- [Uso dos componentes padrões do sistema](#Uso-dos-componentes-padrões-do-sistema)
- [Uso das props globais](#Uso-das-props-globais)
- [Padronização no backend do admin](#Padronização-no-backend-do-admin)
- [Funcionamento do hot reload em ambiente de desenvolvimento](#Funcionamento-do-hot-reload-em-ambiente-de-desenvolvimento)
- [Criação de uma nova tela](#Criação-de-uma-nova-tela)
## Plugins e versões
### Dependências
- Vue `^2.6.11`
- Vue-Router `^3.1.3`
- Vue-Router-Layout `^0.1.2`
- Vuetify `^2.2.11`
- Axios `^0.19.0`
- Vuex `^3.4.0`
___
### **Dependências de desenvolvimento**
- Vue-Cli-Plugin-Auto-Routing `^0.1.2`
- Vue-Auto-Routing `^0.4.1`
- Vuetify-Loader `^1.3.0`
## Vue Auto Routing
> [Github](https://github.com/ktsn/vue-cli-plugin-auto-routing)
Este plugin é usado para gerar rotas automaticamente de acordo com a estrutura das pastas.
É possível escolher qual a raiz das pastas que serão transformadas em rotas.
Exemplo:
```
src/
├── layouts/
├── pages/ * pages foi a raiz escolhida para auto routing *
├── produtos/
├── index.vue
├── index.vue
└── router.js
```
Então teremos as rotas:
```
url.com/produtos/index
url.com/index
```
## Vue Router Layout
> [Github](https://github.com/ktsn/vue-router-layout)
Este plugin é usado para escolher o layout de cada componente renderizado.
Os layouts são definidos no diretório `src/layouts` sendo `default.vue` o layout padrão:
```
src/
├── layouts/
├── default.vue
├── pages/
└── router.js
```
O layout default é estruturado do seguinte modo:
> default.vue
``` [vuejs]
<template>
<v-app id="inspire">
<side-bar/>
<v-content id="container-iframe">
<v-container v-if="legacies.length > 0" fluid>
<v-tabs v-model="tab" background-color="secondary" :show-arrows="true">
<v-tab v-for="(item, key) in legacies" :key="key" :href="`#tab-${key}`">
{{ item.name }}
<v-tooltip top>
<template v-slot:activator="{ on, attrs }">
<v-btn icon v-bind="attrs" v-on="on" @click="removeLegacyFrame(key)">
<v-icon x-small>mdi-close</v-icon>
</v-btn>
</template>
<span>Fechar aba</span>
</v-tooltip>
</v-tab>
<v-tab-item v-for="(item, key) in legacies" :key="key" :value="'tab-' + key" style="height: 100%;">
<v-card class="mx-auto" :height="maxContentHeight" tile>
<iframe :src="item.url" frameborder="0" style="width: 100%; height: 100%;"></iframe>
</v-card>
</v-tab-item>
</v-tabs>
</v-container>
<v-container class="fill-height" fluid v-else>
<v-row align="center" justify="center">
<v-col class="text-center">
<router-view/>
</v-col>
</v-row>
</v-container>
</v-content>
<copyFooter/>
</v-app>
</template>
```
Por padrão os componentes renderizados usam o layout default, porém podem ser escolhidos da seguinte forma:
> produtos/gerenciar.vue
``` [vuejs]
<template>
<div>
<h1>Produtos</h1>
</div>
</template>
<script>
export default {
layout: 'layout1' * layout1 escolhido para ser o layout usado na renderização
}
</script>
```
Mas ao mesmo tempo que o arquivo default renderiza os componentes criado em vue js, ele foi customizado para suportar a renderização das telas legadas do admin, para que a nova estrutura seja compatível tanto com as telas antigas, quanto as novas, é possível visualizar a renderização pela parte a seguir:
``` [vuejs]
<iframe :src="item.url" frameborder="0" style="width: 100%; height: 100%;"></iframe>
```
## Estrutura da aplicação
O diretório atual do projeto está definida na pasta `/front` dentro do `/admin`:
```
admin/
├── .gitlab/
├── .vscode/
├── ci/
├── docker/
├── docs/
├── front/ * projeto vue do admin
├── src/
├── test/
├── .editorconfig
├── .env
├── .gitignore
├── .gitlab-ci.yml
├── .gitlab-deploy.sh
├── build.sh
├── composer.json
├── docker-compose.yml
├── Dockerfile
├── gitextract.sh
├── install.sh
├── README.md
├── shipitfile.js
└── sonar-scanner.template
```
Nossa estrutura de pastas será exatamente essa:
```
front/
├── public
├── index.html
├── src/
├── assets/
├── scss/ * local onde os scss deverão ser criados
├── layout/
├── index.scss
├── main.scss
├── core/
├── helpers/ * helpers uteis ao sistema
├── filters.js
├── functions.js
├── index.js
├── validation.js
├── index.js
├── layouts/
├── default.vue * onde todas as rotas irão ser renderizadas
├── pages/
├── components/
├── alert/ * componente de alert geral do sistema
├── footer/ * componente do footer
├── loader/ * componente de loader geral do sistema
├── logotype/ * componente do logotipo da loja
├── shop-site/ * componente onde renderiza o seletor de site
├── sidebar/ * componente onde renderiza o topbar e sidebar com o menu do admin
├── login/
├── index.vue * página de login do sistema
├── index.vue * onde renderiza os componentes
├── plugins/ * pasta plugins do sistema
├── loader.js
├── vuetify.js
├── router/ * pasta router
├── index.js
├── server/ * pasta axios
├── index.js
├── store/ * pasta vuex (aqui deverão ser criados todos os stores das telas do sistema, com o nome da pasta com o nome da tela em ingles)
├── auth/
├── actions.js
├── index.js
├── mutations.js
├── state.js
├── sidebar/
├── actions.js
├── index.js
├── mutations.js
├── state.js
├── sys/
├── actions.js
├── index.js
├── mutations.js
├── state.js
├── actions.js
├── getters.js
├── index.js
├── mutations.js
├── state.js
├── App.vue
├── globalComponents.js * arquivo dos componentes globais
├── globalPages.js * arquivo dos componentes de páginas globais
├── main.js
```
## Vue Router
**Sempre lembrar de utilizar a estrutura de código encontrada dentro do arquivo router.js**:
``` [js]
import Vue from 'vue'
import VueRouter from 'vue-router'
import routes from 'vue-auto-routing'
import { createRouterLayout } from 'vue-router-layout'
Vue.use(VueRouter);
const RouterLayout = createRouterLayout(layout => {
return import('@/layouts/' + layout + '.vue')
});
import Login from '@/pages/login';
const router = new VueRouter({
routes: [
{
path: '/',
component: RouterLayout,
children: routes
},
{
path: '/pages/login',
component: Login,
children: routes
}
]
});
export default router
```
Na pasta raiz `front/`, temos um arquivo de configuração do vue `vue.config.js` com os seguintes dados:
``` [js]
module.exports = {
outputDir: '../src/web/vue',
publicPath: process.env.NODE_ENV === 'development'
? '/'
: '/web/vue/',
devServer: {
'disableHostCheck': true
},
pluginOptions: {
autoRouting: {
pages: 'src/pages',
importPrefix: '@/pages/',
chunkNamePrefix: 'page-'
}
},
transpileDependencies: [
'vuetify'
]
}
```
O parâmetro `outputDir` será o local onde será compilado o vue.
O parâmetro `publicPath` será a pasta raiz do vue no projeto.
O parâmetro `autoRouting` dentro de `pluginOptions` será onde definimos a pasta raiz que será usada para o auto routing.
Para as demais rotas que serão criadas de arquivos novos do sistema, podemos seguir o padrão a seguir:
``` [js]
{
path: '/pages/login',
component: Login,
children: routes
}
```
## Vuetify
Caso seja necessário alterar as configurações de estilização e cores conforme a loja que for implementada, temos o arquivo localizado em `/front/src/plugins/vuetify.js`, nele existem as configurações do template utilizado para nossa aplicação:
``` [js]
import Vue from 'vue';
import Vuetify from 'vuetify/lib';
Vue.use(Vuetify);
/*
* Caso precise modificar a identidade visual do projeto, por favor,
* modifique apenas as cores abaixo, sem alterar nome de propriedade.
* Para mais referencias de cores, acesse: https://vuetifyjs.com/en/styles/colors/
*/
export default new Vuetify({
theme: {
dark: false,
default: 'light',
themes: {
light: {
primary: '#3E1A58',
secondary: '#E18719',
accent: '#666666',
error: '#b71c1c',
info: '#2196F3',
success: '#4CAF50',
warning: '#FB8C00',
},
dark: {
primary: '#2196F3',
secondary: '#424242',
accent: '#FF4081',
error: '#FF5252',
info: '#2196F3',
success: '#4CAF50',
warning: '#FB8C00',
},
},
},
});
```
## Axios
Dentro da pasta `/front/src/server` temos o arquivo de configuração do axios, onde nele podemos preparar nossa aplicação para fazer as requisições ao backend do sistema, padronizamos da seguinte forma:
``` [js]
import axios from 'axios'
/*
* Em caso de desenvolvimento local (watcher ligado) descomentar as opções:
* baseURL: process.env.VUE_APP_URL,
* OBS: NÃO ENVIAR DESCOMENTADO ESSAS OPÇÕES EM SEU COMMIT
*/
export default axios.create({
// baseURL: process.env.VUE_APP_URL,
baseURL: '/',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
withCredentials: true,
credentials: "same-origin",
})
```
Qualquer configuração que precise atualizar de conexão com a api, deverá ser atualizado neste arquivo.
## Vuex
Para que o nosso sistema seja reativo, utilizamos o plugin `vuex`, definido no nosso `main.js`.
Em cada store, deverá ser criado da seguinte estrutura:
**exemplo store de autenticação do login**
#### Arquivo de Action
``` [js]
import axios from "@/server"
export default {
login({ commit }, payload) {
return new Promise((resolve, reject) => {
axios.post("/Login/Logar", payload, { headers: { 'Content-Type': 'multipart/form-data' } })
.then((response) => {
commit('SET_LOGIN_RESPONSE', response.data); * usado para mudar o state de resposta do login
resolve(response);
})
.catch((error) => { reject(error) })
})
},
...
}
```
#### Arquivo de Mutations
``` [js]
export default {
SET_CHANGE_PASSWORD(state, data) {
state.needsNewPassword = data;
},
SET_LOGIN_RESPONSE(state, data) {
state.loginResponse = data;
},
}
```
#### Arquivo de State
``` [js]
export default {
needsNewPassword: false,
loginResponse: {},
}
```
#### Arquivo de Index
``` [js]
import actions from './actions'
import state from './state'
import mutations from './mutations'
export default {
state,
actions,
mutations,
namespaced: true
}
```
Logo após tal implementação é necessário importar o state e exportar no index do vuex para poder utilizar em todas as telas:
#### Arquivo index do store
``` [js]
import Vue from 'vue'
import Vuex from 'vuex'
import state from "./state"
import getters from "./getters"
import mutations from "./mutations"
import actions from "./actions"
Vue.use(Vuex);
import sidebar from './sidebar'
import auth from './auth'
export default new Vuex.Store({
getters,
mutations,
state,
actions,
modules: {
sidebar,
auth
},
strict: process.env.NODE_ENV !== 'production'
})
```
#### Exemplo de uso em um componente vue
``` [vuejs]
login() {
this.$loader.init();
this.$store.dispatch('auth/login', this.formatData())
.then(() => {
this.$loader.stop();
let data = this.$store.state.auth.loginResponse;
if (data.token) {
this.withToken = data.token;
this.typeAlert = 'success';
this.messageAlert = 'O token de acesso foi enviado para seu e-mail!';
} else {
this.$router.push('/');
}
})
.catch(error => {
this.$loader.stop();
this.typeAlert = 'error';
this.messageAlert = error.response.data.errors.reason;
})
},
```
Neste exemplo vemos como usamos o `this.$store.dispatch` para dispachar um evento de store da função do `auth/login`, onde o mesmo chama a função do store e a executa como uma `Promise`. e para recuperar os dados da requisição utilizamos o state populado pela `Promise` da seguinte maneira, `this.$store.state.auth.loginResponse`.
Obs: Todas os métodos devem ter o mesmo nome da função definida no action do store, ou seja, neste exemplo, o método criado no componente se chama `login()` e o do store se chama também `login()`.
## Helpers customizaveis do sistema
Como vimos anteriormente, no core do sistema temos a pasta de helpers, nele temos todos os arquivos de utilitarios do sistema, onde são totalmente customizáveis e expansíveis, ex do que já temos:
- filters:
``` [js]
import Vue from 'vue'
Vue.filter('capitalize', function (value) {
if (!value) return '';
value = value.toString();
let arr = value.split(" ");
let capitalized_array = [];
arr.forEach((word) => {
let capitalized = word.charAt(0).toUpperCase() + word.slice(1);
capitalized_array.push(capitalized)
});
return capitalized_array.join(" ");
});
Vue.filter('title', function (value, replacer="_") {
if (!value) return '';
value = value.toString();
let arr = value.split(replacer);
let capitalized_array = [];
arr.forEach((word) => {
let capitalized = word.charAt(0).toUpperCase() + word.slice(1)
capitalized_array.push(capitalized)
});
return capitalized_array.join(" ");
});
```
Para utilizar os filtros disponíveis neste arquivo, basta chamálos externamente em seu componente da seguinte forma:
``` [vuejs]
{{ message | title }}
```
- functions:
``` [js]
class Functions {
...
isValidFileType(fn, validType) {
let i, p, e;
if (typeof fn != 'string')
return false;
p = fn.lastIndexOf('.');
if (p < 0 || ++p >= fn.length)
return false;
e = fn.substr(p);
for (i = 0; i < validType.length; i++)
if (e == validType[i])
return true;
return false;
}
}
export default new Functions();
```
- validation:
``` [js]
import functions from './functions';
export default {
rules: {
required: v => v.length > 1 || 'Este campo é obrigatório',
email: v => v.length > 1 && /.+@.+/.test(v) || 'Digite um email válido',
min: function (v, l) {
return v.length >= l || `Este campo deve conter no mínimo ${l} dígitos`
},
max: function (v, l) {
return v.length <= l || `Este campo deve conter no máximo ${l} dígitos`
},
equals_length: function (v, l) {
return v.length == l || `Este campo deve conter ${l} dígitos`
},
file_type: v => functions.isValidFileType(v) || 'Tipo de arquivo inválido',
}
};
```
Aqui temos uma implementação um pouco diferente, onde todas as funções e regras de validação ficarão disponíveis para uso a partir da nossa propriedade global `$helper`, na qual iremos explicar mais pra frente.
Caso seja necessário implementar alguma validação, função ou filtro novo, fiquem a vontade para incrementar esses arquivos. Mas caso precise criar outro tipo de helper que não seja um desses já criados, será necessário importar no arquivo `index.js` do nosso helper para que fique disponível no sistema como um todo.
## Uso dos componentes padrões do sistema
Atualmente possuimos alguns componentes que podem ser utilizados em todo o sistema da seguinte forma:
**Componente alert:**
``` [vuejs]
<template>
<v-row v-if="messageAlert != ''">
<alert :type="typeAlert" :message="messageAlert"/>
</v-row>
</template>
<script>
export default {
data() {
return {
messageAlert: '',
typeAlert: '',
}
}
}
</script>
```
Dessa forma, o alerta só será exibido caso exista algum conteúdo no mesmo, que é passado pela variável `messageAlert`.
Para utilizar esse componente, é preciso passar algumas propriedades:
- type
- Obrigatório: sim
- Tipo: string
- Valor default: ''
- Ex: `primary, secondary, accent, error, info, success e warning`
- message
- Obrigatório: sim
- Tipo: string
- Valor default: ''
- Ex: 'Seja bem vindo ao Admin!'
Todas as cores das opções para o tipo podem ser definidos no arquivo de configuração do [vuetify](#Vuetify).
**Componente dialog:**
``` [vuejs]
<template>
<view-dialog
:dialog="dialog"
:title="title"
:text="message"
:actions="true"
:btnConfirmAction="close"
btnConfirmText="Ok"
/>
</template>
<script>
export default {
data() {
return {
dialog: false,
title: '',
message: '',
color: ''
}
},
methods: {
show() {
this.dialog = true;
this.color = 'error';
this.title = 'Aviso!';
this.message = 'Ocorreu um erro ao deslogar! Tente novamente ou recarregue a pagina.';
},
close() {
this.dialog = false;
}
}
}
</script>
```
Para utilizar esse componente, é preciso passar algumas propriedades:
- dialog
- Obrigatório: sim
- Tipo: boolean
- Valor default: false
- maxWidth
- Obrigatório: não
- Tipo: [string, number]
- Valor default: '300'
- title
- Obrigatório: não
- Tipo: string
- Valor default: ''
- text
- Obrigatório: sim
- Tipo: string
- Valor default: ''
- actions
- Obrigatório: sim
- Tipo: boolean
- Valor default: false
- btnCancelAction
- Obrigatório: não
- Tipo: function
- Valor default: `null`
- btnCancelText
- Obrigatório: não
- Tipo: string
- Valor default: 'Cancelar'
- btnCancelColor
- Obrigatório: não
- Tipo: string
- Valor default: 'warning'
- btnConfirmAction
- Obrigatório: não
- Tipo: function
- Valor default: `null`
- btnConfirmText
- Obrigatório: não
- Tipo: string
- Valor default: 'Confirmar'
- btnConfirmColor
- Obrigatório: não
- Tipo: string
- Valor default: 'success'
**Componente logotype:**
```
<template>
<div>
<logotype height="300"/>
</div>
</template>
```
Para utilizar esse componente, é preciso passar a seguinte propriedade:
- height
- Obrigatório: não
- Tipo: string
- Valor default: '100%'
**Componente site:**
```
<template>
<div>
<shop-site/>
</div>
</template>
```
Para utilizar esse componente, basta chamar em sua página da maneira acima e ele irá renderizar, caso necessite do retorno do site selecionado, você deverá colocar a chamada de bind a seguir, passando uma função callbak para coletar o resultado:
```
<shop-site @set-site-value="onSetSiteValue"/>
```
Dessa forma en sua função callback, você poderá coletar o valor selecionado dessa forma:
```
onSetSiteValue(payload) {
this.site = payload;
}
```
**Componente combo de categorias:**
```
<template>
<div>
<combo-store :site-id="site" @set-category-value="afterSelectCategory"/>
</div>
</template>
<script>
export default {
data() {
return {
site: '',
categorySelected: ''
}
},
methods: {
afterSelectCategory(payload) {
this.categorySelected = payload;
}
}
}
</script>
```
Para utilizar esse componente, é preciso passar a seguinte propriedade:
- SiteId
- tipo: [String, Number]
- default: 0
- Obrigatório: sim
Essa propriedade é necessária para efetuar a busca das categorias conforme a escolha do site selecionado no componente seletor do mesmo. O componente por sua vez, emite um evento no qual é capaz de coletar os dados da loja/categoria/subcategoria selecionado, `@set-category-value`, ele recebe uma função de callback onde é possível coletar o valor selecionado para uma variável no componente pai.
Para visualizar todos esses componentes funcionando na prática e o seu layout, basta acessar a página de teste criada pelo admin `Marketing/Envelopamento/Componente Vue` ou pelo código em `pages/page-test/index.vue`.
Caso seja necessário implementar algum componente novo, será necessário, por questão de padronização, importar o mesmo no arquivo `globalComponents.js` para que fique disponível no sistema como um todo, como os demais. Ex:
``` [js]
import Vue from 'vue';
import Loader from '@/pages/components/loader';
import Alert from '@/pages/components/alert';
import Dialog from '@/pages/components/dialog';
import Logo from '@/pages/components/logotype';
Vue.component(Loader.name, Loader);
Vue.component(Alert.name, Alert);
Vue.component(Dialog.name, Dialog);
Vue.component(Logo.name, Logo);
```
## Uso das props globais
**Prop de loader:**
``` [vuejs]
<script>
export default {
methods: {
sendMessage() {
this.$loader.init(); // inicia o loader
this.$store.dispatch('message')
.then(() => {
this.$loader.stop(); // finaliza o loader
})
}
}
}
</script>
```
Neste exemplo ficticio, vemos como podemos ligar e desligar o loader da página. temos nossa variável global `$loader` que nos permite iniciar `init()` ou finalizar `stop()` o loader da página.
**Prop de helper:**
[Como falamos anteriormente](#Helpers-customizaveis-do-sistema), nossos helpers criados para auxiliar nosso dia a dia, estarão disponíveis a partir da prop `$helper` no sistema inteiro. Com ela vamos poder utilizar nossas funções gerais e nossas regras de validação no sistema como um todo, por exemplo:
``` [vuejs]
<template>
<v-text-field
v-model="token"
:rules="[ rules.equals_length(token, 6), rules.required ]"
></v-text-field>
</template>
<script>
export default {
data() {
return {
rules: this.$helper.validation.rules
}
},
...
}
</script>
```
Neste exemplo, estamos utilizando nosso helper para nos trazer as validações genericas de nosso sistema, dessa forma podemos utilizar no `v-text-field` como regra de validação de formulário.
## Padronização no backend do admin
No Controller base do nosso admin `src/core/Controller.php`, criamos o método para renderizar as páginas em Vue:
``` [php]
function RenderVue($vueRoute)
{
$this->vueRoute = "#{$vueRoute}";
View::Render("Page/VueIframe", true);
}
```
O parâmetro `$vueRoute` receberá o path da componente à ser renderisado.
Exemplo:
`produtos/index`
Agora vamos até o diretório `Page` das nossas views `src/app/views/Page` onde criamos o arquivo `.phtml` que irá renderizar o vue pelo iframe:
> VueIframe.phtml
``` [phtml]
<?
// Identifica automaticamente se o hot reloading est ativo e
// define a URL de acordo.
if (getenv('AMBIENTE') === 'LOCAL' && @fsockopen('localhost', 8080)) {
$vueUrl = 'http://localhost:8080';
} else {
$vueUrl = '/web/vue/';
}
$vueUrl .= $this->vueRoute;
?>
<iframe src="<?= $vueUrl ?>" frameborder="0" style="width: 100%; height: 100%;" />
```
A condição para definir a url serve para caso precisemos utilizar o `hot reloading` do vue para desenvolvimento pelo comando `npm run serve`.
Para isso precisaremos também liberamos a porta 8080 no docker e liberamos o acesso no arquivo `src/index.php`:
> src/index.php
> Adicionamos as linhas abaixo:
``` [php]
// Necessário pra usar hot-reload do Vue localmente
if (getenv('AMBIENTE') == 'LOCAL') {
header('Access-Control-Allow-Origin: http://localhost:8080');
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Allow-Headers: cache-control, content-type');
}
```
> docker-compose.yml
> Adicionamos a linha `- "8080:8080"` nas portas `ports`
``` [yml]
version: "2"
services:
app:
image: registry.i9xp.com.br:8082/docker/php-apache:${DOCKER_TAG}
container_name: projeto-admin
volumes:
- .:/var/www/html/app
env_file:
- .env
ports:
- "8081:80"
- "30204:443"
- "8080:8080"
```
e pronto!
## Funcionamento do hot reload em ambiente de desenvolvimento
Para utilizar o hot reload para trabalhar em ambiente de desenvolvimento local, é preciso acessar a pasta do admin e rodar o seguinte comando: `npm run dev`.
Porém, é importante que antes disso seu docker tenha subido corretamente, na raiz de cada projeto, sempre teremos um arquivo chamado `env.sh`, atualizamos ele para que fosse possível dar build no admin ao subir os containers. Abaixo o exemplo de script atualizado:
> env.sh
``` [sh]
#!/bin/bash
#read funcao
funcao=$1
diretorios=("$(pwd)/admin" "$(pwd)/site")
min=0
max=$(( ${#diretorios[@]} -1 ))
clear
echo "*** Inicio ***"
case "$funcao" in
down)
echo
while [[ max -ge min ]]
do
bold="\033[1m${diretorios[$max]}\033[0m";
echo -e "Finalizando containers em ${bold}"
cd "${diretorios[$max]}"
docker-compose down
(( max-- ))
echo
done
;;
up)
while [[ min -le max ]]
do
bold="\033[1m${diretorios[$min]}\033[0m";
echo -e "Iniciando containers em ${bold}"
cd "${diretorios[$min]}"
docker-compose up -d
npm run vue-install && npm run build
(( min++ ))
echo
done
;;
restart)
sh ~/env.sh down
sh ~/env.sh up
;;
*)
echo "Opção $funcao inválida. Use 'up' ou 'down'"
;;
esac
echo
echo
echo "-----------------------------------------------------------"
echo -e "-- \033[1mLISTAGEM\033[0m --"
echo "-----------------------------------------------------------"
docker ps
echo
echo "*** Fim ***"
```
Com isso, ao rodar seu comando `./env up`, além dos containers do docker subirem, o frontend em vue será renderizado e estará pronto para utilizar em modo de produção. Caso precise em modo de desenvolvimento, entre na pasta do admin e rode o comando mencionado acima, mas lembre-se de ativar as opções do [axios](#Axios) mencionados anteriormente.
## Criação de uma nova tela
Nesta seção vamos definir alguns padrões de construção das novas telas em vuejs para o admin.
Primeiramente, devemos criar o caminho para a nossa nova tela no banco de dados, na tabela `Admin_Area`. Não vamos entrar em muitos detalhes aqui, pois essa parte de criação vamos seguir o padrão já existente, tendo uma pequena diferença, o campo `Url`, deverá conter o nome do componente que você irá criar, caso contrário, isso irá ocasionar um erro. Por padrão, as nomeclaturas de paginas iremos seguir com `page-` e concatenado com isso o nome da página, sem caractere especial e o mais específico possível, por exemplo: `page-envelopamento-gerenciar`. O campo `Legado` deverá ter o valor igual a `1`, dessa forma nosso sistema entenderá que o mesmo é um componente de página e não uma tela legada do `extjs`.
Após essa etapa, devemos criar uma pasta em nosso repositório de `pages`, ex:
```
|---- pages/
|---- page-test/
|---- index.vue
```
Logo após a criação do componente, devemos declarar o mesmo em nosso arquivo de página global e definir um nome para que ele possa ser identificado com a "url" que foi definida na tabela `Admin_Area`, ex:
> globalPages.js
``` [vuejs]
import Vue from 'vue';
/*
* Aqui definimos nossos componentes de telas
*/
Vue.component('page-component-test', () => import('@/pages/page-test'));
```
Para visualizar sua tela nova, basta atribuir a mesma ao seu grupo de usuario e abrir no admin.