Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into 2139-oder_catalog_searchbar
This commit is contained in:
commit
2ed9cbd402
|
@ -0,0 +1,87 @@
|
|||
USE `vn`;
|
||||
DROP procedure IF EXISTS `zone_getWarehouse`;
|
||||
|
||||
DELIMITER $$
|
||||
USE `vn`$$
|
||||
CREATE DEFINER=`root`@`%` PROCEDURE `zone_getWarehouse`(vAddress INT, vLanded DATE, vWarehouse INT)
|
||||
BEGIN
|
||||
/**
|
||||
* Devuelve el listado de agencias disponibles para la fecha,
|
||||
* dirección y almacén pasados.
|
||||
*
|
||||
* @param vAddress
|
||||
* @param vWarehouse warehouse
|
||||
* @param vLanded Fecha de recogida
|
||||
* @select Listado de agencias disponibles
|
||||
*/
|
||||
|
||||
CALL zone_getFromGeo(address_getGeo(vAddress));
|
||||
CALL zone_getOptionsForLanding(vLanded, FALSE);
|
||||
|
||||
SELECT am.id agencyModeFk,
|
||||
am.name agencyMode,
|
||||
am.description,
|
||||
am.deliveryMethodFk,
|
||||
TIMESTAMPADD(DAY, -zo.travelingDays, vLanded) shipped,
|
||||
zw.warehouseFk,
|
||||
z.id zoneFk
|
||||
FROM tmp.zoneOption zo
|
||||
JOIN zone z ON z.id = zo.zoneFk
|
||||
JOIN agencyMode am ON am.id = z.agencyModeFk
|
||||
JOIN zoneWarehouse zw ON zw.zoneFk = zo.zoneFk
|
||||
WHERE zw.warehouseFk = vWarehouse
|
||||
GROUP BY z.agencyModeFk
|
||||
ORDER BY agencyMode;
|
||||
|
||||
DROP TEMPORARY TABLE
|
||||
tmp.zone,
|
||||
tmp.zoneOption;
|
||||
|
||||
END$$
|
||||
|
||||
DELIMITER ;
|
||||
|
||||
USE `vn`;
|
||||
DROP procedure IF EXISTS `zone_getWarehouse__`;
|
||||
|
||||
DELIMITER $$
|
||||
USE `vn`$$
|
||||
CREATE DEFINER=`root`@`%` PROCEDURE `zone_getWarehouse__`(vAddress INT, vLanded DATE, vWarehouse INT)
|
||||
BEGIN
|
||||
/**
|
||||
* Devuelve el listado de agencias disponibles para la fecha,
|
||||
* dirección y almacén pasados.
|
||||
*
|
||||
* @param vAddress
|
||||
* @param vWarehouse warehouse
|
||||
* @param vLanded Fecha de recogida
|
||||
* @select Listado de agencias disponibles
|
||||
*/
|
||||
|
||||
CALL zone_getFromGeo(address_getGeo(vAddress));
|
||||
CALL zone_getOptionsForLanding(vLanded, FALSE);
|
||||
|
||||
SELECT am.id agencyModeFk,
|
||||
am.name agencyMode,
|
||||
am.description,
|
||||
am.deliveryMethodFk,
|
||||
TIMESTAMPADD(DAY, -zo.travelingDays, vLanded) shipped,
|
||||
zw.warehouseFk,
|
||||
z.id zoneFk
|
||||
FROM tmp.zoneOption zo
|
||||
JOIN zone z ON z.id = zo.zoneFk
|
||||
JOIN agencyMode am ON am.id = z.agencyModeFk
|
||||
JOIN zoneWarehouse zw ON zw.zoneFk = zo.zoneFk
|
||||
WHERE zw.warehouseFk
|
||||
GROUP BY z.agencyModeFk
|
||||
ORDER BY agencyMode;
|
||||
|
||||
DROP TEMPORARY TABLE
|
||||
tmp.zone,
|
||||
tmp.zoneOption;
|
||||
|
||||
END$$
|
||||
|
||||
DELIMITER ;
|
||||
|
||||
|
|
@ -345,11 +345,6 @@ let actions = {
|
|||
await this.clearInput('vn-searchbar');
|
||||
await this.write('vn-searchbar', searchValue);
|
||||
await this.waitToClick('vn-searchbar vn-icon[icon="search"]');
|
||||
await this.waitForNumberOfElements('.search-result', 1);
|
||||
await this.waitForContentLoaded();
|
||||
await this.evaluate(() => {
|
||||
return document.querySelector('.search-result').click();
|
||||
});
|
||||
await this.waitForContentLoaded();
|
||||
},
|
||||
|
||||
|
|
|
@ -116,18 +116,7 @@ describe('Client balance path', () => {
|
|||
});
|
||||
|
||||
it('should now search for the user Petter Parker', async() => {
|
||||
await page.write(selectors.clientsIndex.topbarSearch, 'Petter Parker');
|
||||
await page.waitToClick(selectors.clientsIndex.searchButton);
|
||||
await page.waitForNumberOfElements(selectors.clientsIndex.searchResult, 1);
|
||||
let resultCount = await page.countElement(selectors.clientsIndex.searchResult);
|
||||
|
||||
expect(resultCount).toEqual(1);
|
||||
});
|
||||
|
||||
it(`should click on the search result to access to the client's balance`, async() => {
|
||||
await page.waitForTextInElement(selectors.clientsIndex.searchResult, 'Petter Parker');
|
||||
await page.waitToClick(selectors.clientsIndex.searchResult);
|
||||
await page.waitForContentLoaded();
|
||||
await page.accessToSearchResult('Petter Parker');
|
||||
await page.waitToClick(selectors.clientBalance.balanceButton);
|
||||
let url = await page.expectURL('/balance');
|
||||
|
||||
|
|
|
@ -16,15 +16,9 @@ describe('Item summary path', () => {
|
|||
|
||||
it('should search for an item', async() => {
|
||||
await page.clearInput(selectors.itemsIndex.topbarSearch);
|
||||
await page.write(selectors.itemsIndex.topbarSearch, 'Ranged weapon longbow 2m');
|
||||
await page.write(selectors.itemsIndex.topbarSearch, 'Ranged weapon');
|
||||
await page.waitToClick(selectors.itemsIndex.searchButton);
|
||||
await page.waitForNumberOfElements(selectors.itemsIndex.searchResult, 1);
|
||||
const result = await page.countElement(selectors.itemsIndex.searchResult);
|
||||
|
||||
expect(result).toEqual(1);
|
||||
});
|
||||
|
||||
it(`should click on the search result summary button to open the item summary popup`, async() => {
|
||||
await page.waitForNumberOfElements(selectors.itemsIndex.searchResult, 3);
|
||||
await page.waitForTextInElement(selectors.itemsIndex.searchResult, 'Ranged weapon longbow 2m');
|
||||
await page.waitToClick(selectors.itemsIndex.searchResultPreviewButton);
|
||||
const isVisible = await page.isVisible(selectors.itemSummary.basicData);
|
||||
|
@ -75,24 +69,18 @@ describe('Item summary path', () => {
|
|||
it('should search for other item', async() => {
|
||||
await page.clearInput('vn-searchbar');
|
||||
await page.waitToClick(selectors.itemsIndex.searchButton);
|
||||
await page.write(selectors.itemsIndex.topbarSearch, 'Melee weapon combat fist 15cm');
|
||||
await page.write(selectors.itemsIndex.topbarSearch, 'Melee Reinforced');
|
||||
await page.waitToClick(selectors.itemsIndex.searchButton);
|
||||
await page.waitForNumberOfElements(selectors.itemsIndex.searchResult, 1);
|
||||
const result = await page.countElement(selectors.itemsIndex.searchResult);
|
||||
|
||||
expect(result).toEqual(1);
|
||||
});
|
||||
|
||||
it(`should now click on the search result summary button to open the item summary popup`, async() => {
|
||||
await page.waitForNumberOfElements(selectors.itemsIndex.searchResult, 2);
|
||||
await page.waitToClick(selectors.itemsIndex.searchResultPreviewButton);
|
||||
await page.waitForSelector(selectors.itemSummary.basicData, {visible: true});
|
||||
});
|
||||
|
||||
it(`should now check the item summary preview shows fields from basic data`, async() => {
|
||||
await page.waitForTextInElement(selectors.itemSummary.basicData, 'Melee weapon combat fist 15cm');
|
||||
await page.waitForTextInElement(selectors.itemSummary.basicData, 'Melee Reinforced weapon combat fist 15cm');
|
||||
const result = await page.waitToGetProperty(selectors.itemSummary.basicData, 'innerText');
|
||||
|
||||
expect(result).toContain('Melee weapon combat fist 15cm');
|
||||
expect(result).toContain('Melee Reinforced weapon combat fist 15cm');
|
||||
});
|
||||
|
||||
it(`should now check the item summary preview shows fields from tags`, async() => {
|
||||
|
@ -102,13 +90,6 @@ describe('Item summary path', () => {
|
|||
expect(result).toContain('Silver');
|
||||
});
|
||||
|
||||
it(`should now check the item summary preview shows fields from niche`, async() => {
|
||||
await page.waitForTextInElement(selectors.itemSummary.niche, 'A4');
|
||||
const result = await page.waitToGetProperty(selectors.itemSummary.niche, 'innerText');
|
||||
|
||||
expect(result).toContain('A4');
|
||||
});
|
||||
|
||||
it(`should now check the item summary preview shows fields from botanical`, async() => {
|
||||
await page.waitForTextInElement(selectors.itemSummary.botanical, '-');
|
||||
const result = await page.waitToGetProperty(selectors.itemSummary.botanical, 'innerText');
|
||||
|
@ -116,20 +97,13 @@ describe('Item summary path', () => {
|
|||
expect(result).toContain('-');
|
||||
});
|
||||
|
||||
it(`should now check the item summary preview shows fields from barcode`, async() => {
|
||||
await page.waitForTextInElement(selectors.itemSummary.barcode, '4');
|
||||
const result = await page.waitToGetProperty(selectors.itemSummary.barcode, 'innerText');
|
||||
|
||||
expect(result).toContain('4');
|
||||
});
|
||||
|
||||
it(`should now close the summary popup`, async() => {
|
||||
await page.closePopup();
|
||||
await page.waitForSelector(selectors.itemSummary.basicData, {hidden: true});
|
||||
});
|
||||
|
||||
it(`should navigate to the one of the items detailed section`, async() => {
|
||||
await page.waitToClick(selectors.itemsIndex.searchResult);
|
||||
await page.accessToSearchResult('Melee weapon combat fist 15cm');
|
||||
let url = await page.expectURL('summary');
|
||||
|
||||
expect(url).toBe(true);
|
||||
|
|
|
@ -63,7 +63,6 @@ describe('Item Create/Clone path', () => {
|
|||
|
||||
expect(result).toEqual('Infinity Gauntlet');
|
||||
|
||||
|
||||
result = await page
|
||||
.waitToGetProperty(selectors.itemBasicData.type, 'value');
|
||||
|
||||
|
@ -81,7 +80,10 @@ describe('Item Create/Clone path', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('clone', () => {
|
||||
// Issue #2201
|
||||
// When there is just one result you're redirected automatically to it, so
|
||||
// it's not possible to use the clone option.
|
||||
xdescribe('clone', () => {
|
||||
it('should return to the items index by clicking the return to items button', async() => {
|
||||
await page.waitToClick(selectors.itemBasicData.goToItemIndexButton);
|
||||
await page.wait(selectors.itemsIndex.createItemButton);
|
||||
|
|
|
@ -33,17 +33,7 @@ describe('Item regularize path', () => {
|
|||
});
|
||||
|
||||
it('should search for an specific item', async() => {
|
||||
await page.write(selectors.itemsIndex.topbarSearch, 'Ranged weapon pistol 9mm');
|
||||
await page.waitToClick(selectors.itemsIndex.searchButton);
|
||||
await page.waitForNumberOfElements(selectors.itemsIndex.searchResult, 1);
|
||||
const resultCount = await page.countElement(selectors.itemsIndex.searchResult);
|
||||
|
||||
expect(resultCount).toEqual(1);
|
||||
});
|
||||
|
||||
it(`should click on the search result to access to the summary`, async() => {
|
||||
await page.waitForTextInElement(selectors.itemsIndex.searchResult, 'Ranged weapon pistol 9mm');
|
||||
await page.waitToClick(selectors.itemsIndex.searchResult);
|
||||
await page.accessToSearchResult('Ranged weapon pistol 9mm');
|
||||
let url = await page.expectURL('/summary');
|
||||
|
||||
expect(url).toBe(true);
|
||||
|
@ -88,18 +78,9 @@ describe('Item regularize path', () => {
|
|||
});
|
||||
|
||||
it('should search for the ticket with alias missing', async() => {
|
||||
await page.accessToSearchResult('Carol Danvers');
|
||||
await page.keyboard.press('Escape');
|
||||
await page.write(selectors.ticketsIndex.topbarSearch, 'missing');
|
||||
await page.keyboard.press('Enter');
|
||||
await page.waitForNumberOfElements(selectors.ticketsIndex.searchResult, 1);
|
||||
const result = await page.countElement(selectors.ticketsIndex.searchResult);
|
||||
|
||||
expect(result).toEqual(1);
|
||||
});
|
||||
|
||||
it(`should click on the search result to access to the ticket summary`, async() => {
|
||||
await page.waitForTextInElement(selectors.ticketsIndex.searchResult, 'Missing');
|
||||
await page.waitToClick(selectors.ticketsIndex.searchResult);
|
||||
await page.accessToSearchResult('missing');
|
||||
let url = await page.expectURL('/summary');
|
||||
|
||||
expect(url).toBe(true);
|
||||
|
@ -130,18 +111,7 @@ describe('Item regularize path', () => {
|
|||
});
|
||||
|
||||
it('should search for the item once again', async() => {
|
||||
await page.clearInput(selectors.itemsIndex.topbarSearch);
|
||||
await page.write(selectors.itemsIndex.topbarSearch, 'Ranged weapon pistol 9mm');
|
||||
await page.waitToClick(selectors.itemsIndex.searchButton);
|
||||
await page.waitForNumberOfElements(selectors.itemsIndex.searchResult, 1);
|
||||
const resultCount = await page.countElement(selectors.itemsIndex.searchResult);
|
||||
|
||||
expect(resultCount).toEqual(1);
|
||||
});
|
||||
|
||||
it(`should click on the search result to access to the item tax`, async() => {
|
||||
await page.waitForTextInElement(selectors.itemsIndex.searchResult, 'Ranged weapon pistol 9mm');
|
||||
await page.waitToClick(selectors.itemsIndex.searchResult);
|
||||
await page.accessToSearchResult('Ranged weapon pistol 9mm');
|
||||
let url = await page.expectURL('/summary');
|
||||
|
||||
expect(url).toBe(true);
|
||||
|
@ -172,17 +142,7 @@ describe('Item regularize path', () => {
|
|||
});
|
||||
|
||||
it('should search for the ticket with id 25 once again', async() => {
|
||||
await page.write(selectors.ticketsIndex.topbarSearch, '25');
|
||||
await page.waitToClick(selectors.ticketsIndex.searchButton);
|
||||
await page.waitForNumberOfElements(selectors.ticketsIndex.searchResult, 1);
|
||||
const result = await page.countElement(selectors.ticketsIndex.searchResult);
|
||||
|
||||
expect(result).toEqual(1);
|
||||
});
|
||||
|
||||
it(`should now click on the search result to access to the ticket summary`, async() => {
|
||||
await page.waitForTextInElement(selectors.ticketsIndex.searchResult, '25');
|
||||
await page.waitToClick(selectors.ticketsIndex.searchResult);
|
||||
await page.accessToSearchResult('25');
|
||||
let url = await page.expectURL('/summary');
|
||||
|
||||
expect(url).toBe(true);
|
||||
|
|
|
@ -17,17 +17,7 @@ describe('Ticket descriptor path', () => {
|
|||
|
||||
describe('Delete ticket', () => {
|
||||
it('should search for an specific ticket', async() => {
|
||||
await page.write(selectors.ticketsIndex.topbarSearch, '18');
|
||||
await page.waitToClick(selectors.ticketsIndex.searchButton);
|
||||
await page.waitForNumberOfElements(selectors.ticketsIndex.searchResult, 1);
|
||||
const result = await page.countElement(selectors.ticketsIndex.searchResult);
|
||||
|
||||
expect(result).toEqual(1);
|
||||
});
|
||||
|
||||
it(`should click on the search result to access to the ticket summary`, async() => {
|
||||
await page.waitForTextInElement(selectors.ticketsIndex.searchResult, 'Cerebro');
|
||||
await page.waitToClick(selectors.ticketsIndex.searchResult);
|
||||
await page.accessToSearchResult('18');
|
||||
let url = await page.expectURL('/summary');
|
||||
|
||||
expect(url).toBe(true);
|
||||
|
@ -78,18 +68,7 @@ describe('Ticket descriptor path', () => {
|
|||
|
||||
describe('add stowaway', () => {
|
||||
it('should search for a ticket', async() => {
|
||||
await page.clearInput(selectors.ticketsIndex.topbarSearch);
|
||||
await page.write(selectors.ticketsIndex.topbarSearch, '16');
|
||||
await page.waitToClick(selectors.ticketsIndex.searchButton);
|
||||
await page.waitForNumberOfElements(selectors.ticketsIndex.searchResult, 1);
|
||||
const result = await page.countElement(selectors.ticketsIndex.searchResult);
|
||||
|
||||
expect(result).toEqual(1);
|
||||
});
|
||||
|
||||
it(`should now click on the search result to access to the ticket summary`, async() => {
|
||||
await page.waitForTextInElement(selectors.ticketsIndex.searchResult, 'Many Places');
|
||||
await page.waitToClick(selectors.ticketsIndex.searchResult);
|
||||
await page.accessToSearchResult('16');
|
||||
let url = await page.expectURL('/summary');
|
||||
|
||||
expect(url).toBe(true);
|
||||
|
|
|
@ -13,19 +13,31 @@ import './style.scss';
|
|||
*
|
||||
* @property {Object} filter A key-value object with filter parameters
|
||||
* @property {SearchPanel} panel The panel used for advanced searches
|
||||
* @property {Function} onSearch Function to call when search is submited
|
||||
* @property {CrudModel} model The model used for searching
|
||||
* @property {Function} exprBuilder If defined, is used to build each non-null param expresion
|
||||
*/
|
||||
export default class Controller extends Component {
|
||||
export default class Searchbar extends Component {
|
||||
constructor($element, $) {
|
||||
super($element, $);
|
||||
this.searchState = '.';
|
||||
this.autoState = true;
|
||||
|
||||
let criteria = {};
|
||||
this.deregisterCallback = this.$transitions.onSuccess(
|
||||
criteria, () => this.onStateChange());
|
||||
{}, transition => this.onStateChange(transition));
|
||||
}
|
||||
|
||||
$postLink() {
|
||||
this.onStateChange();
|
||||
if (this.autoState) {
|
||||
if (!this.baseState) {
|
||||
let stateParts = this.$state.current.name.split('.');
|
||||
this.baseState = stateParts[0];
|
||||
}
|
||||
|
||||
this.searchState = `${this.baseState}.index`;
|
||||
}
|
||||
|
||||
this.fetchStateFilter(this.autoLoad);
|
||||
}
|
||||
|
||||
$onDestroy() {
|
||||
|
@ -56,7 +68,16 @@ export default class Controller extends Component {
|
|||
if (value == null) this.params = [];
|
||||
}
|
||||
|
||||
onStateChange() {
|
||||
onStateChange(transition) {
|
||||
let ignoreHandler =
|
||||
!this.element.parentNode
|
||||
|| transition == this.transition;
|
||||
|
||||
if (ignoreHandler) return;
|
||||
this.fetchStateFilter();
|
||||
}
|
||||
|
||||
fetchStateFilter(autoLoad) {
|
||||
let filter = null;
|
||||
|
||||
if (this.$state.is(this.searchState)) {
|
||||
|
@ -68,10 +89,11 @@ export default class Controller extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
focus(this.element.querySelector('vn-textfield input'));
|
||||
if (!filter && autoLoad)
|
||||
filter = {};
|
||||
}
|
||||
|
||||
this.filter = filter;
|
||||
this.doSearch(filter, 'state');
|
||||
}
|
||||
|
||||
openPanel(event) {
|
||||
|
@ -100,25 +122,16 @@ export default class Controller extends Component {
|
|||
this.$.popover.hide();
|
||||
filter = compact(filter);
|
||||
filter = filter != null ? filter : {};
|
||||
this.doSearch(filter);
|
||||
this.doSearch(filter, 'panel');
|
||||
}
|
||||
|
||||
onSubmit() {
|
||||
this.doSearch(this.fromBar());
|
||||
this.doSearch(this.fromBar(), 'bar');
|
||||
}
|
||||
|
||||
removeParam(index) {
|
||||
this.params.splice(index, 1);
|
||||
this.doSearch(this.fromBar());
|
||||
}
|
||||
|
||||
doSearch(filter) {
|
||||
this.filter = filter;
|
||||
|
||||
let opts = this.$state.is(this.searchState)
|
||||
? {location: 'replace'} : null;
|
||||
this.$state.go(this.searchState,
|
||||
{q: JSON.stringify(filter)}, opts);
|
||||
this.doSearch(this.fromBar(), 'bar');
|
||||
}
|
||||
|
||||
fromBar() {
|
||||
|
@ -171,111 +184,135 @@ export default class Controller extends Component {
|
|||
this.params.push({chip, key, value});
|
||||
});
|
||||
}
|
||||
|
||||
doSearch(filter, source) {
|
||||
if (filter === this.filter) return;
|
||||
let promise = this.onSearch({$params: filter});
|
||||
promise = promise || this.$q.resolve();
|
||||
promise.then(data => this.onFilter(filter, source, data));
|
||||
}
|
||||
|
||||
onFilter(filter, source, data) {
|
||||
let state;
|
||||
let params;
|
||||
let opts;
|
||||
|
||||
if (filter) {
|
||||
let isOneResult = this.autoState
|
||||
&& source != 'state'
|
||||
&& !angular.equals(filter, {})
|
||||
&& data
|
||||
&& data.length == 1;
|
||||
|
||||
if (isOneResult) {
|
||||
let baseDepth = this.baseState.split('.').length;
|
||||
let stateParts = this.$state.current.name
|
||||
.split('.')
|
||||
.slice(baseDepth);
|
||||
|
||||
let subState = stateParts[0];
|
||||
|
||||
switch (subState) {
|
||||
case 'card':
|
||||
subState += `.${stateParts[1]}`;
|
||||
if (stateParts.length >= 3)
|
||||
subState += '.index';
|
||||
break;
|
||||
default:
|
||||
subState = 'card.summary';
|
||||
}
|
||||
|
||||
if (this.stateParams)
|
||||
params = this.stateParams({$row: data[0]});
|
||||
|
||||
state = `${this.baseState}.${subState}`;
|
||||
filter = null;
|
||||
} else {
|
||||
state = this.searchState;
|
||||
|
||||
if (filter)
|
||||
params = {q: JSON.stringify(filter)};
|
||||
if (this.$state.is(state))
|
||||
opts = {location: 'replace'};
|
||||
}
|
||||
}
|
||||
|
||||
this.filter = filter;
|
||||
|
||||
if (source != 'state')
|
||||
this.transition = this.$state.go(state, params, opts).transition;
|
||||
if (source != 'bar')
|
||||
focus(this.element.querySelector('vn-textfield input'));
|
||||
}
|
||||
|
||||
// Default search handlers
|
||||
|
||||
stateParams(params) {
|
||||
return {id: params.$row.id};
|
||||
}
|
||||
|
||||
onSearch(args) {
|
||||
if (!this.model) return;
|
||||
let filter = args.$params;
|
||||
|
||||
if (filter === null) {
|
||||
this.model.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
let where = null;
|
||||
let params = null;
|
||||
|
||||
if (this.exprBuilder) {
|
||||
where = buildFilter(filter,
|
||||
(param, value) => this.exprBuilder({param, value}));
|
||||
} else {
|
||||
params = Object.assign({}, filter);
|
||||
|
||||
if (this.fetchParams)
|
||||
params = this.fetchParams({$params: params});
|
||||
}
|
||||
|
||||
return this.model.applyFilter(where ? {where} : null, params)
|
||||
.then(() => this.model.data);
|
||||
}
|
||||
}
|
||||
|
||||
ngModule.vnComponent('vnSearchbar', {
|
||||
controller: Controller,
|
||||
controller: Searchbar,
|
||||
template: require('./searchbar.html'),
|
||||
bindings: {
|
||||
searchState: '@?',
|
||||
filter: '<?',
|
||||
suggestedFilter: '<?',
|
||||
panel: '@',
|
||||
info: '@?'
|
||||
info: '@?',
|
||||
onSearch: '&?',
|
||||
baseState: '@?',
|
||||
autoState: '<?',
|
||||
stateParams: '&?',
|
||||
model: '<?',
|
||||
exprBuilder: '&?',
|
||||
fetchParams: '&?'
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @property {CrudModel} model The model used for searching
|
||||
* @property {Function} exprBuilder If defined, is used to build each non-null param expresion
|
||||
* @property {Function} onSearch Function to call when search is submited
|
||||
*/
|
||||
class AutoSearch {
|
||||
constructor($state, $transitions) {
|
||||
this.$state = $state;
|
||||
this.$transitions = $transitions;
|
||||
|
||||
let criteria = {to: this.$state.current.name};
|
||||
this.deregisterCallback = this.$transitions.onSuccess(criteria,
|
||||
() => this.onStateChange());
|
||||
|
||||
this.fetchFilter();
|
||||
constructor(vnSlotService) {
|
||||
this.vnSlotService = vnSlotService;
|
||||
}
|
||||
|
||||
$postLink() {
|
||||
if (this.filter !== null)
|
||||
this.doSearch();
|
||||
}
|
||||
|
||||
$onDestroy() {
|
||||
this.deregisterCallback();
|
||||
}
|
||||
|
||||
fetchFilter() {
|
||||
if (this.$state.params.q) {
|
||||
try {
|
||||
this.filter = JSON.parse(this.$state.params.q);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
} else
|
||||
this.filter = null;
|
||||
}
|
||||
|
||||
onStateChange() {
|
||||
this.fetchFilter();
|
||||
this.doSearch();
|
||||
}
|
||||
|
||||
doSearch() {
|
||||
let filter = this.filter;
|
||||
if (filter == null && this.autoload)
|
||||
filter = {};
|
||||
|
||||
if (this.onSearch)
|
||||
this.onSearch({$params: filter});
|
||||
|
||||
if (this.model) {
|
||||
if (filter !== null) {
|
||||
let where = buildFilter(filter,
|
||||
(param, value) => this.exprBuilder({param, value}));
|
||||
|
||||
let userParams = {};
|
||||
let hasParams = false;
|
||||
|
||||
if (this.paramBuilder) {
|
||||
for (let param in filter) {
|
||||
let value = filter[param];
|
||||
if (value == null) continue;
|
||||
let expr = this.paramBuilder({param, value});
|
||||
if (expr) {
|
||||
Object.assign(userParams, expr);
|
||||
hasParams = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.model.applyFilter(
|
||||
where ? {where} : null,
|
||||
hasParams ? userParams : null
|
||||
);
|
||||
} else
|
||||
this.model.clear();
|
||||
}
|
||||
}
|
||||
|
||||
exprBuilder(param, value) {
|
||||
return {[param]: value};
|
||||
let searchbar = this.vnSlotService.getContent('topbar');
|
||||
if (searchbar && searchbar.$ctrl instanceof Searchbar)
|
||||
this.model = searchbar.$ctrl.model;
|
||||
}
|
||||
}
|
||||
AutoSearch.$inject = ['$state', '$transitions'];
|
||||
AutoSearch.$inject = ['vnSlotService'];
|
||||
|
||||
ngModule.vnComponent('vnAutoSearch', {
|
||||
controller: AutoSearch,
|
||||
bindings: {
|
||||
model: '<?',
|
||||
onSearch: '&?',
|
||||
exprBuilder: '&?',
|
||||
paramBuilder: '&?'
|
||||
model: '=?'
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -8,9 +8,35 @@ describe('Component vnSearchbar', () => {
|
|||
let $scope;
|
||||
let filter = {id: 1, search: 'needle'};
|
||||
|
||||
beforeEach(ngModule('vnCore'));
|
||||
beforeEach(ngModule('vnCore', $stateProvider => {
|
||||
$stateProvider
|
||||
.state('foo', {
|
||||
abstract: true
|
||||
})
|
||||
.state('foo.index', {
|
||||
url: '/foo/index'
|
||||
})
|
||||
.state('foo.card', {
|
||||
abstract: true
|
||||
})
|
||||
.state('foo.card.summary', {
|
||||
url: '/foo/:id/summary'
|
||||
})
|
||||
.state('foo.card.bar', {
|
||||
url: '/foo/:id/bar'
|
||||
})
|
||||
.state('foo.card.baz', {
|
||||
abstract: true
|
||||
})
|
||||
.state('foo.card.baz.index', {
|
||||
url: '/foo/:id/baz/index'
|
||||
})
|
||||
.state('foo.card.baz.edit', {
|
||||
url: '/foo/:id/baz/:bazId/edit'
|
||||
});
|
||||
}));
|
||||
|
||||
beforeEach(angular.mock.inject(($componentController, $rootScope, _$state_) => {
|
||||
beforeEach(inject(($componentController, $rootScope, _$state_) => {
|
||||
$scope = $rootScope.$new();
|
||||
$state = _$state_;
|
||||
$params = $state.params;
|
||||
|
@ -19,25 +45,26 @@ describe('Component vnSearchbar', () => {
|
|||
|
||||
$element = angular.element(`<div></div>`);
|
||||
controller = $componentController('vnSearchbar', {$element, $scope});
|
||||
controller.panel = 'vn-client-search-panel';
|
||||
}));
|
||||
|
||||
describe('$postLink()', () => {
|
||||
it(`should fetch the filter from the state if it's in the filter state`, () => {
|
||||
jest.spyOn(controller, 'doSearch');
|
||||
|
||||
controller.autoState = false;
|
||||
controller.$postLink();
|
||||
|
||||
expect(controller.filter).toEqual(filter);
|
||||
expect(controller.searchString).toBe('needle');
|
||||
expect(controller.params.length).toBe(1);
|
||||
expect(controller.doSearch).toHaveBeenCalledWith(filter, 'state');
|
||||
});
|
||||
|
||||
it(`should not fetch the filter from the state if not in the filter state`, () => {
|
||||
jest.spyOn(controller, 'doSearch');
|
||||
|
||||
controller.autoState = false;
|
||||
controller.searchState = 'other.state';
|
||||
controller.$postLink();
|
||||
|
||||
expect(controller.filter).toBeNull();
|
||||
expect(controller.searchString).toBeNull();
|
||||
expect(controller.params.length).toBe(0);
|
||||
expect(controller.doSearch).toHaveBeenCalledWith(null, 'state');
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -67,6 +94,14 @@ describe('Component vnSearchbar', () => {
|
|||
expect(chips.negated).toBe('not negated');
|
||||
expect(chips.myObjectProp).toBe('myObjectProp');
|
||||
});
|
||||
|
||||
it(`should clear the filter when null`, () => {
|
||||
controller.filter = null;
|
||||
|
||||
expect(controller.filter).toBeNull();
|
||||
expect(controller.searchString).toBeNull();
|
||||
expect(controller.params.length).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('shownFilter() getter', () => {
|
||||
|
@ -98,6 +133,7 @@ describe('Component vnSearchbar', () => {
|
|||
describe('onPanelSubmit()', () => {
|
||||
it(`should compact and define the filter`, () => {
|
||||
controller.$.popover = {hide: jasmine.createSpy('hide')};
|
||||
jest.spyOn(controller, 'doSearch');
|
||||
|
||||
const filter = {
|
||||
id: 1,
|
||||
|
@ -110,42 +146,110 @@ describe('Component vnSearchbar', () => {
|
|||
};
|
||||
controller.onPanelSubmit(filter);
|
||||
|
||||
expect(controller.filter).toEqual({
|
||||
expect(controller.doSearch).toHaveBeenCalledWith({
|
||||
id: 1,
|
||||
myObject: {keepThis: true},
|
||||
myArray: [true]
|
||||
});
|
||||
}, 'panel');
|
||||
});
|
||||
});
|
||||
|
||||
describe('onSubmit()', () => {
|
||||
it(`should define the filter`, () => {
|
||||
jest.spyOn(controller, 'doSearch');
|
||||
|
||||
controller.filter = filter;
|
||||
controller.searchString = 'mySearch';
|
||||
controller.onSubmit();
|
||||
|
||||
expect(controller.filter).toEqual({id: 1, search: 'mySearch'});
|
||||
expect(controller.doSearch).toHaveBeenCalledWith({
|
||||
id: 1,
|
||||
search: 'mySearch'
|
||||
}, 'bar');
|
||||
});
|
||||
});
|
||||
|
||||
describe('removeParam()', () => {
|
||||
it(`should remove the parameter from the filter`, () => {
|
||||
jest.spyOn(controller, 'doSearch');
|
||||
|
||||
controller.filter = filter;
|
||||
controller.removeParam(0);
|
||||
|
||||
expect(controller.filter).toEqual({search: 'needle'});
|
||||
expect(controller.doSearch).toHaveBeenCalledWith({
|
||||
search: 'needle'
|
||||
}, 'bar');
|
||||
});
|
||||
});
|
||||
|
||||
describe('doSearch()', () => {
|
||||
it(`should go to the search state and pass the filter as query param`, () => {
|
||||
it(`should do the filter`, () => {
|
||||
jest.spyOn(controller, 'onSearch');
|
||||
jest.spyOn(controller, 'onFilter');
|
||||
|
||||
controller.doSearch(filter, 'any');
|
||||
$scope.$apply();
|
||||
|
||||
expect(controller.onSearch).toHaveBeenCalledWith({$params: filter});
|
||||
expect(controller.onFilter).toHaveBeenCalledWith(filter, 'any', undefined);
|
||||
});
|
||||
});
|
||||
|
||||
describe('onFilter()', () => {
|
||||
it(`should go to the summary state when one result`, () => {
|
||||
jest.spyOn($state, 'go');
|
||||
|
||||
let data = [{id: 1}];
|
||||
|
||||
controller.baseState = 'foo';
|
||||
controller.onFilter(filter, 'any', data);
|
||||
$scope.$apply();
|
||||
|
||||
expect($state.go).toHaveBeenCalledWith('foo.card.summary', {id: 1}, undefined);
|
||||
expect(controller.filter).toEqual(null);
|
||||
});
|
||||
|
||||
it(`should keep the same card state when one result and it's already inside any card state`, () => {
|
||||
$state.go('foo.card.bar', {id: 1});
|
||||
$scope.$apply();
|
||||
|
||||
jest.spyOn($state, 'go');
|
||||
let data = [{id: 1}];
|
||||
|
||||
controller.baseState = 'foo';
|
||||
controller.onFilter(filter, 'any', data);
|
||||
$scope.$apply();
|
||||
|
||||
expect($state.go).toHaveBeenCalledWith('foo.card.bar', {id: 1}, undefined);
|
||||
expect(controller.filter).toEqual(null);
|
||||
});
|
||||
|
||||
it(`should keep the same card state but index when one result and it's already in card state but inside more than three-nested states`, () => {
|
||||
$state.go('foo.card.baz.edit', {id: 1, bazId: 1});
|
||||
$scope.$apply();
|
||||
|
||||
jest.spyOn($state, 'go');
|
||||
let data = [{id: 1}];
|
||||
|
||||
controller.baseState = 'foo';
|
||||
controller.onFilter(filter, 'any', data);
|
||||
$scope.$apply();
|
||||
|
||||
expect($state.go).toHaveBeenCalledWith('foo.card.baz.index', {id: 1}, undefined);
|
||||
expect(controller.filter).toEqual(null);
|
||||
});
|
||||
|
||||
it(`should go to the search state when multiple results and pass the filter as query param`, () => {
|
||||
jest.spyOn($state, 'go');
|
||||
|
||||
controller.autoState = false;
|
||||
controller.searchState = 'search.state';
|
||||
controller.doSearch(filter);
|
||||
controller.onFilter(filter, 'any');
|
||||
$scope.$apply();
|
||||
|
||||
let queryParams = {q: JSON.stringify(filter)};
|
||||
|
||||
expect($state.go).toHaveBeenCalledWith('search.state', queryParams, null);
|
||||
expect($state.go).toHaveBeenCalledWith('search.state', queryParams, undefined);
|
||||
expect(controller.filter).toEqual(filter);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -67,6 +67,12 @@ export class SlotService {
|
|||
$content.remove();
|
||||
}
|
||||
|
||||
getContent(slot) {
|
||||
if (this.slots[slot])
|
||||
return this.slots[slot].$element[0].firstElementChild;
|
||||
return null;
|
||||
}
|
||||
|
||||
refreshContent(slot) {
|
||||
if (!this.slots[slot]) return;
|
||||
let $content = this.stacks[slot][0];
|
||||
|
|
|
@ -2,12 +2,12 @@
|
|||
import isMobile from './is-mobile';
|
||||
|
||||
export default function focus(element) {
|
||||
if (isMobile) return;
|
||||
if (isMobile || !element) return;
|
||||
setTimeout(() => element.focus(), 10);
|
||||
}
|
||||
|
||||
export function select(element) {
|
||||
if (isMobile) return;
|
||||
if (isMobile || !element) return;
|
||||
setTimeout(() => {
|
||||
element.focus();
|
||||
if (element.select)
|
||||
|
|
|
@ -7,6 +7,11 @@ export default class Section extends Component {
|
|||
super($element, $);
|
||||
this.element.classList.add('vn-section');
|
||||
}
|
||||
|
||||
stopEvent(event) {
|
||||
event.preventDefault();
|
||||
event.stopImmediatePropagation();
|
||||
}
|
||||
}
|
||||
|
||||
ngModule.vnComponent('vnSection', {
|
||||
|
|
|
@ -63,9 +63,12 @@ export function config($translatePartialLoaderProvider, $httpProvider, $compileP
|
|||
$httpProvider.interceptors.push('vnInterceptor');
|
||||
|
||||
$compileProvider
|
||||
.debugInfoEnabled(false)
|
||||
.commentDirectivesEnabled(false)
|
||||
.cssClassDirectivesEnabled(false);
|
||||
|
||||
let env = process.env.NODE_ENV;
|
||||
if (env && env !== 'development')
|
||||
$compileProvider.debugInfoEnabled(false);
|
||||
}
|
||||
ngModule.config(config);
|
||||
|
||||
|
|
|
@ -17,8 +17,8 @@ core.run(vnInterceptor => {
|
|||
vnInterceptor.setApiPath(null);
|
||||
});
|
||||
|
||||
window.ngModule = function(moduleName) {
|
||||
return angular.mock.module(moduleName, function($provide, $translateProvider) {
|
||||
window.ngModule = function(moduleName, ...args) {
|
||||
let fns = [moduleName, function($provide, $translateProvider) {
|
||||
// Avoid unexpected request warnings caused by angular translate
|
||||
// https://angular-translate.github.io/docs/#/guide/22_unit-testing-with-angular-translate
|
||||
$provide.factory('customLocaleLoader', function($q) {
|
||||
|
@ -30,5 +30,10 @@ window.ngModule = function(moduleName) {
|
|||
});
|
||||
|
||||
$translateProvider.useLoader('customLocaleLoader');
|
||||
});
|
||||
}];
|
||||
|
||||
if (args.length)
|
||||
fns = fns.concat(args);
|
||||
|
||||
return angular.mock.module(...fns);
|
||||
};
|
||||
|
|
|
@ -1,13 +1,5 @@
|
|||
<vn-crud-model
|
||||
vn-id="model"
|
||||
url="Claims/filter"
|
||||
limit="20"
|
||||
data="claims"
|
||||
order="claimStateFk ASC, created DESC"
|
||||
auto-load="true">
|
||||
</vn-crud-model>
|
||||
<vn-auto-search
|
||||
on-search="$ctrl.onSearch($params)">
|
||||
model="model">
|
||||
</vn-auto-search>
|
||||
<vn-data-viewer
|
||||
model="model"
|
||||
|
@ -26,7 +18,7 @@
|
|||
</vn-thead>
|
||||
<vn-tbody>
|
||||
<a
|
||||
ng-repeat="claim in claims"
|
||||
ng-repeat="claim in model.data"
|
||||
class="{{::$ctrl.compareDate(ticket.shipped)}} clickable vn-tr search-result"
|
||||
ui-sref="claim.card.summary({id: claim.id})">
|
||||
<vn-td number>{{::claim.id}}</vn-td>
|
||||
|
|
|
@ -43,13 +43,6 @@ export default class Controller {
|
|||
onDescriptorLoad() {
|
||||
this.$.popover.relocate();
|
||||
}
|
||||
|
||||
onSearch(params) {
|
||||
if (params)
|
||||
this.$.model.applyFilter(null, params);
|
||||
else
|
||||
this.$.model.clear();
|
||||
}
|
||||
}
|
||||
|
||||
Controller.$inject = ['$scope'];
|
||||
|
|
|
@ -1,8 +1,15 @@
|
|||
<vn-crud-model
|
||||
vn-id="model"
|
||||
url="Claims/filter"
|
||||
limit="20"
|
||||
order="claimStateFk ASC, created DESC"
|
||||
auto-load="true">
|
||||
</vn-crud-model>
|
||||
<vn-portal slot="topbar">
|
||||
<vn-searchbar
|
||||
search-state="claim.index"
|
||||
panel="vn-claim-search-panel"
|
||||
info="Search claim by id or client name">
|
||||
info="Search claim by id or client name"
|
||||
model="model">
|
||||
</vn-searchbar>
|
||||
</vn-portal>
|
||||
<vn-portal slot="menu">
|
||||
|
|
|
@ -1,13 +1,6 @@
|
|||
<vn-crud-model
|
||||
vn-id="model"
|
||||
url="Clients"
|
||||
order="id DESC"
|
||||
limit="8"
|
||||
data="clients">
|
||||
</vn-crud-model>
|
||||
|
||||
<vn-auto-search
|
||||
model="model"
|
||||
expr-builder="$ctrl.exprBuilder(param, value)">
|
||||
model="model">
|
||||
</vn-auto-search>
|
||||
<vn-data-viewer
|
||||
model="model"
|
||||
|
@ -15,7 +8,7 @@
|
|||
<vn-card>
|
||||
<div class="vn-list separated">
|
||||
<a
|
||||
ng-repeat="client in clients track by client.id"
|
||||
ng-repeat="client in model.data track by client.id"
|
||||
ui-sref="client.card.summary(::{id: client.id})"
|
||||
translate-attr="{title: 'View client'}"
|
||||
class="vn-item search-result">
|
||||
|
|
|
@ -7,33 +7,6 @@ export default class Controller {
|
|||
this.clientSelected = null;
|
||||
}
|
||||
|
||||
exprBuilder(param, value) {
|
||||
switch (param) {
|
||||
case 'search':
|
||||
return /^\d+$/.test(value)
|
||||
? {id: value}
|
||||
: {name: {like: `%${value}%`}};
|
||||
case 'phone':
|
||||
return {
|
||||
or: [
|
||||
{phone: value},
|
||||
{mobile: value}
|
||||
]
|
||||
};
|
||||
case 'name':
|
||||
case 'socialName':
|
||||
case 'city':
|
||||
return {[param]: {like: `%${value}%`}};
|
||||
case 'email':
|
||||
return {[param]: {like: `%${value}%`}};
|
||||
case 'id':
|
||||
case 'fi':
|
||||
case 'postcode':
|
||||
case 'salesPersonFk':
|
||||
return {[param]: value};
|
||||
}
|
||||
}
|
||||
|
||||
openSummary(client, event) {
|
||||
if (event.defaultPrevented) return;
|
||||
event.preventDefault();
|
||||
|
|
|
@ -1,8 +1,16 @@
|
|||
<vn-crud-model
|
||||
vn-id="model"
|
||||
url="Clients"
|
||||
order="id DESC"
|
||||
limit="8"
|
||||
data="clients">
|
||||
</vn-crud-model>
|
||||
<vn-portal slot="topbar">
|
||||
<vn-searchbar
|
||||
search-state="client.index"
|
||||
panel="vn-client-search-panel"
|
||||
info="Search client by id or name">
|
||||
info="Search client by id or name"
|
||||
model="model"
|
||||
expr-builder="$ctrl.exprBuilder(param, value)">
|
||||
</vn-searchbar>
|
||||
</vn-portal>
|
||||
<vn-portal slot="menu">
|
||||
|
|
|
@ -1,7 +1,33 @@
|
|||
import ngModule from '../module';
|
||||
import ModuleMain from 'salix/components/module-main';
|
||||
|
||||
export default class Client extends ModuleMain {}
|
||||
export default class Client extends ModuleMain {
|
||||
exprBuilder(param, value) {
|
||||
switch (param) {
|
||||
case 'search':
|
||||
return /^\d+$/.test(value)
|
||||
? {id: value}
|
||||
: {name: {like: `%${value}%`}};
|
||||
case 'phone':
|
||||
return {
|
||||
or: [
|
||||
{phone: value},
|
||||
{mobile: value}
|
||||
]
|
||||
};
|
||||
case 'name':
|
||||
case 'socialName':
|
||||
case 'city':
|
||||
case 'email':
|
||||
return {[param]: {like: `%${value}%`}};
|
||||
case 'id':
|
||||
case 'fi':
|
||||
case 'postcode':
|
||||
case 'salesPersonFk':
|
||||
return {[param]: value};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ngModule.vnComponent('vnClient', {
|
||||
controller: Client,
|
||||
|
|
|
@ -1,13 +1,5 @@
|
|||
<vn-crud-model
|
||||
vn-id="model"
|
||||
url="Entries/filter"
|
||||
limit="20"
|
||||
params="::$ctrl.params"
|
||||
data="entries"
|
||||
auto-load="true">
|
||||
</vn-crud-model>
|
||||
<vn-auto-search
|
||||
on-search="$ctrl.onSearch($params)">
|
||||
model="model">
|
||||
</vn-auto-search>
|
||||
<vn-data-viewer
|
||||
model="model"
|
||||
|
@ -30,7 +22,7 @@
|
|||
</vn-tr>
|
||||
</vn-thead>
|
||||
<vn-tbody>
|
||||
<a ng-repeat="entry in entries"
|
||||
<a ng-repeat="entry in model.data"
|
||||
class="clickable vn-tr search-result"
|
||||
ui-sref="entry.card.summary({id: {{::entry.id}}})">
|
||||
<vn-td shrink>
|
||||
|
|
|
@ -5,13 +5,6 @@ export default class Controller {
|
|||
this.$ = $scope;
|
||||
}
|
||||
|
||||
onSearch(params) {
|
||||
if (params)
|
||||
this.$.model.applyFilter(null, params);
|
||||
else
|
||||
this.$.model.clear();
|
||||
}
|
||||
|
||||
showTravelDescriptor(event, travelFk) {
|
||||
if (event.defaultPrevented) return;
|
||||
event.preventDefault();
|
||||
|
|
|
@ -1,8 +1,14 @@
|
|||
<vn-crud-model
|
||||
vn-id="model"
|
||||
url="Entries/filter"
|
||||
limit="20"
|
||||
auto-load="true">
|
||||
</vn-crud-model>
|
||||
<vn-portal slot="topbar">
|
||||
<vn-searchbar
|
||||
search-state="entry.index"
|
||||
panel="vn-entry-search-panel"
|
||||
info="Search entrys by id">
|
||||
info="Search entrys by id"
|
||||
model="model">
|
||||
</vn-searchbar>
|
||||
</vn-portal>
|
||||
<vn-portal slot="menu">
|
||||
|
|
|
@ -1,16 +1,9 @@
|
|||
<vn-crud-model
|
||||
vn-id="model"
|
||||
url="InvoiceOuts/filter"
|
||||
limit="20"
|
||||
data="invoiceOuts"
|
||||
order="issued DESC">
|
||||
</vn-crud-model>
|
||||
<vn-auto-search
|
||||
on-search="$ctrl.onSearch($params)">
|
||||
model="model">
|
||||
</vn-auto-search>
|
||||
<vn-data-viewer
|
||||
model="model"
|
||||
class="vn-w-md vn-my-md">
|
||||
class="vn-w-md">
|
||||
<vn-card>
|
||||
<vn-table model="model">
|
||||
<vn-thead>
|
||||
|
@ -27,7 +20,7 @@
|
|||
</vn-tr>
|
||||
</vn-thead>
|
||||
<vn-tbody>
|
||||
<a ng-repeat="invoiceOut in invoiceOuts"
|
||||
<a ng-repeat="invoiceOut in model.data"
|
||||
class="clickable vn-tr search-result"
|
||||
ui-sref="invoiceOut.card.summary({id: {{::invoiceOut.id}}})">
|
||||
<vn-td>{{::invoiceOut.ref | dashIfEmpty}}</vn-td>
|
||||
|
|
|
@ -32,13 +32,6 @@ export default class Controller {
|
|||
event.preventDefault();
|
||||
event.stopImmediatePropagation();
|
||||
}
|
||||
|
||||
onSearch(params) {
|
||||
if (params)
|
||||
this.$.model.applyFilter(null, params);
|
||||
else
|
||||
this.$.model.clear();
|
||||
}
|
||||
}
|
||||
|
||||
Controller.$inject = ['$scope', 'vnToken'];
|
||||
|
|
|
@ -1,8 +1,14 @@
|
|||
<vn-crud-model
|
||||
vn-id="model"
|
||||
url="InvoiceOuts/filter"
|
||||
limit="20"
|
||||
order="issued DESC">
|
||||
</vn-crud-model>
|
||||
<vn-portal slot="topbar">
|
||||
<vn-searchbar
|
||||
search-state="invoiceOut.index"
|
||||
panel="vn-invoice-search-panel"
|
||||
info="Search invoices by reference">
|
||||
info="Search invoices by reference"
|
||||
model="model">
|
||||
</vn-searchbar>
|
||||
</vn-portal>
|
||||
<vn-portal slot="menu">
|
||||
|
|
|
@ -1,12 +1,5 @@
|
|||
<vn-crud-model
|
||||
vn-id="model"
|
||||
url="Items/filter"
|
||||
limit="12"
|
||||
order="isActive DESC, name, id"
|
||||
data="items">
|
||||
</vn-crud-model>
|
||||
<vn-auto-search
|
||||
on-search="$ctrl.onSearch($params)">
|
||||
model="model">
|
||||
</vn-auto-search>
|
||||
<vn-data-viewer
|
||||
model="model"
|
||||
|
@ -37,7 +30,7 @@
|
|||
</vn-tr>
|
||||
</vn-thead>
|
||||
<vn-tbody>
|
||||
<a ng-repeat="item in items"
|
||||
<a ng-repeat="item in model.data"
|
||||
class="clickable vn-tr search-result"
|
||||
ui-sref="item.card.summary({id: item.id})">
|
||||
<vn-td shrink>
|
||||
|
|
|
@ -21,13 +21,6 @@ class Controller {
|
|||
event.stopImmediatePropagation();
|
||||
}
|
||||
|
||||
onSearch(params) {
|
||||
if (params)
|
||||
this.$.model.applyFilter(null, params);
|
||||
else
|
||||
this.$.model.clear();
|
||||
}
|
||||
|
||||
showItemDescriptor(event, itemFk) {
|
||||
if (event.defaultPrevented) return;
|
||||
|
||||
|
@ -39,7 +32,6 @@ class Controller {
|
|||
this.$.itemDescriptor.show();
|
||||
}
|
||||
|
||||
|
||||
showWorkerDescriptor(event, workerFk) {
|
||||
if (event.defaultPrevented) return;
|
||||
|
||||
|
|
|
@ -1,9 +1,16 @@
|
|||
<vn-crud-model
|
||||
vn-id="model"
|
||||
url="Items/filter"
|
||||
limit="12"
|
||||
order="isActive DESC, name, id"
|
||||
data="items">
|
||||
</vn-crud-model>
|
||||
<vn-portal slot="topbar">
|
||||
<vn-searchbar
|
||||
search-state="item.index"
|
||||
panel="vn-item-search-panel"
|
||||
info="Search items by id, name or barcode"
|
||||
suggested-filter="{isActive: true}">
|
||||
suggested-filter="{isActive: true}"
|
||||
model="model">
|
||||
</vn-searchbar>
|
||||
</vn-portal>
|
||||
<vn-portal slot="menu">
|
||||
|
|
|
@ -1,12 +1,6 @@
|
|||
<vn-crud-model
|
||||
vn-id="model"
|
||||
url="Orders/filter"
|
||||
limit="20"
|
||||
data="orders"
|
||||
order="landed DESC, clientFk">
|
||||
</vn-crud-model>
|
||||
|
||||
<vn-auto-search
|
||||
on-search="$ctrl.onSearch($params)">
|
||||
model="model">
|
||||
</vn-auto-search>
|
||||
<vn-data-viewer
|
||||
model="model"
|
||||
|
@ -27,8 +21,10 @@
|
|||
</vn-tr>
|
||||
</vn-thead>
|
||||
<vn-tbody>
|
||||
<vn-tr ng-repeat="order in orders" class="clickable search-result"
|
||||
ui-sref="order.card.summary({id: {{::order.id}}})">
|
||||
<vn-tr
|
||||
ng-repeat="order in model.data"
|
||||
class="clickable search-result"
|
||||
ui-sref="order.card.summary({id: {{::order.id}}})">
|
||||
<vn-td number>{{::order.id}}</vn-td>
|
||||
<vn-td expand>
|
||||
<span class="link" ng-click="$ctrl.showClientDescriptor($event, order.clientFk)">
|
||||
|
|
|
@ -8,13 +8,6 @@ export default class Controller {
|
|||
this.ticketSelected = null;
|
||||
}
|
||||
|
||||
onSearch(params) {
|
||||
if (params)
|
||||
this.$.model.applyFilter(null, params);
|
||||
else
|
||||
this.$.model.clear();
|
||||
}
|
||||
|
||||
showClientDescriptor(event, clientFk) {
|
||||
this.$.clientDescriptor.clientFk = clientFk;
|
||||
this.$.clientDescriptor.parent = event.target;
|
||||
|
|
|
@ -1,8 +1,15 @@
|
|||
<vn-crud-model
|
||||
vn-id="model"
|
||||
url="Orders/filter"
|
||||
limit="20"
|
||||
data="orders"
|
||||
order="landed DESC, clientFk">
|
||||
</vn-crud-model>
|
||||
<vn-portal slot="topbar">
|
||||
<vn-searchbar
|
||||
search-state="order.index"
|
||||
panel="vn-order-search-panel"
|
||||
info="Search orders by id"
|
||||
model="model"
|
||||
filter="$ctrl.filter">
|
||||
</vn-searchbar>
|
||||
</vn-portal>
|
||||
|
|
|
@ -1,13 +1,5 @@
|
|||
<vn-crud-model
|
||||
vn-id="model"
|
||||
url="Routes/filter"
|
||||
limit="20"
|
||||
data="routes"
|
||||
order="created DESC"
|
||||
auto-load="true">
|
||||
</vn-crud-model>
|
||||
<vn-auto-search
|
||||
on-search="$ctrl.onSearch($params)">
|
||||
model="model">
|
||||
</vn-auto-search>
|
||||
<vn-data-viewer
|
||||
model="model"
|
||||
|
@ -27,7 +19,7 @@
|
|||
</vn-tr>
|
||||
</vn-thead>
|
||||
<vn-tbody>
|
||||
<a ng-repeat="route in routes"
|
||||
<a ng-repeat="route in model.data"
|
||||
class="clickable vn-tr search-result"
|
||||
ui-sref="route.card.summary({id: {{::route.id}}})">
|
||||
<vn-td number>{{::route.id | dashIfEmpty}}</vn-td>
|
||||
|
|
|
@ -24,13 +24,6 @@ export default class Controller {
|
|||
this.routeSelected = route;
|
||||
this.$.summary.show();
|
||||
}
|
||||
|
||||
onSearch(params) {
|
||||
if (params)
|
||||
this.$.model.applyFilter(null, params);
|
||||
else
|
||||
this.$.model.clear();
|
||||
}
|
||||
}
|
||||
Controller.$inject = ['$scope', 'vnToken'];
|
||||
|
||||
|
|
|
@ -1,9 +1,16 @@
|
|||
<vn-crud-model
|
||||
vn-id="model"
|
||||
url="Routes/filter"
|
||||
limit="20"
|
||||
order="created DESC"
|
||||
auto-load="true">
|
||||
</vn-crud-model>
|
||||
<vn-portal slot="topbar">
|
||||
<vn-searchbar
|
||||
search-state="route.index"
|
||||
panel="vn-route-search-panel"
|
||||
info="Search routes by id"
|
||||
filter="$ctrl.filter">
|
||||
filter="$ctrl.filter"
|
||||
model="model">
|
||||
</vn-searchbar>
|
||||
</vn-portal>
|
||||
<vn-portal slot="menu">
|
||||
|
|
|
@ -1,13 +1,5 @@
|
|||
<vn-crud-model
|
||||
vn-id="model"
|
||||
url="Tickets/filter"
|
||||
limit="20"
|
||||
params="::$ctrl.params"
|
||||
data="tickets"
|
||||
order="shipped DESC, zoneHour ASC, zoneMinute ASC, clientFk">
|
||||
</vn-crud-model>
|
||||
<vn-auto-search
|
||||
on-search="$ctrl.onSearch($params)">
|
||||
model="model">
|
||||
</vn-auto-search>
|
||||
<vn-data-viewer
|
||||
model="model"
|
||||
|
@ -38,7 +30,7 @@
|
|||
</vn-tr>
|
||||
</vn-thead>
|
||||
<vn-tbody>
|
||||
<a ng-repeat="ticket in tickets"
|
||||
<a ng-repeat="ticket in model.data"
|
||||
class="clickable vn-tr search-result"
|
||||
ui-sref="ticket.card.summary({id: {{::ticket.id}}})">
|
||||
<vn-td shrink>
|
||||
|
|
|
@ -51,30 +51,6 @@ export default class Controller {
|
|||
return this.checked.length;
|
||||
}
|
||||
|
||||
getScopeDates(days) {
|
||||
const today = new Date();
|
||||
today.setHours(0, 0, 0, 0);
|
||||
|
||||
const daysOnward = new Date(today);
|
||||
daysOnward.setDate(today.getDate() + days);
|
||||
daysOnward.setHours(23, 59, 59, 999);
|
||||
|
||||
return {from: today, to: daysOnward};
|
||||
}
|
||||
|
||||
onSearch(params) {
|
||||
if (params) {
|
||||
if (typeof (params.scopeDays) === 'number')
|
||||
Object.assign(params, this.getScopeDates(params.scopeDays));
|
||||
// Set default params to 1 scope days
|
||||
else if (Object.entries(params).length == 0)
|
||||
params = this.getScopeDates(1);
|
||||
|
||||
this.$.model.applyFilter(null, params);
|
||||
} else
|
||||
this.$.model.clear();
|
||||
}
|
||||
|
||||
goToLines(event, ticketFk) {
|
||||
this.preventDefault(event);
|
||||
let url = this.$state.href('ticket.card.sale', {id: ticketFk}, {absolute: true});
|
||||
|
|
|
@ -1,8 +1,15 @@
|
|||
<vn-crud-model
|
||||
vn-id="model"
|
||||
url="Tickets/filter"
|
||||
limit="20"
|
||||
order="shipped DESC, zoneHour ASC, zoneMinute ASC, clientFk">
|
||||
</vn-crud-model>
|
||||
<vn-portal slot="topbar">
|
||||
<vn-searchbar
|
||||
search-state="ticket.index"
|
||||
panel="vn-ticket-search-panel"
|
||||
info="Search ticket by id or alias">
|
||||
info="Search ticket by id or alias"
|
||||
model="model"
|
||||
fetch-params="$ctrl.fetchParams($params)">
|
||||
</vn-searchbar>
|
||||
</vn-portal>
|
||||
<vn-portal slot="menu">
|
||||
|
|
|
@ -1,7 +1,25 @@
|
|||
import ngModule from '../module';
|
||||
import ModuleMain from 'salix/components/module-main';
|
||||
|
||||
export default class Ticket extends ModuleMain {}
|
||||
export default class Ticket extends ModuleMain {
|
||||
fetchParams($params) {
|
||||
if (!Object.entries($params).length)
|
||||
$params.scopeDays = 1;
|
||||
|
||||
if (typeof $params.scopeDays === 'number') {
|
||||
const from = new Date();
|
||||
from.setHours(0, 0, 0, 0);
|
||||
|
||||
const to = new Date(from.getTime());
|
||||
to.setDate(to.getDate() + $params.scopeDays);
|
||||
to.setHours(23, 59, 59, 999);
|
||||
|
||||
Object.assign($params, {from, to});
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
}
|
||||
|
||||
ngModule.vnComponent('vnTicket', {
|
||||
controller: Ticket,
|
||||
|
|
|
@ -10,12 +10,11 @@
|
|||
</vn-crud-model>
|
||||
<vn-portal slot="topbar">
|
||||
<vn-searchbar
|
||||
info="Search weekly ticket by id or client id">
|
||||
info="Search weekly ticket by id or client id"
|
||||
auto-state="false"
|
||||
model="model">
|
||||
</vn-searchbar>
|
||||
</vn-portal>
|
||||
<vn-auto-search
|
||||
on-search="$ctrl.onSearch($params)">
|
||||
</vn-auto-search>
|
||||
<vn-data-viewer
|
||||
model="model"
|
||||
class="vn-w-md vn-mb-xl">
|
||||
|
|
|
@ -33,14 +33,6 @@ export default class Controller {
|
|||
event.stopImmediatePropagation();
|
||||
}
|
||||
|
||||
onSearch(params) {
|
||||
if (params)
|
||||
this.$.model.applyFilter(null, params);
|
||||
else
|
||||
this.$.model.clear();
|
||||
}
|
||||
|
||||
|
||||
showClientDescriptor(event, clientFk) {
|
||||
this.$.clientDescriptor.clientFk = clientFk;
|
||||
this.$.clientDescriptor.parent = event.target;
|
||||
|
@ -85,7 +77,6 @@ export default class Controller {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
Controller.$inject = ['$scope', 'vnApp', '$translate', '$http'];
|
||||
|
||||
ngModule.component('vnTicketWeeklyIndex', {
|
||||
|
|
|
@ -1,13 +1,6 @@
|
|||
<vn-crud-model
|
||||
vn-id="model"
|
||||
url="Travels/filter"
|
||||
limit="20"
|
||||
params="::$ctrl.params"
|
||||
data="travels"
|
||||
order="shipped DESC, landed DESC">
|
||||
</vn-crud-model>
|
||||
|
||||
<vn-auto-search
|
||||
on-search="$ctrl.onSearch($params)">
|
||||
model="model">
|
||||
</vn-auto-search>
|
||||
<vn-data-viewer
|
||||
model="model"
|
||||
|
@ -29,7 +22,7 @@
|
|||
</vn-tr>
|
||||
</vn-thead>
|
||||
<vn-tbody>
|
||||
<a ng-repeat="travel in travels"
|
||||
<a ng-repeat="travel in model.data"
|
||||
class="clickable vn-tr search-result"
|
||||
ui-sref="travel.card.summary({id: {{::travel.id}}})">
|
||||
<vn-td number>{{::travel.id}}</vn-td>
|
||||
|
@ -74,7 +67,7 @@
|
|||
</a>
|
||||
<vn-confirm
|
||||
vn-id="clone"
|
||||
on-response="$ctrl.onCloneAccept($response)"
|
||||
on-accept="$ctrl.onCloneAccept($data)"
|
||||
question="Do you want to clone this travel?"
|
||||
message="All it's properties will be copied">
|
||||
</vn-confirm>
|
||||
|
|
|
@ -1,72 +1,30 @@
|
|||
import ngModule from '../module';
|
||||
import Section from 'salix/components/section';
|
||||
|
||||
export default class Controller {
|
||||
constructor($scope, $state) {
|
||||
this.$ = $scope;
|
||||
this.ticketSelected = null;
|
||||
this.$state = $state;
|
||||
}
|
||||
|
||||
getScopeDates(days) {
|
||||
const today = new Date();
|
||||
today.setHours(0, 0, 0, 0);
|
||||
const daysOnward = new Date(today);
|
||||
daysOnward.setDate(today.getDate() + days);
|
||||
daysOnward.setHours(23, 59, 59, 999);
|
||||
return {shippedFrom: today, shippedTo: daysOnward};
|
||||
}
|
||||
|
||||
onSearch(params) {
|
||||
if (params) {
|
||||
let newParams = params;
|
||||
if (params.scopeDays) {
|
||||
const scopeDates = this.getScopeDates(params.scopeDays);
|
||||
Object.assign(newParams, scopeDates);
|
||||
} else if (Object.entries(params).length == 0)
|
||||
newParams = this.getScopeDates(1);
|
||||
|
||||
this.$.model.applyFilter(null, newParams);
|
||||
} else
|
||||
this.$.model.clear();
|
||||
}
|
||||
|
||||
stopEvent(event) {
|
||||
event.preventDefault();
|
||||
event.stopImmediatePropagation();
|
||||
}
|
||||
|
||||
cloneTravel(event, travel) {
|
||||
this.stopEvent(event);
|
||||
this.travelSelected = travel;
|
||||
this.$.clone.show();
|
||||
}
|
||||
|
||||
onCloneAccept(response) {
|
||||
if (!(response == 'accept' && this.travelSelected))
|
||||
return;
|
||||
if (this.travelSelected) {
|
||||
const travel = {
|
||||
ref: this.travelSelected.ref,
|
||||
agencyModeFk: this.travelSelected.agencyFk,
|
||||
shipped: this.travelSelected.shipped,
|
||||
landed: this.travelSelected.landed,
|
||||
warehouseInFk: this.travelSelected.warehouseInFk,
|
||||
warehouseOutFk: this.travelSelected.warehouseOutFk
|
||||
};
|
||||
const queryParams = JSON.stringify(travel);
|
||||
this.$state.go('travel.create', {q: queryParams});
|
||||
}
|
||||
|
||||
this.travelSelected = null;
|
||||
}
|
||||
export default class Controller extends Section {
|
||||
preview(event, travel) {
|
||||
this.stopEvent(event);
|
||||
this.travelSelected = travel;
|
||||
this.$.summary.show();
|
||||
}
|
||||
}
|
||||
|
||||
Controller.$inject = ['$scope', '$state'];
|
||||
cloneTravel(event, travel) {
|
||||
this.stopEvent(event);
|
||||
this.$.clone.show(travel);
|
||||
}
|
||||
|
||||
onCloneAccept(travel) {
|
||||
const params = JSON.stringify({
|
||||
ref: travel.ref,
|
||||
agencyModeFk: travel.agencyFk,
|
||||
shipped: travel.shipped,
|
||||
landed: travel.landed,
|
||||
warehouseInFk: travel.warehouseInFk,
|
||||
warehouseOutFk: travel.warehouseOutFk
|
||||
});
|
||||
this.$state.go('travel.create', {q: params});
|
||||
}
|
||||
}
|
||||
|
||||
ngModule.component('vnTravelIndex', {
|
||||
template: require('./index.html'),
|
||||
|
|
|
@ -1,108 +1,51 @@
|
|||
import './index.js';
|
||||
|
||||
describe('Travel Component vnTravelIndex', () => {
|
||||
let $componentController;
|
||||
let controller;
|
||||
let $window;
|
||||
let travels = [{
|
||||
let travel = {
|
||||
id: 1,
|
||||
warehouseInFk: 1,
|
||||
totalEntries: 3,
|
||||
isDelivered: false
|
||||
}, {
|
||||
id: 2,
|
||||
warehouseInFk: 1,
|
||||
total: 4,
|
||||
isDelivered: true
|
||||
}, {
|
||||
id: 3,
|
||||
warehouseInFk: 1,
|
||||
total: 2,
|
||||
isDelivered: true
|
||||
}];
|
||||
};
|
||||
|
||||
beforeEach(angular.mock.module('travel', $translateProvider => {
|
||||
$translateProvider.translations('en', {});
|
||||
}));
|
||||
beforeEach(ngModule('travel'));
|
||||
|
||||
beforeEach(angular.mock.inject((_$componentController_, $rootScope) => {
|
||||
$componentController = _$componentController_;
|
||||
beforeEach(angular.mock.inject(($componentController, $rootScope) => {
|
||||
let $scope = $rootScope.$new();
|
||||
controller = $componentController('vnTravelIndex', $scope);
|
||||
let $element = angular.element('<div></div>');
|
||||
controller = $componentController('vnTravelIndex', {$element, $scope});
|
||||
controller.$.summary = {show: jasmine.createSpy('show')};
|
||||
}));
|
||||
|
||||
describe('preview()', () => {
|
||||
it('should show the dialog summary', () => {
|
||||
let event = new MouseEvent('click', {
|
||||
view: $window,
|
||||
bubbles: true,
|
||||
cancelable: true
|
||||
});
|
||||
controller.preview(event, travels[0]);
|
||||
controller.preview(event, travel);
|
||||
|
||||
expect(controller.$.summary.show).toHaveBeenCalledWith();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getScopeDates()', () => {
|
||||
it('should return a range of dates', () => {
|
||||
let days = 2; // never put 1 or anything higher than 2
|
||||
let result = controller.getScopeDates(days);
|
||||
|
||||
let from = new Date(result.shippedFrom).getTime();
|
||||
let to = new Date(result.shippedTo).getTime();
|
||||
let range = to - from;
|
||||
|
||||
const dayInMilliseconds = 24 * 60 * 60 * 1000;
|
||||
|
||||
let millisecondsPerAddedDay = dayInMilliseconds - 1;
|
||||
|
||||
expect(range - dayInMilliseconds).toEqual(dayInMilliseconds + millisecondsPerAddedDay);
|
||||
});
|
||||
});
|
||||
|
||||
describe('onCloneAccept()', () => {
|
||||
it('should do nothing if response is not accept', () => {
|
||||
jest.spyOn(controller.$state, 'go');
|
||||
|
||||
let response = 'ERROR!';
|
||||
controller.travelSelected = 'check me';
|
||||
|
||||
controller.onCloneAccept(response);
|
||||
|
||||
expect(controller.$state.go).not.toHaveBeenCalledWith();
|
||||
expect(controller.travelSelected).toEqual('check me');
|
||||
});
|
||||
|
||||
it('should do nothing if response is accept but travelSelected is not defined in the controller', () => {
|
||||
jest.spyOn(controller.$state, 'go');
|
||||
|
||||
let response = 'accept';
|
||||
controller.travelSelected = undefined;
|
||||
|
||||
controller.onCloneAccept(response);
|
||||
|
||||
expect(controller.$state.go).not.toHaveBeenCalledWith();
|
||||
expect(controller.travelSelected).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should call go() then update travelSelected in the controller', () => {
|
||||
jest.spyOn(controller.$state, 'go');
|
||||
|
||||
let response = 'accept';
|
||||
controller.travelSelected = {
|
||||
ref: 1,
|
||||
agencyFk: 1};
|
||||
const travel = {
|
||||
ref: controller.travelSelected.ref,
|
||||
agencyModeFk: controller.travelSelected.agencyFk
|
||||
ref: 1,
|
||||
agencyFk: 1
|
||||
};
|
||||
const queryParams = JSON.stringify(travel);
|
||||
controller.onCloneAccept(response);
|
||||
const travelParams = {
|
||||
ref: travel.ref,
|
||||
agencyModeFk: travel.agencyFk
|
||||
};
|
||||
const queryParams = JSON.stringify(travelParams);
|
||||
controller.onCloneAccept(travel);
|
||||
|
||||
expect(controller.$state.go).toHaveBeenCalledWith('travel.create', {q: queryParams});
|
||||
expect(controller.travelSelected).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,8 +1,15 @@
|
|||
<vn-crud-model
|
||||
vn-id="model"
|
||||
url="Travels/filter"
|
||||
limit="20"
|
||||
order="shipped DESC, landed DESC">
|
||||
</vn-crud-model>
|
||||
<vn-portal slot="topbar">
|
||||
<vn-searchbar
|
||||
search-state="travel.index"
|
||||
panel="vn-travel-search-panel"
|
||||
info="Search travels by id">
|
||||
info="Search travels by id"
|
||||
model="model"
|
||||
fetch-params="$ctrl.fetchParams($params)">
|
||||
</vn-searchbar>
|
||||
</vn-portal>
|
||||
<vn-portal slot="menu">
|
||||
|
|
|
@ -1,7 +1,25 @@
|
|||
import ngModule from '../module';
|
||||
import ModuleMain from 'salix/components/module-main';
|
||||
|
||||
export default class Travel extends ModuleMain {}
|
||||
export default class Travel extends ModuleMain {
|
||||
fetchParams($params) {
|
||||
if (!Object.entries($params).length)
|
||||
$params.scopeDays = 1;
|
||||
|
||||
if (typeof $params.scopeDays === 'number') {
|
||||
const shippedFrom = new Date();
|
||||
shippedFrom.setHours(0, 0, 0, 0);
|
||||
|
||||
const shippedTo = new Date(shippedFrom.getTime());
|
||||
shippedTo.setDate(shippedTo.getDate() + $params.scopeDays);
|
||||
shippedTo.setHours(23, 59, 59, 999);
|
||||
|
||||
Object.assign($params, {shippedFrom, shippedTo});
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
}
|
||||
|
||||
ngModule.vnComponent('vnTravel', {
|
||||
controller: Travel,
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
import './index.js';
|
||||
|
||||
describe('Travel Component vnTravel', () => {
|
||||
let controller;
|
||||
|
||||
beforeEach(ngModule('travel'));
|
||||
|
||||
beforeEach(angular.mock.inject($componentController => {
|
||||
let $element = angular.element(`<div></div>`);
|
||||
controller = $componentController('vnTravel', {$element});
|
||||
}));
|
||||
|
||||
describe('fetchParams()', () => {
|
||||
it('should return a range of dates with passed scope days', () => {
|
||||
let params = controller.fetchParams({scopeDays: 2});
|
||||
|
||||
let from = params.shippedFrom.getTime();
|
||||
let to = params.shippedTo.getTime() + 1;
|
||||
let msInDay = 86400 * 1000;
|
||||
|
||||
expect(to - from).toEqual(3 * msInDay);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,12 +1,5 @@
|
|||
<vn-crud-model
|
||||
vn-id="model"
|
||||
url="Workers/filter"
|
||||
limit="20"
|
||||
order="id"
|
||||
data="workers">
|
||||
</vn-crud-model>
|
||||
<vn-auto-search
|
||||
on-search="$ctrl.onSearch($params)">
|
||||
model="model">
|
||||
</vn-auto-search>
|
||||
<vn-data-viewer
|
||||
model="model"
|
||||
|
@ -14,7 +7,7 @@
|
|||
<vn-card>
|
||||
<div class="vn-list separated">
|
||||
<a
|
||||
ng-repeat="worker in workers track by worker.id"
|
||||
ng-repeat="worker in model.data track by worker.id"
|
||||
ui-sref="worker.card.summary({id: worker.id})"
|
||||
translate-attr="{title: 'View worker'}"
|
||||
class="vn-item search-result">
|
||||
|
|
|
@ -9,13 +9,6 @@ export default class Controller {
|
|||
});
|
||||
}
|
||||
|
||||
onSearch(params) {
|
||||
if (params)
|
||||
this.$.model.applyFilter(null, params);
|
||||
else
|
||||
this.$.model.clear();
|
||||
}
|
||||
|
||||
preview(event, worker) {
|
||||
if (event.defaultPrevented) return;
|
||||
|
||||
|
@ -26,7 +19,6 @@ export default class Controller {
|
|||
this.$.preview.show();
|
||||
}
|
||||
|
||||
|
||||
goToTimeControl(event, workerId) {
|
||||
if (event.defaultPrevented) return;
|
||||
|
||||
|
@ -34,6 +26,7 @@ export default class Controller {
|
|||
event.stopPropagation();
|
||||
this.$state.go('worker.card.timeControl', {id: workerId}, {absolute: true});
|
||||
}
|
||||
|
||||
onMoreChange(callback) {
|
||||
callback.call(this);
|
||||
}
|
||||
|
|
|
@ -1,8 +1,14 @@
|
|||
<vn-crud-model
|
||||
vn-id="model"
|
||||
url="Workers/filter"
|
||||
limit="20"
|
||||
order="id">
|
||||
</vn-crud-model>
|
||||
<vn-portal slot="topbar">
|
||||
<vn-searchbar
|
||||
search-state="worker.index"
|
||||
panel="vn-worker-search-panel"
|
||||
info="Search workers by id, firstName, lastName or user name">
|
||||
info="Search workers by id, firstName, lastName or user name"
|
||||
model="model">
|
||||
</vn-searchbar>
|
||||
</vn-portal>
|
||||
<vn-portal slot="menu">
|
||||
|
|
|
@ -1,14 +1,5 @@
|
|||
<vn-crud-model
|
||||
vn-id="model"
|
||||
url="Zones"
|
||||
filter="::$ctrl.filter"
|
||||
limit="20"
|
||||
data="zones"
|
||||
auto-load="false">
|
||||
</vn-crud-model>
|
||||
<vn-auto-search
|
||||
model="model"
|
||||
expr-builder="$ctrl.exprBuilder(param, value)">
|
||||
model="model">
|
||||
</vn-auto-search>
|
||||
<vn-data-viewer
|
||||
model="model"
|
||||
|
@ -27,7 +18,7 @@
|
|||
</vn-thead>
|
||||
<vn-tbody>
|
||||
<vn-tr
|
||||
ng-repeat="zone in zones"
|
||||
ng-repeat="zone in model.data"
|
||||
ui-sref="zone.card.summary({id: zone.id})"
|
||||
class="clickable search-result">
|
||||
<vn-td number>{{::zone.id}}</vn-td>
|
||||
|
@ -61,7 +52,7 @@
|
|||
</vn-popup>
|
||||
<vn-confirm
|
||||
vn-id="clone"
|
||||
on-response="$ctrl.onCloneAccept($response)"
|
||||
on-accept="$ctrl.onCloneAccept($data)"
|
||||
question="Do you want to clone this zone?"
|
||||
message="All it's properties will be copied">
|
||||
</vn-confirm>
|
||||
|
|
|
@ -1,80 +1,26 @@
|
|||
import ngModule from '../module';
|
||||
import Section from 'salix/components/section';
|
||||
|
||||
export default class Controller {
|
||||
constructor($scope, $http, $state) {
|
||||
this.$scope = $scope;
|
||||
this.$http = $http;
|
||||
this.$state = $state;
|
||||
this.filter = {
|
||||
include: {
|
||||
relation: 'agencyMode',
|
||||
scope: {fields: ['name']}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
exprBuilder(param, value) {
|
||||
switch (param) {
|
||||
case 'search':
|
||||
return /^\d+$/.test(value)
|
||||
? {id: value}
|
||||
: {name: {like: `%${value}%`}};
|
||||
case 'name':
|
||||
return {[param]: {like: `%${value}%`}};
|
||||
case 'agencyModeFk':
|
||||
return {[param]: value};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clones a zone and all its properties
|
||||
* @param {Object} event - Event object
|
||||
* @param {Object} zone - Selected item
|
||||
*/
|
||||
clone(event, zone) {
|
||||
this.stopEvent(event);
|
||||
this.selectedZone = zone;
|
||||
this.$scope.clone.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone response callback
|
||||
* @param {String} response - Response string (['accept', 'cancel'])
|
||||
*/
|
||||
onCloneAccept(response) {
|
||||
if (!(response == 'accept' && this.selectedZone)) return;
|
||||
const query = `Zones/${this.selectedZone.id}/clone`;
|
||||
this.$http.post(query).then(res => {
|
||||
if (res && res.data)
|
||||
this.$state.go('zone.card.basicData', {id: res.data.id});
|
||||
});
|
||||
|
||||
this.selectedZone = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a summary modal
|
||||
* @param {Object} event - Event object
|
||||
* @param {Object} zone - Selected item
|
||||
*/
|
||||
export default class Controller extends Section {
|
||||
preview(event, zone) {
|
||||
this.stopEvent(event);
|
||||
this.selectedZone = zone;
|
||||
this.$scope.summary.show();
|
||||
this.$.summary.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevents normal event propagation
|
||||
* @param {Object} event - Event object
|
||||
*/
|
||||
stopEvent(event) {
|
||||
event.preventDefault();
|
||||
event.stopImmediatePropagation();
|
||||
clone(event, zone) {
|
||||
this.stopEvent(event);
|
||||
this.$.clone.show(zone);
|
||||
}
|
||||
|
||||
onCloneAccept(zone) {
|
||||
return this.$http.post(`Zones/${zone.id}/clone`)
|
||||
.then(res => {
|
||||
this.$state.go('zone.card.basicData', {id: res.data.id});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Controller.$inject = ['$scope', '$http', '$state'];
|
||||
|
||||
ngModule.component('vnZoneIndex', {
|
||||
template: require('./index.html'),
|
||||
controller: Controller
|
||||
|
|
|
@ -4,25 +4,25 @@
|
|||
filter="::$ctrl.filter">
|
||||
</vn-crud-model>
|
||||
<vn-portal slot="topbar">
|
||||
<vn-searchbar></vn-searchbar>
|
||||
<vn-searchbar
|
||||
on-search="$ctrl.onSearch($params)"
|
||||
auto-state="false">
|
||||
</vn-searchbar>
|
||||
</vn-portal>
|
||||
<vn-auto-search
|
||||
on-search="$ctrl.onSearch($params)">
|
||||
</vn-auto-search>
|
||||
<div class="vn-w-md">
|
||||
<vn-card class="vn-pa-lg vn-mt-md">
|
||||
<vn-treeview
|
||||
vn-id="treeview"
|
||||
root-label="Locations"
|
||||
fetch-func="$ctrl.onFetch($item)"
|
||||
sort-func="$ctrl.onSort($a, $b)">
|
||||
<vn-check acl-role="deliveryBoss"
|
||||
ng-model="item.selected"
|
||||
on-change="$ctrl.onSelection(value, item)"
|
||||
triple-state="true"
|
||||
ng-click="$event.preventDefault()"
|
||||
label="{{::item.name}}">
|
||||
</vn-check>
|
||||
</vn-treeview>
|
||||
<vn-card class="vn-pa-lg">
|
||||
<vn-treeview
|
||||
vn-id="treeview"
|
||||
root-label="Locations"
|
||||
fetch-func="$ctrl.onFetch($item)"
|
||||
sort-func="$ctrl.onSort($a, $b)">
|
||||
<vn-check acl-role="deliveryBoss"
|
||||
ng-model="item.selected"
|
||||
on-change="$ctrl.onSelection(value, item)"
|
||||
triple-state="true"
|
||||
ng-click="$event.preventDefault()"
|
||||
label="{{::item.name}}">
|
||||
</vn-check>
|
||||
</vn-treeview>
|
||||
</vn-card>
|
||||
</div>
|
||||
|
|
|
@ -1,8 +1,15 @@
|
|||
<vn-crud-model
|
||||
vn-id="model"
|
||||
url="Zones"
|
||||
filter="::$ctrl.filter"
|
||||
limit="20">
|
||||
</vn-crud-model>
|
||||
<vn-portal slot="topbar">
|
||||
<vn-searchbar
|
||||
search-state="zone.index"
|
||||
info="Search zone by id or name"
|
||||
panel="vn-zone-search-panel"
|
||||
info="Search zone by id or name">
|
||||
model="model"
|
||||
expr-builder="$ctrl.exprBuilder(param, value)">
|
||||
</vn-searchbar>
|
||||
</vn-portal>
|
||||
<vn-portal slot="menu">
|
||||
|
|
|
@ -1,7 +1,30 @@
|
|||
import ngModule from '../module';
|
||||
import ModuleMain from 'salix/components/module-main';
|
||||
|
||||
export default class Zone extends ModuleMain {}
|
||||
export default class Zone extends ModuleMain {
|
||||
constructor($element, $) {
|
||||
super($element, $);
|
||||
this.filter = {
|
||||
include: {
|
||||
relation: 'agencyMode',
|
||||
scope: {fields: ['name']}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
exprBuilder(param, value) {
|
||||
switch (param) {
|
||||
case 'search':
|
||||
return /^\d+$/.test(value)
|
||||
? {id: value}
|
||||
: {name: {like: `%${value}%`}};
|
||||
case 'name':
|
||||
return {[param]: {like: `%${value}%`}};
|
||||
case 'agencyModeFk':
|
||||
return {[param]: value};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ngModule.vnComponent('vnZone', {
|
||||
controller: Zone,
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
import './index.js';
|
||||
|
||||
describe('Zone Component vnZoneIndex', () => {
|
||||
let $componentController;
|
||||
describe('Zone Component vnZone', () => {
|
||||
let controller;
|
||||
|
||||
beforeEach(ngModule('zone'));
|
||||
|
||||
beforeEach(angular.mock.inject(_$componentController_ => {
|
||||
$componentController = _$componentController_;
|
||||
controller = $componentController('vnZoneIndex');
|
||||
beforeEach(angular.mock.inject($componentController => {
|
||||
let $element = angular.element(`<div></div>`);
|
||||
controller = $componentController('vnZone', {$element});
|
||||
}));
|
||||
|
||||
describe('exprBuilder()', () => {
|
|
@ -8354,7 +8354,7 @@
|
|||
},
|
||||
"kind-of": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz",
|
||||
"resolved": "http://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz",
|
||||
"integrity": "sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ=",
|
||||
"dev": true
|
||||
},
|
||||
|
@ -9990,7 +9990,7 @@
|
|||
},
|
||||
"jasmine-core": {
|
||||
"version": "2.99.1",
|
||||
"resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.99.1.tgz",
|
||||
"resolved": "http://registry.npmjs.org/jasmine-core/-/jasmine-core-2.99.1.tgz",
|
||||
"integrity": "sha1-5kAN8ea1bhMLYcS80JPap/boyhU=",
|
||||
"dev": true
|
||||
},
|
||||
|
@ -17968,7 +17968,7 @@
|
|||
},
|
||||
"xmlbuilder": {
|
||||
"version": "9.0.7",
|
||||
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz",
|
||||
"resolved": "http://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz",
|
||||
"integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0="
|
||||
},
|
||||
"xmlcreate": {
|
||||
|
|
Loading…
Reference in New Issue