diff --git a/e2e/helpers/selectors.js b/e2e/helpers/selectors.js index 37f7308a58..0ad9ad7f40 100644 --- a/e2e/helpers/selectors.js +++ b/e2e/helpers/selectors.js @@ -1014,9 +1014,9 @@ export default { save: 'vn-travel-create vn-submit > button' }, travelExtraCommunity: { - anySearchResult: 'vn-travel-extra-community > vn-data-viewer div > vn-tbody > vn-tr', - firstTravelReference: 'vn-travel-extra-community vn-tbody:nth-child(2) vn-td-editable[name="reference"]', - firstTravelLockedKg: 'vn-travel-extra-community vn-tbody:nth-child(2) vn-td-editable[name="lockedKg"]', + anySearchResult: 'vn-travel-extra-community > vn-card div > tbody > tr[ng-attr-id="{{::travel.id}}"]', + firstTravelReference: 'vn-travel-extra-community tbody:nth-child(2) vn-textfield[ng-model="travel.ref"]', + firstTravelLockedKg: 'vn-travel-extra-community tbody:nth-child(2) vn-input-number[ng-model="travel.kg"]', removeContinentFilter: 'vn-searchbar > form > vn-textfield > div.container > div.prepend > prepend > div > span:nth-child(3) > vn-icon > i' }, travelBasicData: { diff --git a/e2e/paths/10-travel/04_extra_community.spec.js b/e2e/paths/10-travel/04_extra_community.spec.js index e4a8591924..a1cad6a7d1 100644 --- a/e2e/paths/10-travel/04_extra_community.spec.js +++ b/e2e/paths/10-travel/04_extra_community.spec.js @@ -19,18 +19,22 @@ describe('Travel extra community path', () => { it('should edit the travel reference and the locked kilograms', async() => { await page.waitToClick(selectors.travelExtraCommunity.removeContinentFilter); await page.waitForSpinnerLoad(); - await page.writeOnEditableTD(selectors.travelExtraCommunity.firstTravelReference, 'edited reference'); - await page.waitForSpinnerLoad(); - await page.writeOnEditableTD(selectors.travelExtraCommunity.firstTravelLockedKg, '1500'); + await page.clearInput(selectors.travelExtraCommunity.firstTravelReference); + await page.write(selectors.travelExtraCommunity.firstTravelReference, 'edited reference'); + await page.clearInput(selectors.travelExtraCommunity.firstTravelLockedKg); + await page.write(selectors.travelExtraCommunity.firstTravelLockedKg, '1500'); + const message = await page.waitForSnackbar(); + + expect(message.text).toContain('Data saved!'); }); it('should reload the index and confirm the reference and locked kg were edited', async() => { await page.accessToSection('travel.index'); await page.accessToSection('travel.extraCommunity'); await page.waitToClick(selectors.travelExtraCommunity.removeContinentFilter); - await page.waitForTextInElement(selectors.travelExtraCommunity.firstTravelReference, 'edited reference'); - const reference = await page.getProperty(selectors.travelExtraCommunity.firstTravelReference, 'innerText'); - const lockedKg = await page.getProperty(selectors.travelExtraCommunity.firstTravelLockedKg, 'innerText'); + + const reference = await page.waitToGetProperty(selectors.travelExtraCommunity.firstTravelReference, 'value'); + const lockedKg = await page.waitToGetProperty(selectors.travelExtraCommunity.firstTravelLockedKg, 'value'); expect(reference).toContain('edited reference'); expect(lockedKg).toContain(1500); diff --git a/modules/travel/back/methods/travel/extraCommunityFilter.js b/modules/travel/back/methods/travel/extraCommunityFilter.js index 7769b7f21e..feb16d0522 100644 --- a/modules/travel/back/methods/travel/extraCommunityFilter.js +++ b/modules/travel/back/methods/travel/extraCommunityFilter.js @@ -128,7 +128,7 @@ module.exports = Self => { w.name AS warehouseInFk, w.name AS warehouseInName, SUM(b.stickers) AS stickers, - s.id AS supplierFk, + s.id AS cargoSupplierFk, s.nickname AS cargoSupplierNickname, CAST(SUM(i.density * b.stickers * IF(pkg.volume, pkg.volume, pkg.width * pkg.depth * pkg.height) / 1000000 ) as DECIMAL(10,0)) as loadedKg, CAST(SUM(167.5 * b.stickers * IF(pkg.volume, pkg.volume, pkg.width * pkg.depth * pkg.height) / 1000000 ) as DECIMAL(10,0)) as volumeKg @@ -160,13 +160,14 @@ module.exports = Self => { e.travelFk, e.ref, e.loadPriority, + s.id AS supplierFk, s.name AS supplierName, SUM(b.stickers) AS stickers, e.evaNotes, e.notes, CAST(SUM(i.density * b.stickers * IF(pkg.volume, pkg.volume, pkg.width * pkg.depth * pkg.height) / 1000000 ) as DECIMAL(10,0)) as loadedkg, CAST(SUM(167.5 * b.stickers * IF(pkg.volume, pkg.volume, pkg.width * pkg.depth * pkg.height) / 1000000 ) as DECIMAL(10,0)) as volumeKg - FROM tmp.travel tr + FROM tmp.travel tr JOIN entry e ON e.travelFk = tr.id JOIN buy b ON b.entryFk = e.id JOIN packaging pkg ON pkg.id = b.packageFk diff --git a/modules/travel/front/extra-community-search-panel/index.html b/modules/travel/front/extra-community-search-panel/index.html index ab1e88891b..09fbb89495 100644 --- a/modules/travel/front/extra-community-search-panel/index.html +++ b/modules/travel/front/extra-community-search-panel/index.html @@ -50,15 +50,15 @@ </vn-horizontal> <vn-horizontal> <vn-autocomplete vn-one - label="Warehouse In" - ng-model="filter.warehouseInFk" + label="Warehouse Out" + ng-model="filter.warehouseOutFk" url="Warehouses" show-field="name" value-field="id"> </vn-autocomplete> <vn-autocomplete vn-one - label="Warehouse Out" - ng-model="filter.warehouseOutFk" + label="Warehouse In" + ng-model="filter.warehouseInFk" url="Warehouses" show-field="name" value-field="id"> @@ -84,4 +84,4 @@ <vn-submit label="Search"></vn-submit> </vn-horizontal> </form> -</div> \ No newline at end of file +</div> diff --git a/modules/travel/front/extra-community/index.html b/modules/travel/front/extra-community/index.html index 0b94c6c1e6..e685237506 100644 --- a/modules/travel/front/extra-community/index.html +++ b/modules/travel/front/extra-community/index.html @@ -1,6 +1,7 @@ <vn-crud-model vn-id="model" url="Travels/extraCommunityFilter" + filter="::$ctrl.filter" data="travels" order="shipped ASC, landed ASC, travelFk, loadPriority, agencyModeFk, evaNotes" limit="20" @@ -18,112 +19,165 @@ model="model"> </vn-searchbar> </vn-portal> -<vn-data-viewer model="model" class="travel-list"> - <vn-card> - <section class="vn-pa-md"> - <vn-tool-bar class="vn-mb-md"> - <vn-button disabled="!$ctrl.hasDateRange" - icon="picture_as_pdf" - ng-click="$ctrl.showReport()" - vn-tooltip="Open as PDF"> - </vn-button> - </vn-tool-bar> - <vn-table> - <vn-thead> - <vn-tr> - <vn-th shrink>Id</vn-th> - <vn-th expand>Supplier</vn-th> - <vn-th expand>Freighter</vn-th> - <vn-th>Reference</vn-th> - <vn-th number>Packages</vn-th> - <vn-th number>Bl. KG</vn-th> - <vn-th number>Phy. KG</vn-th> - <vn-th number>Vol. KG</vn-th> - <vn-th expand translate-attr="{title: 'Warehouse Out'}"> - Wh. Out - </vn-th> - <vn-th expand>W. Shipped</vn-th> - <vn-th expand translate-attr="{title: 'Warehouse In'}"> - Wh. In - </vn-th> - <vn-th expand>W. Landed</vn-th> - </vn-tr> - </vn-thead> - <vn-tbody ng-repeat="travel in travels" class="vn-mb-md" vn-droppable="$ctrl.onDrop($event)" ng-attr-id="{{::travel.id}}"> - <vn-tr class="header"> - <vn-td> - <span class="link" +<vn-card class="travel-list scrollable"> + <smart-table + model="model" + options="$ctrl.smartTableOptions"> + <slot-actions> + <section> + <vn-tool-bar class="vn-mb-md"> + <vn-button + disabled="!$ctrl.hasDateRange || $ctrl.$.model.userFilter" + icon="picture_as_pdf" + ng-click="$ctrl.showReport()" + vn-tooltip="Open as PDF"> + </vn-button> + </vn-tool-bar> + </section> + </slot-actions> + <slot-table> + <table> + <thead> + <tr> + <th field="id" shrink> + <span translate>Id {{$ctrl.dragClientY}}</span> + </th> + <th field="cargoSupplierFk" expand> + <span translate>Supplier</span> + </th> + <th field="agencyModeFk" expand> + <span translate>Freighter</span> + </th> + <th field="ref"> + <span translate>Reference</span> + </th> + <th field="stickers" number> + <span translate>Packages</span> + </th> + <th field="kg" number> + <span translate>Bl. KG</span> + </th> + <th field="loadedKg" number> + <span translate>Phy. KG</span> + </th> + <th field="volumeKg" number> + <span translate>Vol. KG</span> + </th> + <th + field="warehouseOutFk" + translate-attr="{title: 'Warehouse Out'}"> + <span translate>Wh. Out</span> + </th> + <th field="shipped"> + <span translate>W. Shipped</span> + </th> + <th + field="warehouseInFk" + translate-attr="{title: 'Warehouse In'}"> + <span translate>Wh. In</span> + </th> + <th field="landed"> + <span translate>W. Landed</span> + </th> + </tr> + </thead> + <tbody + ng-repeat="travel in travels" + class="vn-mb-md" + vn-droppable="$ctrl.onDrop($event)" + ng-attr-id="{{::travel.id}}" + vn-stop-click> + <tr + class="header"> + <td vn-click-stop> + <span + class="link" ng-click="travelDescriptor.show($event, travel.id)"> {{::travel.id}} </span> - </vn-td> - <vn-td expand>{{::travel.agencyModeName}}</vn-td> - <vn-td expand>{{::travel.cargoSupplierNickname}}</vn-td> - <vn-td-editable name="reference" expand> - <text>{{travel.ref}}</text> - <field> - <vn-textfield class="dense" vn-focus - ng-model="travel.ref" - on-change="$ctrl.save(travel.id, {ref: value})"> - </vn-textfield> - </field> - </vn-td-editable> - <vn-td number>{{::travel.stickers}}</vn-td> - <vn-td-editable name="lockedKg" expand style="text-align: right"> - <text number>{{travel.kg}}</text> - <field> - <vn-input-number class="dense" vn-focus - ng-model="travel.kg" - on-change="$ctrl.save(travel.id, {kg: value})" - min="0"> - </vn-input-number> - </field> - </vn-td-editable> - <vn-td number>{{::travel.loadedKg}}</vn-td> - <vn-td number>{{::travel.volumeKg}}</vn-td> - <vn-td expand>{{::travel.warehouseOutName}}</vn-td> - <vn-td expand>{{::travel.shipped | date: 'dd/MM/yyyy'}}</vn-td> - <vn-td expand>{{::travel.warehouseInName}}</vn-td> - <vn-td expand>{{::travel.landed | date: 'dd/MM/yyyy'}}</vn-td> - </vn-tr> - <a href="#" ng-repeat="entry in travel.entries" class="vn-tr" draggable + </td> + <td expand vn-click-stop> + <span + class="link" + ng-click="supplierDescriptor.show($event, travel.cargoSupplierFk)"> + {{::travel.cargoSupplierNickname}} + </span> + </td> + <td expand>{{::travel.agencyModeName}}</td> + <td name="reference" expand vn-click-stop> + <vn-textfield + class="dense td-editable" + ng-model="travel.ref" + on-change="$ctrl.save(travel.id, {ref: value})"> + </vn-textfield> + </vn-icon> + </td> + <td number>{{::travel.stickers}}</td> + <td name="lockedKg" expand> + <vn-input-number + number + class="td-editable number" + ng-model="travel.kg" + on-change="$ctrl.save(travel.id, {kg: value})" + min="0" + vn-click-stop> + </vn-input-number> + </td> + <td number>{{::travel.loadedKg}}</td> + <td number>{{::travel.volumeKg}}</td> + <td expand>{{::travel.warehouseOutName}}</td> + <td expand>{{::travel.shipped | date: 'dd/MM/yyyy'}}</td> + <td expand>{{::travel.warehouseInName}}</td> + <td expand>{{::travel.landed | date: 'dd/MM/yyyy'}}</td> + </tr> + <tr + ng-repeat="entry in travel.entries" + draggable ng-attr-id="{{::entry.id}}" ng-click="$event.preventDefault()"> - <vn-td> + <td> <span class="link" ng-click="entryDescriptor.show($event, entry.id)"> {{::entry.id}} </span> - </vn-td> - <vn-td>{{::entry.supplierName}}</vn-td> - <vn-td></vn-td> - <vn-td expand>{{::entry.ref}}</vn-td> - <vn-td number>{{::entry.stickers}}</vn-td> - <vn-td number></vn-td> - <vn-td number>{{::entry.loadedkg}}</vn-td> - <vn-td number>{{::entry.volumeKg}}</vn-td> - <vn-td> + </td> + <td> + <span + class="link" + ng-click="supplierDescriptor.show($event, entry.supplierFk)"> + {{::entry.supplierName}} + </span> + </td> + <td></td> + <td expand>{{::entry.ref}}</td> + <td number>{{::entry.stickers}}</td> + <td number></td> + <td number>{{::entry.loadedkg}}</td> + <td number>{{::entry.volumeKg}}</td> + <td> <span ng-if="::entry.notes" vn-tooltip="{{::entry.notes}}"> {{::entry.notes}} </span> - </vn-td> - <vn-td> + </td> + <td> <span ng-if="::entry.evaNotes" vn-tooltip="{{::entry.evaNotes}}"> {{::entry.evaNotes}} </span> - </vn-td> - <vn-td></vn-td> - <vn-td></vn-td> - </a> - </vn-tbody> - </vn-table> - </section> - </vn-card> -</vn-data-viewer> + </td> + <td></td> + <td></td> + </tr> + </tbody> + </table> + </slot-table> + </smart-table> +</vn-card> <vn-travel-descriptor-popover vn-id="travelDescriptor"> </vn-travel-descriptor-popover> <vn-entry-descriptor-popover vn-id="entryDescriptor"> </vn-entry-descriptor-popover> - +<vn-supplier-descriptor-popover + vn-id="supplierDescriptor"> +</vn-supplier-descriptor-popover> diff --git a/modules/travel/front/extra-community/index.js b/modules/travel/front/extra-community/index.js index a380c1ed85..461712e9c5 100644 --- a/modules/travel/front/extra-community/index.js +++ b/modules/travel/front/extra-community/index.js @@ -14,8 +14,15 @@ class Controller extends Section { draggable.addEventListener('dragend', event => this.dragEnd(event)); - this.draggableElement = 'a[draggable]'; - this.droppableElement = 'vn-tbody[vn-droppable]'; + draggable.addEventListener('dragover', + event => this.dragOver(event)); + draggable.addEventListener('dragenter', + event => this.dragEnter(event)); + draggable.addEventListener('dragleave', + event => this.dragLeave(event)); + + this.draggableElement = 'tr[draggable]'; + this.droppableElement = 'tbody[vn-droppable]'; const twoDays = 2; const shippedFrom = new Date(); @@ -32,6 +39,8 @@ class Controller extends Section { landedTo: landedTo, continent: 'AM' }; + + this.smartTableOptions = {}; } get hasDateRange() { @@ -44,6 +53,15 @@ class Controller extends Section { return hasLanded || hasShipped || hasContinent || hasWarehouseOut; } + onDragInterval() { + if (this.dragClientY > 0 && this.dragClientY < 75) + this.$window.scrollTo(0, this.$window.scrollY - 10); + + const maxHeight = window.screen.availHeight - (window.outerHeight - window.innerHeight); + if (this.dragClientY > maxHeight - 75 && this.dragClientY < maxHeight) + this.$window.scrollTo(0, this.$window.scrollY + 10); + } + findDraggable($event) { const target = $event.target; const draggable = target.closest(this.draggableElement); @@ -65,6 +83,7 @@ class Controller extends Section { const id = parseInt(draggable.id); this.entryId = id; this.entry = draggable; + this.interval = setInterval(() => this.onDragInterval(), 50); } dragEnd($event) { @@ -72,6 +91,8 @@ class Controller extends Section { draggable.classList.remove('dragging'); this.entryId = null; this.entry = null; + + clearInterval(this.interval); } onDrop($event) { @@ -91,6 +112,37 @@ class Controller extends Section { } } + undrop() { + if (!this.dropping) return; + this.dropping.classList.remove('dropping'); + this.dropping = null; + } + + dragOver($event) { + this.dragClientY = $event.clientY; + $event.preventDefault(); + } + + dragEnter($event) { + let element = this.findDroppable($event); + if (element) this.dropCount++; + + if (element != this.dropping) { + this.undrop(); + if (element) element.classList.add('dropping'); + this.dropping = element; + } + } + + dragLeave($event) { + let element = this.findDroppable($event); + + if (element) { + this.dropCount--; + if (this.dropCount == 0) this.undrop(); + } + } + save(id, data) { const endpoint = `Travels/${id}`; this.$http.patch(endpoint, data) diff --git a/modules/travel/front/extra-community/index.spec.js b/modules/travel/front/extra-community/index.spec.js index 59688a46ca..ae48b9ca11 100644 --- a/modules/travel/front/extra-community/index.spec.js +++ b/modules/travel/front/extra-community/index.spec.js @@ -27,7 +27,7 @@ describe('Travel Component vnTravelExtraCommunity', () => { describe('findDraggable()', () => { it('should find the draggable element', () => { - const draggable = document.createElement('a'); + const draggable = document.createElement('tr'); draggable.setAttribute('draggable', true); const $event = new Event('dragstart'); @@ -43,7 +43,7 @@ describe('Travel Component vnTravelExtraCommunity', () => { describe('findDroppable()', () => { it('should find the droppable element', () => { - const droppable = document.createElement('vn-tbody'); + const droppable = document.createElement('tbody'); droppable.setAttribute('vn-droppable', true); const $event = new Event('drop'); @@ -58,9 +58,9 @@ describe('Travel Component vnTravelExtraCommunity', () => { }); describe('dragStart()', () => { - it(`should add the class "dragging" to the draggable element + it(`should add the class "dragging" to the draggable element and then set the entryId controller property`, () => { - const draggable = document.createElement('a'); + const draggable = document.createElement('tr'); draggable.setAttribute('draggable', true); draggable.setAttribute('id', 3); @@ -78,9 +78,9 @@ describe('Travel Component vnTravelExtraCommunity', () => { }); describe('dragEnd()', () => { - it(`should remove the class "dragging" from the draggable element + it(`should remove the class "dragging" from the draggable element and then set the entryId controller property to null`, () => { - const draggable = document.createElement('a'); + const draggable = document.createElement('tr'); draggable.setAttribute('draggable', true); draggable.setAttribute('id', 3); draggable.classList.add('dragging'); @@ -100,13 +100,13 @@ describe('Travel Component vnTravelExtraCommunity', () => { describe('onDrop()', () => { it('should make an HTTP patch query', () => { - const droppable = document.createElement('vn-tbody'); + const droppable = document.createElement('tbody'); droppable.setAttribute('vn-droppable', true); droppable.setAttribute('id', 1); jest.spyOn(controller, 'findDroppable').mockReturnValue(droppable); - const oldDroppable = document.createElement('vn-tbody'); + const oldDroppable = document.createElement('tbody'); oldDroppable.setAttribute('vn-droppable', true); const entry = document.createElement('div'); oldDroppable.appendChild(entry); diff --git a/modules/travel/front/extra-community/style.scss b/modules/travel/front/extra-community/style.scss index f903f94eac..532a3056ab 100644 --- a/modules/travel/front/extra-community/style.scss +++ b/modules/travel/front/extra-community/style.scss @@ -15,41 +15,44 @@ vn-travel-extra-community { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; + cursor: pointer; } - vn-td-editable text { - background-color: transparent; - padding: 0; - border: 0; - border-bottom: 1px dashed $color-active; - border-radius: 0; - color: $color-active - } - - vn-td-editable:hover text:after { - font-family: 'Material Icons'; - content: 'edit'; - position: absolute; - right: -15px; - color: $color-spacer - } - - vn-table[vn-droppable] { + table[vn-droppable] { border-radius: 0; } - a[draggable] { + tr[draggable] { transition: all .5s; cursor: move; + overflow: auto; outline: 0; + height: 65px; + pointer-events: fill; + user-select:all; } - a[draggable]:hover { - background-color: $color-hover-cd + tr[draggable] *::selection{ + background-color: transparent; } - a[draggable].dragging { - background-color: $color-success-light; - font-weight:bold + tr[draggable]:hover { + background-color: $color-hover-cd; } -} \ No newline at end of file + + tr[draggable].dragging { + background-color: $color-primary-light; + color: $color-font-light; + font-weight:bold; + } + + .td-editable{ + input{ + font-size: 1.25rem!important; + } + } + + .number *{ + text-align: right; + } +}