Created
June 4, 2018 20:40
-
-
Save iErik/2863f6b1952c75a85ff1404b4d1f733e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <template> | |
| <c-card | |
| :title="accountName ? `Exclusivo de ${accountName}` : ''"> | |
| <c-toggle | |
| class="input" | |
| v-if="!data.id" | |
| :labels="{ checked: 'Ativa', unchecked: 'Inativa'}" | |
| :value="data.status" | |
| height="30" | |
| width="80" | |
| sync | |
| @input="v => data.status = v" | |
| slot="action" /> | |
| <c-form | |
| autocomplete="new-password" | |
| id="perk" | |
| class="new-perk-form" | |
| @submit.prevent> | |
| <c-title size="3">Informações</c-title> | |
| <div class="half -left"> | |
| <!-- name required --> | |
| <c-input | |
| required | |
| name="nome" | |
| v-model="data.name" | |
| v-validate="'required'" | |
| label="Nome do parceiro" | |
| :feedback-show="errors.has('nome')" | |
| :feedback-message="errors.first('nome')" /> | |
| <!-- colors --> | |
| <c-input | |
| type="color" | |
| name="cor" | |
| required | |
| v-model="data.colors.primary" | |
| v-validate="'required'" | |
| :feedback-show="errors.has('cor')" | |
| :feedback-message="errors.first('cor')" | |
| label="Cor" /> | |
| <!-- logo --> | |
| <div class="inline"> | |
| <c-input | |
| type="file" | |
| file-accept="image/*" | |
| :file-label="computedFileLabel" | |
| v-model="data.logo" | |
| @change.native="e => previewLogo(e, 'logoSrc')" | |
| label="Logo (5:4, mín. de 100:80)" | |
| /> | |
| <c-button | |
| :disabled="!logoSrc" | |
| :popover-label="`<img style='height: 100%; width: 100px; object-fit: contain' src='${logoSrc}' />`" | |
| alternative | |
| type="button" | |
| size="lg" | |
| icon="file-preview"> | |
| Preview | |
| </c-button> | |
| </div> | |
| <!-- logo --> | |
| <div class="inline"> | |
| <c-input | |
| type="file" | |
| file-accept="image/png" | |
| :file-label="computedNegativeFileLabel" | |
| v-model="data.logo_negative" | |
| @change.native="e => previewLogo(e, 'negativeLogoSrc')" | |
| label="Logo negativo (5:4, mín. de 100:80, PNG transparente)" | |
| /> | |
| <c-button | |
| :disabled="!negativeLogoSrc" | |
| :popover-label="` | |
| <div style='background-color: ${data.colors.primary}'> | |
| <img | |
| style=' | |
| background-color: transparent; | |
| height: 100%; width: 100px; | |
| object-fit: contain;' | |
| src='${negativeLogoSrc}' | |
| /> | |
| </div> | |
| `" | |
| alternative | |
| type="button" | |
| size="lg" | |
| icon="file-preview"> | |
| Preview | |
| </c-button> | |
| </div> | |
| <c-input | |
| name="cnpj" | |
| v-model="data.cnpj" | |
| :value="data.cnpj" | |
| @input="cnpj => data.cnpj = cnpj.replace(/\D/g, '')" | |
| :mask="['##.###.###/####-##']" | |
| label="CNPJ" | |
| /> | |
| </div> | |
| <div class="half"> | |
| <!-- is_main --> | |
| <c-toggle | |
| form-label="Principal" | |
| :labels="{ checked: 'É principal', unchecked: 'Não é principal'}" | |
| :value="!!data.is_main" | |
| @input="v => data.is_main = +v" | |
| width="140" /> | |
| <!-- description required --> | |
| <c-input | |
| text-area | |
| maxlength="200" | |
| required | |
| v-model="data.description" | |
| name="sobre" | |
| v-validate="'required|max:200'" | |
| :feedback-show="errors.has('sobre')" | |
| :feedback-message="errors.first('sobre')" | |
| :label="`Sobre o parceiro (${computedDescriptionLength})`" /> | |
| <!-- observation --> | |
| <c-input | |
| text-area | |
| maxlength="140" | |
| name="observacoes" | |
| v-validate="'max:140'" | |
| rows="4" | |
| :feedback-show="errors.has('observacoes')" | |
| :feedback-message="errors.first('observacoes')" | |
| v-model="data.observation" | |
| :label="`Observações (${computedObservationLength})`" /> | |
| <!-- categories (Array de ids) --> | |
| <c-select | |
| :options="availableCategories" | |
| :multiple="true" | |
| :disabled="isFetchingCategories" | |
| label="label" | |
| track-by="value" | |
| required | |
| :feedback-show="!data.categories.length" | |
| :feedback-message="'O campo categorias é obrigatório.'" | |
| :value="computedCategories" | |
| @input="v => updateCategories(v)" | |
| form-label="Categorias" /> | |
| </div> | |
| <c-title size="3">Contato</c-title> | |
| <div class="half -left"> | |
| <!-- contact.name --> | |
| <c-input | |
| label="Nome" | |
| required | |
| name="nomeContato" | |
| v-validate="'required'" | |
| :feedback-show="errors.has('nomeContato')" | |
| :feedback-message="errors.first('nomeContato')" | |
| v-model="data.contact.name" /> | |
| <!-- contact.phone --> | |
| <c-input | |
| label="Telefone" | |
| placeholder="(DDD) + Número" | |
| :mask="['(##) ####-####', '(##) ####-#####']" | |
| v-model="data.contact.phone" /> | |
| <!-- contact.email --> | |
| <c-input | |
| label="Email" | |
| type="email" | |
| v-validate="'email'" | |
| name="emailContato" | |
| :feedback-show="errors.has('emailContato')" | |
| :feedback-message="errors.first('emailContato')" | |
| v-model="data.contact.email" /> | |
| </div> | |
| <!-- contract --> | |
| <div class="half"> | |
| <c-input | |
| type="file" | |
| v-model="data.contract" | |
| file-label="Escolher arquivo" | |
| label="Importar contrato" /> | |
| </div> | |
| </c-form> | |
| <c-form @submit.prevent class="new-perk-form"> | |
| <!-- address (Array de objetos) --> | |
| <c-title size="3">Endereço</c-title> | |
| <section class="manual-address"> | |
| <input type="hidden" :value="newAddress.id" /> | |
| <!-- code required --> | |
| <c-input | |
| label="CEP" | |
| :mask="['#####-###']" | |
| type="text" | |
| name="cep" | |
| @keyup="fillAddress" | |
| v-validate="newAddress.code ? 'required' : ''" | |
| :required="!!newAddress.code" | |
| :feedback-show="errors.has('cep')" | |
| :feedback-message="errors.first('cep')" | |
| v-model="newAddress.code" /> | |
| <div class="inline"> | |
| <!-- street required --> | |
| <c-input | |
| label="Endereço" | |
| type="text" | |
| name="endereço" | |
| v-validate="newAddress.code ? 'required' : ''" | |
| :required="!!newAddress.code" | |
| :feedback-show="errors.has('endereço')" | |
| :feedback-message="errors.first('endereço')" | |
| v-model="newAddress.street" /> | |
| <!-- number required --> | |
| <c-input | |
| label="Número" | |
| type="text" | |
| name="numero" | |
| v-validate="newAddress.code ? 'required' : ''" | |
| :required="!!newAddress.code" | |
| :feedback-show="errors.has('numero')" | |
| :feedback-message="errors.first('numero')" | |
| v-model="newAddress.number" /> | |
| </div> | |
| <div class="inline"> | |
| <!-- complement --> | |
| <c-input | |
| label="Complemento" | |
| type="text" | |
| v-model="newAddress.complement" /> | |
| <!-- lat --> | |
| <c-input | |
| label="Latitude" | |
| type="text" | |
| v-model="newAddress.latitude" /> | |
| <!-- long --> | |
| <c-input | |
| label="Longitude" | |
| type="text" | |
| v-model="newAddress.longitude" /> | |
| </div> | |
| <!-- neighborhood required --> | |
| <c-input | |
| label="Bairro" | |
| type="text" | |
| name="bairro" | |
| v-validate="newAddress.code ? 'required' : ''" | |
| :required="!!newAddress.code" | |
| :feedback-show="errors.has('bairro')" | |
| :feedback-message="errors.first('bairro')" | |
| v-model="newAddress.neighborhood" /> | |
| <div class="inline"> | |
| <!-- state required --> | |
| <c-input | |
| label="Estado" | |
| type="text" | |
| name="estado" | |
| v-validate="newAddress.code ? 'required' : ''" | |
| :required="!!newAddress.code" | |
| :feedback-show="errors.has('estado')" | |
| :feedback-message="errors.first('estado')" | |
| v-model="newAddress.state" /> | |
| <!-- city --> | |
| <c-input | |
| label="Cidade" | |
| type="text" | |
| name="cidade" | |
| v-validate="newAddress.code ? 'required' : ''" | |
| :required="!!newAddress.code" | |
| :feedback-show="errors.has('cidade')" | |
| :feedback-message="errors.first('cidade')" | |
| v-model="newAddress.city" /> | |
| </div> | |
| <c-button | |
| icon="plus" | |
| :disabled="isNewAddressInvalid" | |
| @click="saveAddress"> | |
| <template v-if="data.address.length">Adicionar à lista ({{data.address.length}})</template> | |
| <template v-else>Adicionar outro</template> | |
| </c-button> | |
| </section> | |
| <c-table v-if="data.address.length" | |
| @select="v => selectedAddress = v" | |
| :cols="addressCols" | |
| :rows="data.address"> | |
| <template | |
| slot="row" | |
| slot-scope="{ row, index }"> | |
| <td class="c-table-cell -content c-table-text">{{ row.street }}</td> | |
| <td class="c-table-cell -content c-table-text">{{ row.number }}</td> | |
| <td class="c-table-cell -content c-table-text">{{ row.complement }}</td> | |
| <td class="c-table-cell -content c-table-text">{{ row.neighborhood }}</td> | |
| <td class="c-table-cell -content c-table-text">{{ row.state }}</td> | |
| <td class="c-table-cell -content c-table-text">{{ row.city }}</td> | |
| <td class="c-table-cell -content c-table-text"> | |
| <c-button alternative size="lg" icon="pencil" | |
| @click="editAddress(row, index)" | |
| popover-label="Editar" /> | |
| <c-button alternative size="lg" icon="trash" | |
| @click="removeAddress(index)" | |
| popover-label="Apagar" /> | |
| </td> | |
| </template> | |
| </c-table> | |
| <c-button | |
| alternative | |
| @click="$router.push({ name: 'Parceiro', params: $route.params })" | |
| formname="perk" | |
| slot="actions" | |
| type="reset"> | |
| Cancelar | |
| </c-button> | |
| <c-button | |
| slot="actions" | |
| :disabled="!data.categories.length || isProcessing || isFormInvalid" | |
| @click="emitSubmit" | |
| formname="perk" | |
| icon="floppy-disk" | |
| type="button"> | |
| Salvar | |
| </c-button> | |
| </c-form> | |
| </c-card> | |
| </template> | |
| <script> | |
| import { validate } from 'vue-convenia-util' | |
| import * as types from '@store/types' | |
| import cep from 'cep-promise' | |
| const ADDRESS_TEMPLATE = { | |
| id: '', | |
| code: '', | |
| street: '', | |
| number: '', | |
| complement: '', | |
| latitude: '', | |
| longitude: '', | |
| neighborhood: '', | |
| state: '', | |
| city: '' | |
| } | |
| const PERK_TEMPLATE = { | |
| id: null, | |
| name: '', | |
| is_main: false, | |
| logo: '', | |
| cnpj: '', | |
| logo_negative: '', | |
| description: '', | |
| categories: [], | |
| notify: false, | |
| colors: { | |
| primary: '' | |
| }, | |
| contact: { | |
| name: '', | |
| phone: '', | |
| email: '' | |
| }, | |
| address: [], | |
| status: true | |
| } | |
| const checkFields = (names) => function () { | |
| const checkField = (name) => this.fields[name] && this.fields[name].invalid | |
| const checked = names.some(checkField) | |
| return checked | |
| } | |
| export default { | |
| props: { | |
| currentPerk: { | |
| type: Object, | |
| default: () => {} | |
| }, | |
| availableCategories: { | |
| type: Array, | |
| default: () => [] | |
| }, | |
| isFetchingCategories: Boolean, | |
| isProcessing: Boolean, | |
| accountName: String | |
| }, | |
| data () { | |
| return { | |
| logoSrc: '', | |
| negativeLogoSrc: '', | |
| newAddress: { ...ADDRESS_TEMPLATE }, | |
| selectedAddress: '', | |
| addressCols: [ | |
| { label: 'Endereço' }, | |
| { label: 'Número' }, | |
| { label: 'Comp.' }, | |
| { label: 'Bairro' }, | |
| { label: 'Estado' }, | |
| { label: 'Cidade' }, | |
| { label: 'Ações' } | |
| ], | |
| data: this.currentPerk && Object.keys(this.currentPerk).length ? { | |
| ...PERK_TEMPLATE, | |
| ...this.currentPerk, | |
| colors: { | |
| ...PERK_TEMPLATE.colors, | |
| ...this.currentPerk.colors | |
| }, | |
| contact: { | |
| ...PERK_TEMPLATE.contact, | |
| ...this.currentPerk.contact | |
| }, | |
| address: [ | |
| ...PERK_TEMPLATE.address, | |
| ...this.currentPerk.address | |
| ], | |
| categories: [ | |
| ...PERK_TEMPLATE.categories, | |
| ...this.currentPerk.categories | |
| ] | |
| } : { | |
| ...PERK_TEMPLATE, | |
| colors: { ...PERK_TEMPLATE.colors }, | |
| contact: { ...PERK_TEMPLATE.contact }, | |
| address: [ ...PERK_TEMPLATE.address ], | |
| categories: [ ...PERK_TEMPLATE.categories ] | |
| } | |
| } | |
| }, | |
| computed: { | |
| computedDescriptionLength () { | |
| return 200 - (this.data.description ? this.data.description.length : 0) | |
| }, | |
| computedObservationLength () { | |
| return 140 - (this.data.observation ? this.data.observation.length : 0) | |
| }, | |
| computedCategories () { | |
| return this.data.categories.map(it => { | |
| return { | |
| label: it.name, | |
| value: it.id | |
| } | |
| }) | |
| }, | |
| computedFileLabel () { | |
| if (validate.is(this.data.logo, 'String') && this.data.logo.length > 1) { | |
| this.logoSrc = this.data.logo | |
| return 'Trocar foto' | |
| } | |
| return 'Escolher foto' | |
| }, | |
| computedNegativeFileLabel () { | |
| if (validate.is(this.data.logo_negative, 'String') && this.data.logo_negative.length > 1) { | |
| this.negativeLogoSrc = this.data.logo_negative | |
| return 'Trocar foto' | |
| } | |
| return 'Escolher foto' | |
| }, | |
| isInformationInvalid: checkFields(['nome', 'sobre', 'observacoes', 'cor']), | |
| isContactInvalid: checkFields(['nomeContato', 'emailContato']), | |
| isNewAddressInvalid () { | |
| // return this.hasAddress ? false : ['cep', 'endereço', 'numero', 'bairro', 'estado', 'cidade'].some(field => this.fields[field].invalid) | |
| return false | |
| }, | |
| isFormInvalid () { | |
| return this.isInformationInvalid || this.isContactInvalid || this.isNewAddressInvalid | |
| }, | |
| hasAddress () { | |
| return this.data.address.length >= 1 | |
| } | |
| }, | |
| methods: { | |
| updateCategories (categories) { | |
| this.data.categories = categories.map(it => { | |
| return { | |
| name: it.label, | |
| id: it.value | |
| } | |
| }) | |
| }, | |
| async emitSubmit () { | |
| const result = await this.$validator.validateAll() | |
| const action = this.$route.params.perkId ? 'update' : 'create' | |
| const isMain = this.data.is_main ? 1 : 0 | |
| const status = this.data.status ? 1 : 0 | |
| const payload = { ...this.data, is_main: isMain, status } | |
| if (action === 'update' && !(payload.logo instanceof File)) { | |
| delete payload.logo | |
| } | |
| if (action === 'update' && !(payload.logo_negative instanceof File)) { | |
| delete payload.logo_negative | |
| } | |
| payload.categories = payload.categories.map(it => it.id) | |
| if (result) { | |
| if (this.newAddress.code && !this.isNewAddressInvalid) { | |
| payload.address.push(this.newAddress) | |
| } | |
| this.$emit(action, payload) | |
| } else this.$store.dispatch(types.NOTIFICATIONS_INCLUDE, Error('Verifique os campos preenchidos.')) | |
| }, | |
| previewLogo (event, src) { | |
| const input = event.target | |
| if (input.files && input.files[0]) { | |
| const reader = new FileReader() | |
| reader.onload = e => { | |
| this[src] = e.target.result | |
| } | |
| reader.readAsDataURL(input.files[0]) | |
| } | |
| }, | |
| async fillAddress () { | |
| if (this.newAddress.code.length <= 8) return | |
| const response = await cep(this.newAddress.code.replace('-', '')) | |
| this.newAddress = { ...this.newAddress, ...response } | |
| this.$nextTick(() => { | |
| ['endereço', 'bairro', 'estado', 'cidade'].forEach(field => { | |
| this.$validator.validate(field) | |
| }) | |
| }) | |
| }, | |
| editAddress (data, index) { | |
| this.newAddress = { ...data } | |
| this.data.address.splice(index, 1) | |
| this.$nextTick(() => { | |
| ['cep', 'endereço', 'numero', 'bairro', 'estado', 'cidade'].forEach(field => { | |
| this.$validator.validate(field) | |
| }) | |
| }) | |
| }, | |
| removeAddress (index) { | |
| this.data.address.splice(index, 1) | |
| this.clearAddressValidator() | |
| }, | |
| saveAddress () { | |
| this.data.address.push({ ...this.newAddress }) | |
| this.clearAddressValidator() | |
| this.newAddress = { ...ADDRESS_TEMPLATE } | |
| }, | |
| clearAddressValidator () { | |
| ['cep', 'endereço', 'numero', 'bairro', 'estado', 'cidade'].forEach(key => { | |
| this.$validator.flag(key, { | |
| dirty: false, | |
| invalid: true, | |
| pending: false, | |
| pristine: true, | |
| required: true, | |
| touched: false, | |
| untouched: true, | |
| valid: false, | |
| validated: false | |
| }) | |
| }) | |
| } | |
| } | |
| } | |
| </script> | |
| <style lang="scss"> | |
| .new-perk-form { | |
| display: flex; | |
| flex-flow: row wrap; | |
| & > .half { | |
| flex: 0 50%; | |
| padding-left: inner-base(); | |
| &.-left { | |
| padding-left: 0; | |
| padding-right: inner-base(); | |
| } | |
| & > .c-input, | |
| & > .c-toggle, | |
| & > .c-select, | |
| & > .inline { | |
| margin-bottom: inner-base(); | |
| } | |
| & > .c-select + .c-input { | |
| position: relative; | |
| top: -2px; | |
| } | |
| @include medium-down { | |
| flex: 0 100%; | |
| padding-left: 0; | |
| &.-left { | |
| padding-right: 0; | |
| } | |
| } | |
| & > .inline { | |
| display: flex; | |
| flex-flow: row wrap; | |
| align-items: flex-end; | |
| .c-input { | |
| flex: 1; | |
| .input-label { | |
| overflow: hidden; | |
| } | |
| } | |
| .c-button { | |
| flex: 0 100px; | |
| margin-left: inner-base(); | |
| & > .c-popover { | |
| z-index: 3; | |
| } | |
| } | |
| } | |
| } | |
| & > .c-title, | |
| & > .manual-address, | |
| & > .actions { | |
| flex: 0 100%; | |
| width: 100%; | |
| } | |
| } | |
| .manual-address { | |
| & > .inline { | |
| display: flex; | |
| flex-flow: row wrap; | |
| & > .c-input { | |
| flex: 1; | |
| margin-left: inner-base(); | |
| &:first-child { margin-left: 0; } | |
| } | |
| } | |
| & > .inline, | |
| & > .c-input { | |
| margin-bottom: inner-base(); | |
| } | |
| } | |
| </style> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment