add local product

This commit is contained in:
Shaun Chyxion 2018-11-24 17:03:51 +08:00
parent 3b291c04fa
commit 20e191e73a
22 changed files with 657 additions and 19 deletions

View File

@ -0,0 +1,8 @@
import { helper } from '@ember/component/helper';
export function applicationAdminOrOwner(params/*, hash*/) {
const user = params[1];
return user.admin || params[0].filterBy('owner', user.id).length;
}
export default helper(applicationAdminOrOwner);

View File

@ -0,0 +1,7 @@
import { helper } from '@ember/component/helper';
export function localProductHasPerm(params/*, hash*/) {
return ['leli', 'chyxion'].includes(params[0].account);
}
export default helper(localProductHasPerm);

View File

@ -74,6 +74,12 @@ Router.map(function() {
this.route('create');
this.route('edit', {path: '/:id/edit'});
});
this.route('local-product', function() {
this.route('list', {path: '/list/:page'});
this.route('create');
this.route('edit', {path: '/:id/edit'});
});
});
export default Router;

View File

@ -1,4 +1,3 @@
import Ember from 'ember';
import BaseRoute from '../base';
import EmberObject, { computed } from '@ember/object';
import RSVP from 'rsvp';

View File

@ -1,4 +1,3 @@
import Ember from 'ember';
import BaseEditRoute from '../base-edit';
import $ from 'jquery';

View File

@ -1,20 +1,5 @@
import Ember from 'ember';
import BaseListRoute from './../base-list';
export default BaseListRoute.extend({
breadcrumbs: [{text: 'Customer Application'}],
actions: {
showContent(app) {
this.get('dialog').dialog({
title: app.name,
message: `<p>${app.content}</p>`,
buttons: {
ok: {
label: 'OK',
className: 'btn-info'
}
}
});
}
}
});

View File

@ -0,0 +1,34 @@
import BaseRoute from '../base';
import EmberObject, { computed } from '@ember/object';
import RSVP from 'rsvp';
export default BaseRoute.extend({
breadcrumbs: [{route: 'local-product.list', params: 1, text: 'Local Product'},
{text: 'Create Local Product'}],
modelClass: EmberObject.extend({
hasImage: computed('images.@each.file', function() {
return this.get('images').filter(image => image.file).length > 0;
})
}),
model() {
return RSVP.hash({
enabled: true,
images: [{}],
attachments: [{}]
});
},
actions: {
addImage() {
this.get('controller.model.images').pushObject({});
},
removeImage(image) {
this.get('controller.model.images').removeObject(image);
},
addAttachment() {
this.get('controller.model.attachments').pushObject({});
},
removeAttachment(attachment) {
this.get('controller.model.attachments').removeObject(attachment);
}
}
});

View File

@ -0,0 +1,122 @@
import BaseEditRoute from '../base-edit';
import $ from 'jquery';
export default BaseEditRoute.extend({
afterModel(model) {
const me = this;
me._super(...arguments);
this.set('breadcrumbs',
[{route: 'local-product.list', params: 1, text: 'Local Product'},
{text: 'Edit Local Product [' + model.name + ']'}]);
},
actions: {
addImage() {
const me = this;
me.set('controller.model.addImage', true);
me.set('controller.model.image', {});
},
submitAddImage() {
const me = this;
me.set('controller.errors', {});
if (me.get('controller.model.image.file')) {
me.set('controller.errors', {});
me.get('store').ajaxPost('local-product/add-image',
new FormData($('#form_' + me.get('controller.model.id'))[0]))
.then(image => {
me.get('controller.model.images').pushObject(image);
me.set('controller.model.addImage', false);
});
}
else {
me.set('controller.errors.image', ['No image file selected']);
}
},
closeAddImage() {
const me = this;
me.set('controller.model.addImage', false);
},
removeImage(image) {
const me = this;
me.get('dialog').confirm('Are you sure to remove image?', () => {
me.get('store').ajaxPost('local-product/remove-image', image).then(() => {
me.get('message').alert('Image removed');
me.get('controller.model.images').removeObject(image);
});
});
},
moveImageUp(image) {
const me = this;
me.moveUp(me.get('controller.model.images'), image, 'updateImage');
},
moveImageDown(image) {
const me = this;
me.moveDown(me.get('controller.model.images'), image, 'updateImage');
},
// attachments
addAttachment() {
const me = this;
me.set('controller.model.addAttachment', true);
me.set('controller.model.attachment', {});
},
submitAddAttachment() {
const me = this;
me.set('controller.errors', {});
if (me.get('controller.model.attachment.file')) {
me.get('store').ajaxPost('local-product/add-attachment',
new FormData($('#form_' + me.get('controller.model.id'))[0]))
.then(attachment => {
me.get('controller.model.attachments').pushObject(attachment);
me.set('controller.model.addAttachment', false);
});
}
else {
me.set('controller.errors.attachment', ['No attachment file selected']);
}
},
closeAddAttachment() {
const me = this;
me.set('controller.model.addAttachment', false);
},
removeAttachment(attachment) {
const me = this;
me.get('dialog').confirm('Are you sure to remove attachment?', () => {
me.get('store').ajaxPost('local-product/remove-attachment', attachment).then(() => {
me.get('message').alert('Attachment removed');
me.get('controller.model.attachments').removeObject(attachment);
});
});
},
moveAttachmentUp(attachment) {
const me = this;
me.moveUp(me.get('controller.model.attachments'), attachment, 'updateAttachment');
},
moveAttachmentDown(attachment) {
const me = this;
me.moveDown(me.get('controller.model.attachments'), attachment, 'updateAttachment');
}
},
updateImage(image) {
this.get('ajax').doPost('local-product/update-image', image, false);
},
updateAttachment(attachment) {
this.get('ajax').doPost('local-product/update-attachment', attachment, false);
},
moveUp(files, file, update) {
if (files && files.length > 1) {
let index = files.indexOf(file);
files.removeObject(file);
files.insertAt(index - 1, file);
--file.sort;
this[update](file);
}
},
moveDown(files, file, update) {
if (files && files.length > 1) {
let index = files.indexOf(file);
files.removeObject(file);
files.insertAt(index + 1, file);
++file.sort;
this[update](file);
}
}
});

View File

@ -0,0 +1,5 @@
import BaseListRoute from './../base-list';
export default BaseListRoute.extend({
breadcrumbs: [{text: 'Local Product'}]
});

View File

@ -1,4 +1,3 @@
import Ember from 'ember';
import BaseService from '../service';
export default BaseService.extend({

View File

@ -0,0 +1,15 @@
import BaseService from '../service';
export default BaseService.extend({
pageSize: 128,
modelName: 'LocalProduct',
constraints: {
name: {
presence: true,
length: {
minimum: 1,
maximum: 128
}
}
}
});

View File

@ -71,6 +71,12 @@
<span class="menu-text"> Customer Application </span>
{{/link-to}}
</li>
<li>
{{#link-to 'local-product.list' 1}}
<i class="menu-icon fa fa-bullhorn blue"></i>
<span class="menu-text"> Local Product </span>
{{/link-to}}
</li>
{{#if ajax.user.admin}}
<li>
{{#link-to 'import-record.list' 1}}

View File

@ -32,6 +32,7 @@
<i class="ace-icon fa fa-sticky-note-o bigger-110 hidden-480"></i>
Remark
</th>
{{#if ajax.user.admin}}
<th>
<i class="ace-icon fa fa-id-badge bigger-110 hidden-480"></i>
{{th-filter name='owner'
@ -42,6 +43,8 @@
text-exp='$.employeeId ($.name)'
}}
</th>
{{/if}}
{{#if (application/admin-or-owner model.data ajax.user)}}
<th>
<i class="ace-icon fa fa-exchange bigger-110 hidden-480"></i>
Enabled
@ -50,23 +53,34 @@
<i class="ace-icon fa fa-cogs bigger-110 hidden-480"></i>
Settings
</th>
{{/if}}
</tr>
</thead>
<tbody>
{{#each model.data as |it|}}
{{#if (or it.enabled (or ajax.user.admin (eq ajax.user.id it.owner)))}}
<tr>
<td>
{{customer-application/preview-btn model=it}}
</td>
<td>
{{editable-cell model=it field='note'}}
{{#if (or ajax.user.admin (eq ajax.user.id it.owner))}}
{{editable-cell model=it field='note'}}
{{else}}
{{it.note}}
{{/if}}
</td>
{{#if ajax.user.admin}}
<td>
{{option-text model.users it.owner 'id' 'name' '$.employeeId ($.name)'}}
</td>
{{/if}}
{{#if (application/admin-or-owner model.data ajax.user)}}
<td>
{{#if (or ajax.user.admin (eq ajax.user.id it.owner))}}
{{status-cell model=it enabledText='TRUE' disabledText='FALSE'}}
{{/if}}
</td>
<td>
<div class="hidden-sm hidden-xs btn-group">
@ -117,7 +131,9 @@
</div>
</div>
</td>
{{/if}}
</tr>
{{/if}}
{{/each}}
</tbody>
</table>

View File

@ -0,0 +1,64 @@
{{#form-content}}
{{form-input name='name' label='Name'}}
{{form-input name='note' label='Remark'}}
{{form-input name='content' label='Content' type='textarea'}}
{{#form-input name='image' label='Images'}}
<div class="widget-box transparent">
<div class="widget-header widget-header-small">
<div class="widget-toolbar action-buttons">
<a href="#" data-rel="tooltip" title="Add Image" {{action (route-action 'addImage')}}>
<i class="ace-icon fa fa-plus-circle green"></i>
</a>
</div>
</div>
<div class="widget-body">
<div class="widget-main padding-4">
{{#each model.images as |image i|}}
<div class="col-xs-6 no-padding-left">
<div class="space-4"></div>
{{input name=(concat 'imageTitles[' i ']') class='width-80' placeholder='Image title' value=image.title}}
&nbsp;
<a href="#" class="red" data-rel="tooltip" title="Remove Image" {{action (route-action 'removeImage' image)}}>
<i class="ace-icon fa fa-times bigger-125"></i>
</a>
<div class="space-2"></div>
{{image-input name=(concat 'images[' i ']') image=image}}
</div>
{{/each}}
</div>
</div>
</div>
{{/form-input}}
{{#form-input name='attachment' label='Attachments'}}
<div class="widget-box transparent">
<div class="widget-header widget-header-small">
<div class="widget-toolbar action-buttons">
<a href="#" data-rel="tooltip" title="Add Attachment" {{action (route-action 'addAttachment')}}>
<i class="ace-icon fa fa-plus-circle green"></i>
</a>
</div>
</div>
<div class="widget-body">
<div class="widget-main padding-4">
{{#each model.attachments as |attachment i|}}
<div class="col-xs-6 no-padding-left">
<div class="space-4"></div>
{{input name=(concat 'attachmentTitles[' i ']') class='width-80' placeholder='Attachment name' value=image.title}}
&nbsp;
<a href="#" class="red" data-rel="tooltip" title="Remove Attachment" {{action (route-action 'removeAttachment' attachment)}}>
<i class="ace-icon fa fa-times bigger-125"></i>
</a>
<div class="space-2"></div>
{{!input type='file'}}
{{file-input name=(concat 'attachments[' i ']') file=attachment}}
</div>
{{/each}}
</div>
</div>
</div>
{{/form-input}}
<hr />
{{form-footer-buttons}}
{{/form-content}}

View File

@ -0,0 +1,185 @@
{{#form-content}}
{{input type='hidden' name='id' value=model.id}}
{{form-input name='name' label='Name'}}
{{form-input name='note' label='Remark'}}
{{form-input name='content' label='Content' type='textarea'}}
{{!-- {{#form-input name='content' label='Content'}}
{{wysiwyg-editor model=model name='content'}}
{{/form-input}} --}}
{{#form-input label='Images' name='images'}}
<div class="widget-box transparent" style="opacity: 1;">
<!-- #section:custom/widget-box.options -->
<div class="widget-header">
{{!-- <h5 class="widget-title bigger lighter">
<i class="ace-icon fa fa-table"></i>
Users
</h5> --}}
<div class="widget-toolbar action-buttons">
<a href="#" data-rel="tooltip" title="Add Image" {{action (route-action 'addImage')}}>
<i class="ace-icon fa fa-plus-circle green"></i>
</a>
</div>
</div>
<!-- /section:custom/widget-box.options -->
<div class="widget-body">
<div class="widget-main no-padding">
<table class="table table-striped table-bordered table-hover">
<thead class="thin-border-bottom">
<tr>
<th style="width: 86px;">
<i class="ace-icon fa fa-file-image-o bigger-110 hidden-480"></i>
Image
</th>
<th>
<i class="ace-icon fa fa-sticky-note-o bigger-110 hidden-480"></i>
Description
</th>
<th style="width: 106px;">
<i class="ace-icon fa fa-cogs bigger-110 hidden-480"></i>
Settings
</th>
</tr>
</thead>
<tbody>
{{#each model.images as |image|}}
<tr>
<td>
{{image-previews images=image.url}}
</td>
<td>
{{editable-cell model=image field='note' post-url='local-product/update-image'}}
</td>
<td>
{{!image.note}}
<div class="btn-group">
<a class="btn btn-xs btn-danger" data-rel="tooltip" title="Remove" {{action (route-action 'removeImage' image)}}>
<i class="ace-icon fa fa-trash-o"></i>
</a>
{{#if (not-eq model.images.firstObject.id image.id)}}
<a class="btn btn-xs btn-info" data-rel="tooltip" title="Move Up" {{action (route-action 'moveImageUp' image)}}>
<i class="ace-icon fa fa-arrow-up bigger-90"></i>
</a>
{{/if}}
{{#if (not-eq model.images.lastObject.id image.id)}}
<a class="btn btn-xs btn-success" data-rel="tooltip" title="Move Down" {{action (route-action 'moveImageDown' image)}}>
<i class="ace-icon fa fa-arrow-down bigger-90"></i>
</a>
{{/if}}
</div>
</td>
</tr>
{{/each}}
</tbody>
</table>
</div>
</div>
</div>
{{/form-input}}
{{#form-input label='Attachments' name='attachments'}}
<div class="widget-box transparent" style="opacity: 1;">
<!-- #section:custom/widget-box.options -->
<div class="widget-header">
<div class="widget-toolbar action-buttons">
<a href="#" data-rel="tooltip" title="Add Attachment" {{action (route-action 'addAttachment')}}>
<i class="ace-icon fa fa-plus-circle green"></i>
</a>
</div>
</div>
<!-- /section:custom/widget-box.options -->
<div class="widget-body">
<div class="widget-main no-padding">
<table class="table table-striped table-bordered table-hover">
<thead class="thin-border-bottom">
<tr>
<th style="width: 86px;">
<i class="ace-icon fa fa-paperclip bigger-110 hidden-480"></i>
Attachment
</th>
<th>
<i class="ace-icon fa fa-sticky-note-o bigger-110 hidden-480"></i>
Description
</th>
<th style="width: 106px;">
<i class="ace-icon fa fa-cogs bigger-110 hidden-480"></i>
Settings
</th>
</tr>
</thead>
<tbody>
{{#each model.attachments as |attachment|}}
<tr>
<td>
{{!image-previews images=image.url}}
<a href="{{attachment.url}}" target="_blank">
<i class="ace-icon fa fa-download bigger-110 grey"></i>
Download
</a>
</td>
<td>
{{editable-cell model=attachment field='note' post-url='local-product/update-attachment'}}
</td>
<td>
<div class="btn-group">
<a class="btn btn-xs btn-danger" data-rel="tooltip" title="Remove" {{action (route-action 'removeAttachment' attachment)}}>
<i class="ace-icon fa fa-trash-o"></i>
</a>
{{#if (not-eq model.images.firstObject.id attachment.id)}}
<a class="btn btn-xs btn-info" data-rel="tooltip" title="Move Up" {{action (route-action 'moveAttachmentUp' attachment)}}>
<i class="ace-icon fa fa-arrow-up bigger-90"></i>
</a>
{{/if}}
{{#if (not-eq model.images.lastObject.id attachment.id)}}
<a class="btn btn-xs btn-success" data-rel="tooltip" title="Move Down" {{action (route-action 'moveAttachmentDown' attachment)}}>
<i class="ace-icon fa fa-arrow-down bigger-90"></i>
</a>
{{/if}}
</div>
</td>
</tr>
{{/each}}
</tbody>
</table>
</div>
</div>
</div>
{{/form-input}}
{{form-input-enabled label='Enabled' enabledText='TRUE' disabledText='FALSE'}}
<hr />
{{form-footer-buttons type='update'}}
{{/form-content}}
{{#if model.addImage}}
{{#modal-dialog title='Add Image' no-cancel=true submit=(route-action 'submitAddImage') on-close=(route-action 'closeAddImage') close-to-parent=false}}
{{#form-content form-id=(concat 'form_' model.id)}}
{{input type='hidden' name='productId' value=model.id}}
{{#form-input name='note' label='Description'}}
{{input name='note' value=model.image.note class='col-xs-12'}}
{{/form-input}}
{{#form-input name='image' label='Image'}}
{{image-input name='image' image=model.image}}
{{/form-input}}
{{/form-content}}
{{/modal-dialog}}
{{/if}}
{{#if model.addAttachment}}
{{#modal-dialog title='Add Attachment' no-cancel=true submit=(route-action 'submitAddAttachment') on-close=(route-action 'closeAddAttachment') close-to-parent=false}}
{{#form-content form-id=(concat 'form_' model.id)}}
{{input type='hidden' name='productId' value=model.id}}
{{#form-input name='note' label='Description'}}
{{input name='note' value=model.attachment.note class='col-xs-12'}}
{{/form-input}}
{{#form-input name='attachment' label='Attachment'}}
{{file-input name='attachment' file=model.attachment}}
{{/form-input}}
{{/form-content}}
{{/modal-dialog}}
{{/if}}

View File

@ -0,0 +1,109 @@
{{#main-content}}
<div class="widget-box transparent">
{{#if (local-product/has-perm ajax.user)}}
{{#grid-header}}
<li>
{{#link-to 'local-product.create'}}
<i class="ace-icon fa fa-plus-circle bigger-110 green"></i>
Create Local Product
{{/link-to}}
</li>
{{/grid-header}}
{{else}}
{{grid-header}}
{{/if}}
<div class="widget-body">
<!-- #section:custom/scrollbar -->
<div class="widget-main no-padding table-responsive no-border">
<table class="table table-striped table-bordered table-hover dataTable" style="border: 1px solid #ddd;">
<thead class="thin-border-bottom">
<tr>
<th>
{{th-filter name='namePrefix'
text='Name'
label='Name Filter'
options=model.namePrefixes
value-field='value'
text-field='text'
}}
</th>
<th>
<i class="ace-icon fa fa-sticky-note-o bigger-110 hidden-480"></i>
Remark
</th>
{{#if (local-product/has-perm ajax.user)}}
<th>
<i class="ace-icon fa fa-exchange bigger-110 hidden-480"></i>
Enabled
</th>
<th style="min-width: 114px">
<i class="ace-icon fa fa-cogs bigger-110 hidden-480"></i>
Settings
</th>
{{/if}}
</tr>
</thead>
<tbody>
{{#each model.data as |it|}}
<tr>
<td>
{{customer-application/preview-btn model=it}}
</td>
<td>
{{#if (local-product/has-perm ajax.user)}}
{{editable-cell model=it field='note'}}
{{else}}
{{it.note}}
{{/if}}
</td>
{{#if (local-product/has-perm ajax.user)}}
<td>
{{status-cell model=it enabledText='TRUE' disabledText='FALSE'}}
</td>
<td>
<div class="hidden-sm hidden-xs btn-group">
{{status-toggle-button model=it}}
{{#link-to 'local-product.edit' it.id class='btn btn-xs btn-info' data-rel='tooltip' title='Edit'}}
<i class="ace-icon fa fa-pencil bigger-120"></i>
{{/link-to}}
{{#unless it.enabled}}
{{delete-btn model=it}}
{{/unless}}
</div>
<div class="hidden-md hidden-lg">
<div class="inline pos-rel">
<button class="btn btn-minier btn-primary dropdown-toggle" data-toggle="dropdown" data-position="auto">
<i class="ace-icon fa fa-cog icon-only bigger-110"></i>
</button>
<ul class="dropdown-menu dropdown-only-icon dropdown-yellow dropdown-menu-right dropdown-caret dropdown-close">
<li>
{{status-toggle-button model=it iconOnly=true}}
</li>
<li>
{{#link-to 'local-product.edit' it.id class='tooltip-info' data-rel='tooltip' title='Edit'}}
<span class="blue">
<i class="ace-icon fa fa-pencil-square-o bigger-120"></i>
</span>
{{/link-to}}
</li>
{{#unless it.enabled}}
<li>
{{delete-btn model=it icon-only=true}}
</li>
{{/unless}}
</ul>
</div>
</div>
</td>
{{/if}}
</tr>
{{/each}}
</tbody>
</table>
</div>
{{pagination-bar}}
</div>
</div>
{{/main-content}}

View File

@ -0,0 +1,17 @@
import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';
moduleForComponent('application/admin-or-owner', 'helper:application/admin-or-owner', {
integration: true
});
// Replace this with your real tests.
test('it renders', function(assert) {
this.set('inputValue', '1234');
this.render(hbs`{{application/admin-or-owner inputValue}}`);
assert.equal(this.$().text().trim(), '1234');
});

View File

@ -0,0 +1,17 @@
import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';
moduleForComponent('local-product/has-perm', 'helper:local-product/has-perm', {
integration: true
});
// Replace this with your real tests.
test('it renders', function(assert) {
this.set('inputValue', '1234');
this.render(hbs`{{local-product/has-perm inputValue}}`);
assert.equal(this.$().text().trim(), '1234');
});

View File

@ -0,0 +1,11 @@
import { moduleFor, test } from 'ember-qunit';
moduleFor('route:local-product/create', 'Unit | Route | local product/create', {
// Specify the other units that are required for this test.
// needs: ['controller:foo']
});
test('it exists', function(assert) {
let route = this.subject();
assert.ok(route);
});

View File

@ -0,0 +1,11 @@
import { moduleFor, test } from 'ember-qunit';
moduleFor('route:local-product/edit', 'Unit | Route | local product/edit', {
// Specify the other units that are required for this test.
// needs: ['controller:foo']
});
test('it exists', function(assert) {
let route = this.subject();
assert.ok(route);
});

View File

@ -0,0 +1,11 @@
import { moduleFor, test } from 'ember-qunit';
moduleFor('route:local-product/list', 'Unit | Route | local product/list', {
// Specify the other units that are required for this test.
// needs: ['controller:foo']
});
test('it exists', function(assert) {
let route = this.subject();
assert.ok(route);
});

View File

@ -0,0 +1,12 @@
import { moduleFor, test } from 'ember-qunit';
moduleFor('service:local-product/service', 'Unit | Service | local product/service', {
// Specify the other units that are required for this test.
// needs: ['service:foo']
});
// Replace this with your real tests.
test('it exists', function(assert) {
let service = this.subject();
assert.ok(service);
});