Refactor address vue app and create a PickAddressType

This commit is contained in:
Julien Fastré 2021-10-13 22:58:54 +02:00
parent 01ff88074b
commit 13b96637bb
13 changed files with 282 additions and 110 deletions

View File

@ -109,3 +109,13 @@
{# The choice_with_other_widget widget is defined in the main bundle #}
{% block pick_address_row %}
{{ form_label(form) }}
{{ form_errors(form) }}
{{ form_widget(form) }}
{% endblock %}
{% block pick_address_widget %}
{{ form_widget(form) }}
<div data-input-address-container="{{ form.vars.uniqid }}"></div>
{% endblock %}

View File

@ -0,0 +1,45 @@
<?php
namespace Chill\MainBundle\Form\Type\DataTransformer;
use Chill\MainBundle\Repository\AddressRepository;
use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\Exception\TransformationFailedException;
final class AddressToIdDataTransformer implements DataTransformerInterface
{
private AddressRepository $addressRepository;
public function __construct(AddressRepository $addressRepository)
{
$this->addressRepository = $addressRepository;
}
public function reverseTransform($value)
{
if (NULL === $value || '' === $value) {
return null;
}
$address = $this->addressRepository->find($value);
if (NULL === $address) {
$failure = new TransformationFailedException(sprintf("Address with id %s does not exists", $value));
$failure
->setInvalidMessage("The given {{ value }} is not a valid address id", [ '{{ value }}' => $value]);
throw $failure;
}
return $address;
}
public function transform($value)
{
if (NULL === $value) {
return '';
}
return $value->getId();
}
}

View File

@ -0,0 +1,52 @@
<?php
namespace Chill\MainBundle\Form\Type;
use Chill\MainBundle\Entity\Address;
use Chill\MainBundle\Form\Type\DataTransformer\AddressToIdDataTransformer;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\OptionsResolver\OptionsResolver;
final class PickAddressType extends AbstractType
{
private AddressToIdDataTransformer $addressToIdDataTransformer;
public function __construct(AddressToIdDataTransformer $addressToIdDataTransformer)
{
$this->addressToIdDataTransformer = $addressToIdDataTransformer;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->addModelTransformer($this->addressToIdDataTransformer);
}
public function buildView(FormView $view, FormInterface $form, array $options)
{
$view->vars['uniqid'] = $view->vars['attr']['data-input-address'] =\uniqid('input_address_');
$view->vars['attr']['data-use-valid-from'] = $options['useValidFrom'];
$view->vars['attr']['data-use-valid-to'] = $options['useValidTo'];
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'class' => Address::class,
'useValidFrom' => false,
'useValidTo' => false,
// reset default from hidden type
'required' => true,
'error_bubbling' => false,
]);
}
public function getParent()
{
return HiddenType::class;
}
}

View File

@ -17,7 +17,8 @@ export default {
components: {
AddAddress
},
props: ['addAddress'],
props: ['addAddress', 'callback'],
emits: ['addressEdited', 'addressCreated'],
computed: {
context() {
return this.addAddress.context;
@ -46,6 +47,7 @@ export default {
// address is already linked, just finish !
this.$refs.addAddress.afterLastPaneAction({});
this.$emit('addressEdited', payload);
// New created address
} else {
@ -57,6 +59,8 @@ export default {
* Post new created address to targetEntity
*/
postAddressTo(payload) {
this.$emit('addressCreated', payload);
console.log('postAddress', payload.addressId, 'To', payload.target, payload.targetId);
switch (payload.target) {
case 'household':

View File

@ -0,0 +1,87 @@
import {createApp} from 'vue';
import {_createI18n} from 'ChillMainAssets/vuejs/_js/i18n';
import {addressMessages} from './i18n';
import App from './App.vue';
const i18n = _createI18n(addressMessages);
let inputs = document.querySelectorAll('input[type="hidden"][data-input-address]');
const isNumeric = function(v) { return !isNaN(v); };
inputs.forEach(el => {
let
addressId = el.value,
uniqid = el.dataset.inputAddress,
container = document.querySelector('div[data-input-address-container="' + uniqid + '"]'),
currentTarget = el,
i = 0,
isEdit = addressId !== '',
addressIdInt = addressId !== '' ? parseInt(addressId) : null
;
if (container === null) {
throw Error("no container");
}
const app = createApp({
template: `<app v-bind:addAddress="this.addAddress" @address-created="associateToInput"></app>`,
data() {
return {
addAddress: {
context: {
// for legacy ? can be remove ?
target: {
name: 'input-address',
id: addressIdInt,
},
edit: isEdit,
addressId: addressIdInt,
},
options: {
/// Options override default.
/// null value take default component value defined in AddAddress data()
button: {
text: {
create: null,
edit: null,
},
size: null,
displayText: false
},
/// Modal title text if create or edit address (trans chain, see i18n)
title: {
create: null,
edit: null,
},
/// Display panes in Modal for step123
openPanesInModal: true,
/// Display actions buttons of panes in a sticky-form-button navbar
stickyActions: false,
/// Use Date fields
useDate: {
validFrom: el.dataset.useValidFrom === 'true', //boolean, default: false
validTo: el.dataset.useValidTo === 'true' //boolean, default: false
},
/// Don't display show renderbox Address: showPane display only a button
onlyButton: false,
}
}
}
},
methods: {
associateToInput(payload) {
console.log(payload);
el.value = payload.addressId;
}
}
})
.use(i18n)
.component('app', App)
.mount(container);
});

View File

@ -60,6 +60,7 @@ module.exports = function(encore, entries)
encore.addEntry('mod_ckeditor5', __dirname + '/Resources/public/module/ckeditor5/index.js');
encore.addEntry('mod_disablebuttons', __dirname + '/Resources/public/module/disable-buttons/index.js');
encore.addEntry('mod_input_address', __dirname + '/Resources/public/vuejs/Address/mod_input_address_index.js');
// Vue entrypoints
encore.addEntry('vue_address', __dirname + '/Resources/public/vuejs/Address/index.js');
encore.addEntry('vue_onthefly', __dirname + '/Resources/public/vuejs/OnTheFly/index.js');

View File

@ -1,4 +1,5 @@
services:
chill.main.form.type.translatable.string:
class: Chill\MainBundle\Form\Type\TranslatableStringFormType
arguments:
@ -128,3 +129,11 @@ services:
tags:
- { name: form.type }
Chill\MainBundle\Form\Type\PickAddressType:
autoconfigure: true
autowire: true
Chill\MainBundle\Form\DataTransform\AddressToIdDataTransformer:
autoconfigure: true
autowire: true

View File

@ -550,9 +550,9 @@ class ThirdParty implements TrackCreationInterface, TrackUpdateInterface
* @param string $acronym
* @return $this
*/
public function setAcronym(string $acronym): ThirdParty
public function setAcronym(?string $acronym = null): ThirdParty
{
$this->acronym = $acronym;
$this->acronym = (string) $acronym;
return $this;
}

View File

@ -5,6 +5,7 @@ namespace Chill\ThirdPartyBundle\Form;
use Chill\MainBundle\Entity\Address;
use Chill\MainBundle\Entity\Civility;
use Chill\MainBundle\Form\Type\ChillCollectionType;
use Chill\MainBundle\Form\Type\PickAddressType;
use Chill\MainBundle\Form\Type\PickCenterType;
use Chill\MainBundle\Form\Type\ChillTextareaType;
use Chill\MainBundle\Templating\TranslatableStringHelper;
@ -88,7 +89,9 @@ class ThirdPartyType extends AbstractType
])
;
/*
$builder
/*
->add('address', HiddenType::class)
->get('address')
->addModelTransformer(new CallbackTransformer(
@ -106,8 +109,7 @@ class ThirdPartyType extends AbstractType
->getRepository(Address::class)
->findOneBy(['id' => (int) $addressId]);
}
))
;
))*/
// Contact Person ThirdParty (child)
if (ThirdParty::KIND_CONTACT === $options['kind'] || ThirdParty::KIND_CHILD === $options['kind']) {
@ -147,6 +149,9 @@ class ThirdPartyType extends AbstractType
// Institutional ThirdParty (parent)
} else {
$builder
->add('address', PickAddressType::class, [
'label' => 'Address'
])
->add('nameCompany', TextType::class, [
'label' => 'thirdparty.NameCompany',
'required' => false

View File

@ -20,11 +20,11 @@
</div>
<div v-else-if="action === 'edit' || action === 'create'">
<div class="form-floating mb-3">
<div class="form-floating mb-3" v-if="thirdparty.kind !== 'child'">
<div class="form-check">
<input class="form-check-input mt-0" type="radio" v-model="kind" value="company" id="tpartyKindInstitution">
<label for="tpartyKindInstitution" class="required">
<span class="badge bg-thirdparty-company">
<span class="badge bg-thirdparty-company" style="padding-top: 0;">
{{ $t('tparty.company')}}
</span>
</label>
@ -32,17 +32,35 @@
<div class="form-check">
<input class="form-check-input mt-0" type="radio" v-model="kind" value="contact" id="tpartyKindContact">
<label for="tpartyKindContact" class="required">
<span class="badge bg-thirdparty-contact">
<span class="badge bg-thirdparty-contact" style="padding-top: 0;">
{{ $t('tparty.contact')}}
</span>
</label>
</div>
</div>
<div v-else>
<p>Contact de&nbsp;:</p>
<third-party-render-box :thirdparty="thirdparty.parent"
:options="{
addInfo: true,
addEntity: false,
addAltNames: true,
addId: false,
addLink: false,
addAge: false,
hLevel: 4,
addCenter: false,
addNoData: true,
isMultiline: false
}"></third-party-render-box>
</div>
<div class="form-floating mb-3">
<input class="form-control form-control-lg" id="name" v-model="thirdparty.text" v-bind:placeholder="$t('thirdparty.name')" />
<label for="name">{{ $t('thirdparty.name') }}</label>
</div>
<template
v-if="thirdparty.kind !== 'child'">
<add-address
key="thirdparty"
:context="context"
@ -50,6 +68,7 @@
:address-changed-callback="submitAddress"
ref="addAddress">
</add-address>
</template>
<div class="input-group mb-3">
<span class="input-group-text" id="email"><i class="fa fa-fw fa-envelope"></i></span>
@ -140,7 +159,7 @@ export default {
edit: false,
addressId: null
};
if ( typeof this.thirdparty.address !== 'undefined'
if ( !(this.thirdparty.address === undefined || this.thirdparty.address === null)
&& this.thirdparty.address.address_id !== null
) { // to complete
context.addressId = this.thirdparty.address.address_id;
@ -155,11 +174,14 @@ export default {
loadData(){
getThirdparty(this.id).then(thirdparty => new Promise((resolve, reject) => {
this.thirdparty = thirdparty;
this.thirdparty.kind = thirdparty.kind;
console.log('get thirdparty', thirdparty);
if (this.action !== 'show') {
if (thirdparty.address !== null) {
// bof! we force getInitialAddress because addressId not available when mounted
this.$refs.addAddress.getInitialAddress(thirdparty.address.address_id);
}
}
resolve();
}));
},
@ -174,6 +196,7 @@ export default {
}
},
mounted() {
console.log('mounted', this.action);
if (this.action !== 'create') {
this.loadData();
} else {

View File

@ -28,12 +28,16 @@
{{ form_widget(form.activeChildren) }}
{% endif %}
{{ form_row(form.address) }}
{#
<div class="mb-3 row">
{{ form_label(form.address) }}
{{ form_widget(form.address) }}
<div class="col-sm-8">
{% if thirdParty.address %}
{# include vue_address component #}
{# include vue_address component #
{% include '@ChillMain/Address/_insert_vue_address.html.twig' with {
targetEntity: { name: 'thirdparty', id: thirdParty.id },
mode: 'edit',
@ -42,9 +46,9 @@
} %}
{#
backUrl: path('chill_3party_3party_new'),
#}
#
{% else %}
{# include vue_address component #}
{# include vue_address component #
{% include '@ChillMain/Address/_insert_vue_address.html.twig' with {
targetEntity: { name: 'thirdparty', id: thirdParty.id },
mode: 'new',
@ -55,6 +59,7 @@
{% endif %}
</div>
</div>
#}
{{ form_row(form.comment) }}
{{ form_row(form.centers) }}

View File

@ -19,3 +19,11 @@
</div>
</div>
{% endblock %}
{% block js %}
{{ encore_entry_script_tags('mod_input_address') }}
{% endblock %}
{% block css %}
{{ encore_entry_link_tags('mod_input_address') }}
{% endblock %}

View File

@ -36,87 +36,10 @@
</div>
{% endblock %}
{% block content_not %}
<div class="thirdparty-edit my-5">
<div class="row justify-content-center">
<div class="col-md-10">
{{ form_start(form) }}
{% if form.civility is defined %}
{{ form_row(form.civility) }}
{% endif %}
{{ form_row(form.name) }}
{% if form.nameCompany is defined %}
{{ form_row(form.nameCompany) }}
{{ form_row(form.acronym) }}
{% endif %}
{% if form.profession is defined %}
{{ form_row(form.profession) }}
{% endif %}
{{ form_row(form.types) }}
{{ form_row(form.categories) }}
{{ form_row(form.telephone) }}
{{ form_row(form.email) }}
<div class="mb-3 row">
{{ form_label(form.address) }}
{{ form_widget(form.address) }}
<div class="col-sm-8">
{% if thirdParty.address %}
{# include vue_address component #}
{% include '@ChillMain/Address/_insert_vue_address.html.twig' with {
targetEntity: { name: 'thirdparty', id: thirdParty.id },
mode: 'edit',
addressId: thirdParty.address.id,
buttonSize: 'btn-sm',
} %}
{#
backUrl: path('chill_3party_3party_update', { thirdparty_id: thirdParty.id }),
#}
{% else %}
{# include vue_address component #}
{% include '@ChillMain/Address/_insert_vue_address.html.twig' with {
targetEntity: { name: 'thirdparty', id: thirdParty.id },
mode: 'new',
buttonSize: 'btn-sm',
buttonText: 'Create a new address',
modalTitle: 'Create a new address',
} %}
{% endif %}
</div>
</div>
{{ form_row(form.comment) }}
{{ form_row(form.centers) }}
{{ form_row(form.active) }}
<ul class="record_actions sticky-form-buttons">
<li class="cancel">
<a class="btn btn-cancel" href="{{ chill_path_forward_return_path('chill_3party_3party_index') }}">
{{ 'Back to the list'|trans }}
</a>
</li>
<li>
</li>
<li>
{{ form_widget(form.submit, {'label': 'Update', 'attr': {'class': 'btn btn-update' }}) }}
</li>
</ul>
{{ form_end(form) }}
</div>
</div>
</div>
{% block js %}
{{ encore_entry_script_tags('mod_input_address') }}
{% endblock %}
{% block css %}
{{ encore_entry_link_tags('mod_input_address') }}
{% endblock %}