This commit is contained in:
Shaun Chyxion 2017-08-16 21:33:34 +08:00
parent adff46015a
commit 64d1dab732
67 changed files with 1163 additions and 192 deletions

View File

@ -46,6 +46,11 @@
<artifactId>commons-csv</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.9.9</version>
</dependency>
<!-- Test Dependencies -->
<dependency>
<groupId>org.springframework</groupId>

View File

@ -0,0 +1,25 @@
package com.pudonghot.ambition.crm.controller;
import me.chyxion.tigon.webmvc.ResourceModel;
import org.apache.commons.io.IOUtils;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import java.io.IOException;
/**
* @author Shaun Chyxion <br>
* chyxion@163.com <br>
* Mar 10, 2017 23:15:01
*/
@Controller
public class DatabaseController {
@RequestMapping("/database/dump")
public ResourceModel databaseDump() throws IOException {
return new ResourceModel(
IOUtils.toByteArray(DatabaseController.class.getResourceAsStream("/static/index.html")),
MediaType.TEXT_HTML_VALUE, null);
}
}

View File

@ -0,0 +1,98 @@
package com.pudonghot.ambition.crm.controller;
import java.util.Map;
import java.util.Date;
import java.util.HashMap;
import javax.validation.Valid;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import com.alibaba.fastjson.JSONObject;
import me.chyxion.tigon.mybatis.Search;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import me.chyxion.tigon.model.ViewModel;
import me.chyxion.tigon.model.ListResult;
import org.apache.commons.lang3.StringUtils;
import com.pudonghot.ambition.crm.model.User;
import org.springframework.stereotype.Controller;
import com.pudonghot.ambition.crm.model.WeekGoal;
import com.pudonghot.ambition.crm.util.AmDateUtil;
import org.apache.shiro.authz.annotation.RequiresRoles;
import com.pudonghot.ambition.crm.service.WeekGoalService;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestMapping;
import com.pudonghot.ambition.crm.form.update.WeekGoalFormForUpdate;
/**
* @author Shaun Chyxion <br>
* chyxion@163.com <br>
* Mar 09, 2017 21:54:00
*/
@Slf4j
@Controller
@RequestMapping("/week-goal")
public class WeekGoalController
extends BaseQueryController<WeekGoal> {
private static final Map<String, String> CRITERION_COLS;
static {
CRITERION_COLS = new HashMap<>();
CRITERION_COLS.put(WeekGoal.QUARTER, WeekGoal.QUARTER);
}
@RequestMapping("/list")
public ListResult<ViewModel<WeekGoal>> list(
@Min(0)
@RequestParam(value = "start", defaultValue = "0")
final int start,
@Min(1)
@Max(512)
@RequestParam(value = "limit", defaultValue = "64")
final int limit,
@RequestParam(value = "filters", required = false)
final String filters,
@RequestParam(value = "search", required = false)
final String strSearch) {
((WeekGoalService) queryService).initWeekGoal(getUserId());
return listViewModels(filters(
search(start, limit, strSearch, null), filters));
}
@RequiresRoles(User.ROLE_ADMIN)
@RequestMapping(value = "/update", method = RequestMethod.POST)
public ViewModel<WeekGoal> update(
@Valid WeekGoalFormForUpdate form) {
return ((WeekGoalService) queryService).update(form);
}
private Search filters(final Search search, final String strFilters) {
if (StringUtils.isBlank(strFilters)) {
log.debug("No filters given.");
search.eq(WeekGoal.QUARTER, AmDateUtil.getQuarter(new Date()));
return search;
}
final JSONObject joFilters;
try {
joFilters = JSON.parseObject(decodeParam(strFilters));
}
catch (Exception e) {
throw new IllegalStateException(
"Invalid filters [" + strFilters + "]", e);
}
final Integer[] quarters =
joFilters.getObject(WeekGoal.QUARTER, Integer[].class);
if (quarters != null && quarters.length > 0) {
search.in(WeekGoal.QUARTER, quarters);
}
else {
search.eq(WeekGoal.QUARTER, AmDateUtil.getQuarter(new Date()));
}
return search;
}
}

View File

@ -0,0 +1,12 @@
package com.pudonghot.ambition.crm.service;
import java.io.InputStream;
/**
* @author Shaun Chyxion <br>
* chyxion@163.com <br>
* Aug 08, 2017 21:54:55
*/
public interface DatabaseService {
InputStream dump();
}

View File

@ -0,0 +1,26 @@
package com.pudonghot.ambition.crm.service;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import me.chyxion.tigon.model.ViewModel;
import me.chyxion.tigon.service.BaseCrudService;
import com.pudonghot.ambition.crm.model.WeekGoal;
import org.hibernate.validator.constraints.NotBlank;
import com.pudonghot.ambition.crm.form.update.WeekGoalFormForUpdate;
/**
* @version 0.0.1
* @since 0.0.1
* @author Shaun Chyxion <br>
* chyxion@163.com <br>
* May 4, 2016 1:18:42 PM
*/
public interface WeekGoalService
extends BaseCrudService<String, WeekGoal> {
ViewModel<WeekGoal> update(@NotNull @Valid WeekGoalFormForUpdate form);
void initWeekGoal(@NotBlank String userId);
}

View File

@ -0,0 +1,20 @@
package com.pudonghot.ambition.crm.service.support;
import java.io.InputStream;
import com.pudonghot.ambition.crm.service.DatabaseService;
/**
* @author Shaun Chyxion <br>
* chyxion@163.com <br>
* Aug 08, 2017 21:55:52
*/
public class DatabaseServiceSupport implements DatabaseService {
/**
* {@inheritDoc}
*/
@Override
public InputStream dump() {
return null;
}
}

View File

@ -0,0 +1,110 @@
package com.pudonghot.ambition.crm.service.support;
import java.util.Date;
import java.util.List;
import org.joda.time.*;
import java.util.ArrayList;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.Assert;
import me.chyxion.tigon.mybatis.Search;
import me.chyxion.tigon.model.ViewModel;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.stereotype.Service;
import com.pudonghot.ambition.crm.model.WeekGoal;
import com.pudonghot.ambition.crm.util.AmDateUtil;
import com.pudonghot.ambition.crm.mapper.WeekGoalMapper;
import com.pudonghot.ambition.crm.service.WeekGoalService;
import me.chyxion.tigon.service.support.BaseCrudServiceSupport;
import com.pudonghot.ambition.crm.form.update.WeekGoalFormForUpdate;
/**
* @version 0.0.1
* @since 0.0.1
* @author Shaun Chyxion <br>
* chyxion@163.com <br>
* Sep 22, 2015 10:45:41 AM
*/
@Slf4j
@Service
public class WeekGoalServiceSupport
extends BaseCrudServiceSupport<String, WeekGoal, WeekGoalMapper>
implements WeekGoalService {
/**
* {@inheritDoc}
*/
@Override
public ViewModel<WeekGoal> update(final WeekGoalFormForUpdate form) {
final String id = form.getId();
final WeekGoal weekGoal = find(id);
Assert.state(weekGoal != null, "No week goal [" + id + "] found");
return update(form.copy(weekGoal));
}
/**
* {@inheritDoc}
*/
@Override
public void initWeekGoal(final String userId) {
if (count(new Search(WeekGoal.USER_ID, userId)
.eq(WeekGoal.YEAR, DateTime.now().getYear())) == 0) {
log.info("User [{}] week goal is not initialized, init.", userId);
final Date now = new Date();
final int thisYear = AmDateUtil.getYear(now);
for (final Pair<Date, Date> week : weeksOfYear()) {
final WeekGoal weekGoal = new WeekGoal();
weekGoal.setId(idSeq.get());
weekGoal.setUserId(userId);
weekGoal.setYear(thisYear);
weekGoal.setGoal(0);
weekGoal.setDone(0);
weekGoal.setDateStart(week.getLeft());
final Date dateEnd = week.getRight();
weekGoal.setDateEnd(dateEnd);
// date end in next year is 4th quarter
weekGoal.setQuarter(AmDateUtil.getYear(dateEnd) == thisYear ?
AmDateUtil.getQuarter(dateEnd) : 4);
weekGoal.setEnabled(true);
weekGoal.setCreatedBy(userId);
weekGoal.setDateCreated(now);
create(weekGoal);
}
}
else {
log.info("User [{}] week goal found, ignore init.", userId);
}
}
private List<Pair<Date, Date>> weeksOfYear() {
Period weekPeriod = new Period().withWeeks(1);
int thisYear = DateTime.now().getYear();
DateTime dateStart = new DateTime(thisYear, 1, 1, 0, 0, 0, 0 );
// move to first monday
while (dateStart.getDayOfWeek() != DateTimeConstants.MONDAY) {
dateStart = dateStart.plusDays(1);
}
DateTime dateEnd = new DateTime(thisYear + 1, 1, 1, 0, 0, 0, 0);
// move to next sunday
while (dateEnd.getDayOfWeek() != DateTimeConstants.SATURDAY) {
dateEnd = dateEnd.plusDays(1);
}
final List<Pair<Date, Date>> weeks = new ArrayList<>(54);
Interval interval = new Interval(dateStart, weekPeriod);
DateTime intervalStart = null;
while ((intervalStart = interval.getStart()).isBefore(dateEnd)) {
weeks.add(Pair.of(intervalStart.toDate(),
interval.getEnd().minusMillis(1).toDate()));
interval = new Interval(intervalStart.plus(weekPeriod), weekPeriod);
}
return weeks;
}
}

View File

@ -0,0 +1,34 @@
package com.pudonghot.ambition.crm.util;
import java.util.Date;
import java.util.Calendar;
/**
* @author Donghuang <br>
* donghuang@wacai.com <br>
* Aug 13, 2017 11:06 PM
*/
public class AmDateUtil {
/**
* get quarter of date
* @param date date
* @return quarter
*/
public static int getQuarter(Date date) {
final Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
return calendar.get(Calendar.MONTH) / 3 + 1;
}
/**
* get year
* @param date date
* @return year
*/
public static int getYear(Date date) {
final Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
return calendar.get(Calendar.YEAR);
}
}

View File

@ -3,11 +3,11 @@ package com.pudonghot.ambition.crm;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser;
import org.apache.commons.csv.CSVPrinter;
import org.apache.commons.csv.CSVRecord;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.List;
@ -27,5 +27,18 @@ public class CSVTest {
List<CSVRecord> records = parse.getRecords();
CSVParser gbk = CSVParser.parse(CSVTest.class.getResource("/data/customers.csv"), Charset.forName("GBK"), CSVFormat.RFC4180);
log.info("csv: [{}].", gbk);
// p.print();
}
@Test
public void testPrint() throws IOException {
CSVFormat format = CSVFormat.DEFAULT;
CSVParser parse = format.parse(new InputStreamReader(CSVTest.class.getResourceAsStream("/data/customers.csv")));
List<CSVRecord> records = parse.getRecords();
CSVParser gbk = CSVParser.parse(CSVTest.class.getResource("/data/customers.csv"), Charset.forName("GBK"), CSVFormat.RFC4180);
log.info("csv: [{}].", gbk);
CSVPrinter p = new CSVPrinter(new FileWriter(new File("")), format);
// p.print();
}
}

View File

@ -1,5 +1,14 @@
package com.pudonghot.ambition.crm;
import com.pudonghot.ambition.crm.util.AmDateUtil;
import org.joda.time.DateTime;
import org.joda.time.DateTimeConstants;
import org.joda.time.Interval;
import org.joda.time.Period;
import org.junit.Test;
import java.util.Date;
/**
* @version 0.0.1
* @since 0.0.1
@ -9,4 +18,44 @@ package com.pudonghot.ambition.crm;
*/
public class TestDriver {
@Test
public void testWeeksOfYear() {
Period weekPeriod = new Period().withWeeks(1);
// int thisYear = DateTime.now().getYear();
int thisYear = 2013;
DateTime dateStart = new DateTime(thisYear, 1, 1, 0, 0, 0, 0 );
// move to first monday
while (dateStart.getDayOfWeek() != DateTimeConstants.MONDAY) {
dateStart = dateStart.plusDays(1);
}
DateTime dateEnd = new DateTime(thisYear + 1, 1, 1, 0, 0, 0, 0);
// move to next sunday
while (dateEnd.getDayOfWeek() != DateTimeConstants.SATURDAY) {
dateEnd = dateEnd.plusDays(1);
}
Interval interval = new Interval(dateStart, weekPeriod);
DateTime intervalStart = null;
while ((intervalStart = interval.getStart()).isBefore(dateEnd)) {
System.out.println("week : " + intervalStart.getWeekOfWeekyear()
+ " start: " + intervalStart.toDate()
+ " ending: " + interval.getEnd().minusMillis(1).toDate());
interval = new Interval(intervalStart.plus(weekPeriod), weekPeriod);
}
}
/**
* {@inheritDoc}
*/
@Test
public void testGetQuarter() {
System.err.println(AmDateUtil.getQuarter(new Date()));
System.err.println(AmDateUtil.getYear(new Date()));
}
}

View File

@ -0,0 +1,45 @@
package com.pudonghot.ambition.crm.service;
import java.io.File;
import org.junit.Test;
import java.io.FileWriter;
import java.io.IOException;
import org.junit.runner.RunWith;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVPrinter;
import com.pudonghot.ambition.crm.AmbitionCRM;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Donghuang <br>
* donghuang@wacai.com <br>
* Apr 24, 2017 11:33 PM
*/
@SpringBootTest
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = AmbitionCRM.class)
public class DatabaseDumpServiceTest {
@Autowired
private UserService userService;
@Test
public void testReadCSV() throws Exception {
CSVFormat format = CSVFormat.DEFAULT;
CSVPrinter p = new CSVPrinter(new FileWriter(new File("/Users/chyxion/Workspaces/dbdump.cvs")), format);
userService.scan(user -> {
try {
p.printRecord(user);
}
catch (IOException e) {
throw new IllegalStateException(e);
}
return false;
});
p.flush();
p.close();
}
}

View File

@ -0,0 +1,13 @@
package com.pudonghot.ambition.crm.mapper;
import me.chyxion.tigon.mybatis.BaseMapper;
import com.pudonghot.ambition.crm.model.WeekGoal;
/**
* @author Shaun Chyxion <br>
* chyxion@163.com <br>
* Aug 09, 2017 22:25:37
*/
public interface WeekGoalMapper extends BaseMapper<String, WeekGoal> {
}

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
/**
* @author Shaun Chyxion <br>
* chyxion@163.com <br>
* Aug 09, 2017 22:26:12
*/
-->
<!DOCTYPE mapper PUBLIC
"-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.pudonghot.ambition.crm.mapper.WeekGoalMapper">
</mapper>

View File

@ -0,0 +1,24 @@
package com.pudonghot.ambition.crm.form.update;
import lombok.Getter;
import lombok.Setter;
import me.chyxion.tigon.form.FU2;
import javax.validation.constraints.Min;
/**
* @version 0.0.1
* @since 0.0.1
* @author Shaun Chyxion <br>
* chyxion@163.com <br>
* May 10, 2016 1:42:01 PM
*/
@Getter
@Setter
public class WeekGoalFormForUpdate extends FU2<String, String> {
private static final long serialVersionUID = 1L;
@Min(0)
private int goal;
@Min(0)
private int done;
}

View File

@ -0,0 +1,43 @@
package com.pudonghot.ambition.crm.model;
import lombok.Getter;
import lombok.Setter;
import java.util.Date;
import me.chyxion.tigon.model.M3;
import me.chyxion.tigon.mybatis.Table;
import me.chyxion.tigon.mybatis.NotUpdate;
/**
* @author Shaun Chyxion <br>
* chyxion@163.com <br>
* Aug 09, 2017 22:24:40
*/
@Getter
@Setter
@Table("crm_week_goal")
public class WeekGoal extends M3<String, String> {
private static final long serialVersionUID = 1L;
// Column Names
public static final String USER_ID = "user_id";
public static final String YEAR = "year";
public static final String QUARTER = "quarter";
public static final String DATE_START = "date_start";
public static final String DATE_END = "date_end";
public static final String GOAL = "goal";
public static final String DONE = "done";
// Properties
@NotUpdate
private String userId;
@NotUpdate
private int year;
@NotUpdate
private int quarter;
@NotUpdate
private Date dateStart;
@NotUpdate
private Date dateEnd;
private int goal;
private int done;
}

View File

@ -2,16 +2,40 @@ import Ember from 'ember';
import BaseComponentMixin from '../mixins/components/base-component';
export default Ember.Component.extend(BaseComponentMixin, {
'col-width': 6,
colWidth: Ember.computed.alias('col-width'),
classNameBindings: ['hasError:has-error'],
model: Ember.computed.alias('route.controller.model'),
errors: Ember.computed.alias('route.controller.errors'),
hasError: Ember.computed('errors', function() {
return this.get('errors.' + this.get('name'));
}),
'error-msg': true,
errorMsg: Ember.computed.alias('error-msg'),
'label-class': 'col-xs-12 col-sm-3 col-md-3',
labelClass: Ember.computed.alias('label-class'),
'input-class': 'col-xs-12 col-sm-5 col-md-5',
inputClass: Ember.computed.alias('input-class'),
'data-scope': 'model',
dataScope: Ember.computed.alias('data-scope'),
didReceiveAttrs() {
let me = this;
me._super(...arguments);
let dataScope = me.get('data-scope');
if ('controller' === dataScope) {
me.set('dataModel', me.get('route.controller'));
}
else if ('route' === dataScope) {
me.set('dataModel', me.get('route'));
}
else {
me.set('dataModel', me.get('model'));
}
},
getVal() {
return this.get('model.' + this.get('name'));
return this.get('dataModel.' + this.get('name'));
},
setVal(val) {
this.set('model.' + this.get('name'), val);
this.set('dataModel.' + this.get('name'), val);
}
});

View File

@ -2,7 +2,10 @@ import Ember from 'ember';
import BaseComponent from './base-component';
export default BaseComponent.extend({
allowBlank: true,
'allow-blank': true,
allowBlank: Ember.computed.alias('allow-blank'),
type: 'text',
step: 1, // for number
actions: {
doEdit() {
var me = this;
@ -10,7 +13,7 @@ export default BaseComponent.extend({
me.set('isEditing', true)
Ember.run.later(() => {
Ember.$('input[type="text"][name="' + me.get('model.id') + '"]').focus();
}, 128);
}, 320);
},
doUpdate() {
let me = this;
@ -20,9 +23,9 @@ export default BaseComponent.extend({
}
me.set('isUpdating', true);
let newValue = me.getFieldValue();
if (me.get('oldValue') !== newValue) {
if (!me.get('allowBlank') && !newValue) {
me.get('message').warn('属性不能为空');
if (me.get('oldValue') != newValue) {
if (!me.get('allow-blank') && !newValue) {
me.get('message').warn('Property could not be blank');
// reset field value
me.resetValue();
me.set('isUpdating', false);

View File

@ -1,74 +0,0 @@
import Ember from 'ember';
import BaseFormInput from './base-form-input';
export default BaseFormInput.extend({
tagName: 'div',
classNames: ['form-group'],
classNameBindings: ['hasError:has-error'],
colWidth: 6,
'col-width': Ember.computed.alias('colWidth'),
configNames: ['allow-single-deselect',
'disable-search',
'disable-search-threshold',
'enable-split-word-search',
'inherit-select-classes',
'max-selected-options',
'no-results-text',
'placeholder-text-multiple',
'placeholder-text-single',
'search-contains',
'single-backstroke-delete',
'width',
'display-disabled-options',
'display-selected-options',
'include-group-label-in-selected',
'max-shown-results',
'case-sensitive-search',
'hide-results-on-select',
'rtl'
],
// chosen options
'allow-single-deselect': false,
'disable-search': false,
'disable-search-threshold': 0,
'enable-split-word-search': true,
'inherit-select-classes': false,
'max-selected-options': Infinity,
'no-results-text': 'No results match',
'placeholder-text-multiple': 'Select Some Options',
'placeholder-text-single': 'Select an Option',
'search-contains': false,
'single-backstroke-delete': true,
'width': '100%',
'display-disabled-options': true,
'display-selected-options': true,
'include-group-label-in-selected': false,
'max-shown-results': undefined,
'case-sensitive-search': false,
'hide-results-on-select': true,
'rtl': false,
didInsertElement() {
let me = this;
me._super(...arguments);
let config = {};
me.get('configNames').forEach(n => config[n.replace(/\-/g, '_')] = me.get(n));
me.$('select').chosen(config).change(function(e, option) {
let value = option.selected;
if (value) {
Ember.set(me.findOption(value), 'selected', 'selected');
}
else {
Ember.set(me.findOption(option.deselected), 'selected', false);
}
});
me.$('.search-field > input').width('100%');
},
findOption(value) {
return this.get('options').find(option => {return option.value == value});
}
});

View File

@ -4,5 +4,10 @@ import BaseFormInput from './base-form-input';
export default BaseFormInput.extend({
classNames: ['form-group'],
classNameBindings: ['hasError:has-error'],
colWidth: 6
actions: {
onOptionChanged(val) {
let me = this;
me.setVal(val);
}
}
});

View File

@ -0,0 +1,26 @@
import Ember from 'ember';
import BaseFormInput from './base-form-input';
export default BaseFormInput.extend({
classNames: ['form-group'],
classNameBindings: ['hasError:has-error'],
didInsertElement() {
let me = this;
me._super(...arguments);
me.$('select.select2').select2({
placeholder: me.get('placeholder') || me.get('label')
}).on('change', function(e) {
if (e.added) {
Ember.set(me.findOption(e.added.id), 'selected', 'selected');
}
else if (e.removed) {
Ember.set(me.findOption(e.removed.id), 'selected', false);
}
});
},
findOption(value) {
return this.get('options').find(option => option.value == value);
}
});

View File

@ -11,7 +11,6 @@ export default BaseFormInput.extend({
},
didInsertElement() {
let me = this;
me._super(...arguments);
me.$('input[type=text]').ace_spinner({
value: me.getVal(),
min: me.get('min'),

View File

@ -13,8 +13,8 @@ export default BaseFormInput.extend({
didReceiveAttrs() {
let me = this;
let isFile = me.get('type') === 'file';
!me.get('inputClass') &&
me.set('inputClass', isFile ? 'col-xs-3' : 'col-xs-12 col-sm-5');
!me.get('input-class') &&
me.set('input-class', isFile ? 'col-xs-3' : 'col-xs-12 col-sm-5');
let image = me.get('image');
if (isFile && image) {
me.set('imageUrl', me.get('model.' + image));

View File

@ -0,0 +1,27 @@
import Ember from 'ember';
export default Ember.Component.extend({
step: 1,
value: 0,
classNames: ['ace-spinner', 'middle'],
actions: {
increase() {
let me = this;
let max = me.get('max');
if (!Ember.isNone(max) && me.get('value') == max) {
Ember.Logger.info('Spinner increase to max: ', max);
return;
}
this.incrementProperty('value');
},
decrease() {
let me = this;
let min = me.get('min');
if (!Ember.isNone(min) && me.get('value') == min) {
Ember.Logger.info('Spinner decrease to min: ', min);
return;
}
this.decrementProperty('value');
}
}
});

View File

@ -2,7 +2,7 @@ import Ember from 'ember';
import BaseComponentMixin from '../mixins/components/base-component';
export default Ember.Component.extend(BaseComponentMixin, {
classNames: ['widget-toolbar', 'no-border'],
classNames: ['widget-toolbar', 'no-border', 'no-padding'],
searchText: Ember.computed.oneWay('route.controller.search'),
actions: {
search() {

View File

@ -10,6 +10,7 @@ export default Ember.Component.extend(BaseComponentMixin, {
asc: Ember.computed.equal('order', 'asc'),
desc: Ember.computed.equal('order', 'desc'),
// classNameBindings: ['asc:sorting_asc', 'desc:sorting_desc', 'sorting:sorting'],
attributeBindings: ['style'],
getDir() {
let me = this;
let name = me.get('name');

View File

@ -4,6 +4,8 @@ import BaseComponentMixin from '../mixins/components/base-component';
export default Ember.Component.extend(BaseComponentMixin, {
tagName: 'span',
classNames: ['cursor-pointer'],
'dialog-title': 'Data Filter',
'full-width': false,
didReceiveAttrs() {
let me = this;
let filters = me.getFilters()[me.get('name')];

View File

@ -0,0 +1,16 @@
import Ember from 'ember';
const WeekGoalCompletionRateComponent = Ember.Component.extend({
rate: Ember.computed('goal', 'goal.goal', 'goal.done', function() {
let me = this;
let goal = me.get('goal.goal');
let done = me.get('goal.done');
let result = ((done * 1.0 / goal * 1.0) * 100).toFixed(2);
return result > 0 ? (result + '%').replace(/\.00%$/g, '%') : 0;
})
});
WeekGoalCompletionRateComponent.reopenClass({
positionalParams: ['goal'],
});
export default WeekGoalCompletionRateComponent;

View File

@ -0,0 +1,17 @@
import Ember from 'ember';
const WeekGoalCompletionRateComponent = Ember.Component.extend({
tagName: '',
rate: Ember.computed('goal', 'goal.goal', 'goal.done', function() {
let me = this;
let goal = me.get('goal.goal');
let done = me.get('goal.done');
let result = ((done * 1.0 / goal * 1.0) * 100).toFixed(2);
return result > 0 ? (result + '%').replace(/\.00%$/g, '%') : 0;
})
});
WeekGoalCompletionRateComponent.reopenClass({
positionalParams: ['goal'],
});
export default WeekGoalCompletionRateComponent;

View File

@ -0,0 +1,23 @@
import Ember from 'ember';
const WeekGoalTotalCompletionRateComponent = Ember.Component.extend({
tagName: '',
totalGoal: Ember.computed('goals.@each.goal', function() {
return this.get('goals').map(it => it.goal).reduce((pv, g) => pv + parseInt(g), 0);
}),
totalDone: Ember.computed('goals.@each.done', function() {
return this.get('goals').map(it => it.done).reduce((pv, g) => pv + parseInt(g), 0);
}),
totalRate: Ember.computed('totalGoal', 'totalDone', function() {
let me = this;
let goal = me.get('totalGoal');
let done = me.get('totalDone');
let result = ((done * 1.0 / goal * 1.0) * 100).toFixed(2);
return result > 0 ? (result + '%').replace(/\.00%$/g, '%') : 0;
})
});
WeekGoalTotalCompletionRateComponent.reopenClass({
positionalParams: ['goals'],
});
export default WeekGoalTotalCompletionRateComponent;

View File

@ -0,0 +1,13 @@
import Ember from 'ember';
const WeekGoalTotalGoalComponent = Ember.Component.extend({
tagName: '',
totalDone: Ember.computed('goals.@each.done', function() {
return this.get('goals').map(it => it.done).reduce((pv, g) => pv + parseInt(g), 0);
})
});
WeekGoalTotalGoalComponent.reopenClass({
positionalParams: ['goals'],
});
export default WeekGoalTotalGoalComponent;

View File

@ -0,0 +1,13 @@
import Ember from 'ember';
const WeekGoalTotalGoalComponent = Ember.Component.extend({
tagName: '',
totalGoal: Ember.computed('goals.@each.goal', function() {
return this.get('goals').map(it => it.goal).reduce((pv, g) => pv + parseInt(g), 0);
})
});
WeekGoalTotalGoalComponent.reopenClass({
positionalParams: ['goals'],
});
export default WeekGoalTotalGoalComponent;

View File

@ -49,6 +49,10 @@ Router.map(function() {
this.route('create', {path: '/:customerId/create'});
this.route('edit', {path: '/:id/edit'});
});
this.route('week-goal', function() {
this.route('list');
});
});
export default Router;

View File

@ -0,0 +1,20 @@
import Ember from 'ember';
import BaseListRoute from './../base-list';
export default BaseListRoute.extend({
queryParams: {
filters: {
refreshModel: true
}
},
breadcrumbs: [{text: 'Week Goal'}],
setupController(controller) {
let me = this;
me._super(...arguments);
controller.set('quarters', [
{value: 1, text: '1', selected: true},
{value: 2, text: '2', selected: true},
{value: 3, text: '3', selected: true},
{value: 4, text: '4', selected: true}]);
}
});

View File

@ -0,0 +1,7 @@
import Ember from 'ember';
import BaseService from '../service';
export default BaseService.extend({
modelName: 'WeekGoal',
pageSize: 64
});

View File

@ -51,4 +51,26 @@
max-height: 48px;
overflow: auto;
cursor: pointer;
}
.table > thead > tr > th {
white-space: nowrap;
}
[data-ember-action] {
cursor: pointer;
}
.table > thead > tr > th,
.table > tbody > tr > th,
.table > tfoot > tr > th
/*
,
.table > thead > tr > td,
.table > tbody > tr > td,
.table > tfoot > tr > td
*/
{
padding: 4px !important;
font-size: 16px;
}

View File

@ -1,14 +1,27 @@
{{#if isEditing}}
{{input type="text"
name=model.id
placeholder=placeholder
class="col-xs-12"
value=(mut (get model field))
focus-out='doUpdate'
insert-newline='doUpdate'
}}
{{#if (eq type 'text')}}
{{input type='text'
name=model.id
placeholder=placeholder
class='col-xs-12'
value=(mut (get model field))
focus-out='doUpdate'
insert-newline='doUpdate'
}}
{{else if (eq type 'number')}}
{{input-spinner name=model.id
max=max
min=min
step=step
value=(mut (get model field))
}}
<button class="btn btn-sm btn-success" type="button" {{action 'doUpdate'}}>
<i class="ace-icon fa fa-check bigger-110"></i>
Submit
</button>
{{/if}}
{{else}}
<span class="col-xs-12" {{action 'doEdit'}} style="height: 2em;">
<div class="col-xs-12 no-padding" {{action 'doEdit'}} style="height: 1em;">
{{get model field}}
</span>
</div>
{{/if}}

View File

@ -1,15 +0,0 @@
<label class="col-xs-12 col-sm-3 col-md-3 control-label no-padding-right"> {{label}} </label>
<div class="col-xs-12 col-sm-5 no-padding-right">
<div class="row col-xs-12 no-padding">
<div class="col-xs-{{colWidth}} no-padding-right">
<select multiple={{multiple}}
data-placeholder={{if placeholder placeholder label}}
class="form-control chosen-select">
{{#each options as |option|}}
<option value="{{option.value}}" selected={{option.selected}}>{{option.text}}</option>
{{/each}}
</select>
</div>
</div>
</div>
{{form-input-errors-msg name=name}}

View File

@ -1,11 +1,19 @@
<label class="col-xs-12 col-sm-3 col-md-3 control-label no-padding-right"> {{label}} </label>
<div class="col-xs-12 col-sm-5 no-padding-right">
{{#if label}}
<label for="{{if name name idField}}" class="{{get this 'label-class'}} control-label no-padding-right"> {{label}} </label>
{{/if}}
<div class="{{get this 'input-class'}}">
<div class="row col-xs-12 no-padding">
<div class="col-xs-{{colWidth}} no-padding-right">
{{#x-select class='form-control' name=name value=(mut (get model name)) as |xs|}}
<div class="col-xs-{{get this 'col-width'}} no-padding-right">
{{#x-select class='form-control'
name=name
value=(mut (get dataModel name))
action='onOptionChanged'
as |xs|}}
{{yield xs}}
{{/x-select}}
</div>
</div>
</div>
{{form-input-errors-msg name=name}}
{{#if (get this 'error-msg')}}
{{form-input-errors-msg name=name}}
{{/if}}

View File

@ -0,0 +1,19 @@
<label for="{{name}}" class="{{get this 'label-class'}} control-label no-padding-right"> {{label}} </label>
<div class="{{get this 'input-class'}} no-padding-right">
<div class="row col-xs-12 no-padding">
<div class="col-xs-{{get this 'col-width'}} no-padding-right">
<select name=name
multiple={{multiple}}
data-placeholder={{if placeholder placeholder label}}
class="select2"
style="width: 100%;">
{{#each options as |option|}}
<option value={{option.value}} selected={{option.selected}}>{{option.text}}</option>
{{/each}}
</select>
</div>
</div>
</div>
{{#if (get this 'error-msg')}}
{{form-input-errors-msg name=name}}
{{/if}}

View File

@ -1,4 +1,6 @@
{{#if label}}
<label class="col-xs-3 control-label no-padding-right"> {{label}} </label>
{{/if}}
<div class="col-xs-3">
{{input class='col-xs-12'
type='text'

View File

@ -1,31 +1,32 @@
<label for="{{name}}" class="col-xs-12 col-sm-3 col-md-3 control-label no-padding-right"> {{label}} </label>
<label for="{{if name name idField}}" class="{{get this 'label-class'}} control-label no-padding-right"> {{label}} </label>
<div class="{{get this 'input-class'}}">
<div class="{{inputClass}}">
{{#if hasBlock}}
{{yield}}
{{!else}}
{{else if (eq 'file' type)}}
{{else}}
{{#if (eq 'file' type)}}
{{input class='col-xs-12' type='file' name=name}}
{{else if (eq 'textarea' type)}}
{{textarea name=name
class='col-xs-12 form-control textarea-resize-vertical'
value=(mut (get model name))
{{textarea class='col-xs-12'
name=name
value=(mut (get dataModel name))
class='form-control'
placeholder=(if placeholder placeholder label)}}
{{else if (eq 'show' type)}}
<h5 class="grey">{{get model name}}</h5>
{{else}}
{{input class='col-xs-12 width-100'
type=type
readonly=readonly
name=name
placeholder=(if placeholder placeholder label)
value=(mut (get model name))}}
value=(mut (get dataModel name))}}
{{/if}}
{{!/if}}
{{/if}}
</div>
{{#if imageUrl}}
<div class="col-xs-2 padding-top-3 no-padding-left" style="padding-top: 3px;">
{{image-previews previews=imageUrl}}
</div>
{{/if}}
{{form-input-errors-msg name=name}}
{{#if (get this 'error-msg')}}
{{form-input-errors-msg name=name}}
{{/if}}

View File

@ -0,0 +1,11 @@
<div class="input-group">
{{input type='number' class='spinbox-input form-control text-center' name=name value=value min=min max=max}}
<div class="spinbox-buttons input-group-btn btn-group-vertical">
<button type="button" class="btn spinbox-up btn-sm btn-info" {{action 'increase'}}>
<i class="icon-only ace-icon fa fa-chevron-up"></i>
</button>
<button type="button" class="btn spinbox-down btn-sm btn-info" {{action 'decrease'}}>
<i class="icon-only ace-icon fa fa-chevron-down"></i>
</button>
</div>
</div>

View File

@ -45,6 +45,13 @@
<span class="menu-text"> Customers </span>
{{/link-to}}
</li>
<li>
{{#link-to 'week-goal.list'}}
<i class="menu-icon fa fa-calendar-check-o blue" aria-hidden="true"></i>
<span class="menu-text"> Week Goal </span>
{{/link-to}}
</li>
{{#if ajax.user.admin}}
<li>
{{#link-to 'customer-status.list' 1}}

View File

@ -1,3 +1,5 @@
<div class="main-content-inner">
{{yield}}
<div class="row">
{{yield}}
</div>
</div>

View File

@ -7,11 +7,4 @@
focus-out='search'
}}
<i class="ace-icon fa fa-search nav-search-icon"></i>
</span>
{{!--
<div class="inline" style="margin-top:0">
<a class="btn btn-xs btn-info" style="margin-bottom: 3px;">
<i class="ace-icon fa fa-search bigger-120"></i>
</a>
</div>
--}}
</span>

View File

@ -1,6 +1,6 @@
{{#if order}}
<a {{action 'removeSort'}} style="cursor: pointer;">
<i class="ace-icon fa fa-times red2"></i>
<i class="ace-icon fa fa-times red2 bigger-110"></i>
</a>
{{/if}}
@ -14,10 +14,10 @@
<a {{action 'sort'}} class="pull-right" style="cursor: pointer;">
{{#if asc}}
<i class="ace-icon fa fa-sort-asc"></i>
<i class="ace-icon fa fa-sort-asc bigger-110"></i>
{{else if desc}}
<i class="ace-icon fa fa-sort-desc"></i>
<i class="ace-icon fa fa-sort-desc bigger-110"></i>
{{else}}
<i class="ace-icon fa fa-sort"></i>
<i class="ace-icon fa fa-sort bigger-110"></i>
{{/if}}
</a>

View File

@ -1,12 +1,13 @@
<span {{action 'onClick'}} data-rel="tooltip" title="Filter">
<span class="{{if (get this 'full-width') 'col-xs-12 no-padding'}}" data-rel="tooltip" title="Filter" {{action 'onClick'}}>
{{text}}
</span>
{{#if showModal}}
{{#modal-dialog title='Data Filter' submit=(action 'onModalSubmit') on-close=(action 'onModalClose') transitionToParentRouteAfterClose=false}}
{{#modal-dialog title=(get this 'dialog-title') submit=(action 'onModalSubmit') on-close=(action 'onModalClose') transitionToParentRouteAfterClose=false}}
<div class="widget-body">
<div class="widget-main">
<form class="form-horizontal">
{{form-input-chosen-select multiple=true col-width=12 label=(if label label text) options=options}}
{{!form-input-chosen-select multiple=true col-width=12 label=(if label label text) options=options}}
{{form-input-select2 multiple=true col-width=12 label=(if label label text) options=options}}
</form>
</div>
</div>

View File

@ -0,0 +1 @@
{{rate}}

View File

@ -0,0 +1 @@
{{rate}}

View File

@ -0,0 +1 @@
{{totalRate}}

View File

@ -0,0 +1 @@
{{totalDone}}

View File

@ -0,0 +1 @@
{{totalGoal}}

View File

@ -10,7 +10,7 @@
<div class="widget-body">
<!-- #section:custom/scrollbar -->
<div class="widget-main no-padding">
<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>

View File

@ -35,8 +35,8 @@
<div class="widget-body">
<!-- #section:custom/scrollbar -->
<div class="widget-main no-padding">
<table class="table table-striped table-bordered table-hover dataTable" style="border: 1px solid #ddd;">
<div class="widget-main no-padding table-responsive no-border">
<table class="table table-striped table-bordered table-hover" style="border: 1px solid #ddd;">
<thead class="thin-border-bottom">
<tr>
{{#if tableOptions.showId}}
@ -55,21 +55,21 @@
{{sortable-th name='sumYtdSales' text='Sales'}}
{{/if}}
{{#if tableOptions.showCountryCode}}
<th class="hidden-480">Country</th>
<th>Country</th>
{{/if}}
{{#if tableOptions.showState}}
<th class="hidden-480">State</th>
<th>State</th>
{{/if}}
{{#if tableOptions.showCity}}
<th class="hidden-480">City</th>
<th>City</th>
{{/if}}
{{#if tableOptions.showMs}}
{{#sortable-th name='ms' class='hidden-480'}}
{{#sortable-th name='ms'}}
{{th-filter name='ms' text='MS' label='MS Filter' options=model.msList}}
{{/sortable-th}}
{{/if}}
{{#if tableOptions.showRegion}}
{{#sortable-th name='region' text='Region' class='hidden-480'}}
{{#sortable-th name='region' text='Region'}}
{{th-filter name='region' text='Region' label='Region Filter' options=model.regionList}}
{{/sortable-th}}
{{/if}}
@ -79,18 +79,18 @@
{{/sortable-th}}
{{/if}}
{{#if tableOptions.showStatus}}
<th class="hidden-480">
<th>
{{th-filter name='status' text='Status' label='Status Filter' options=model.statusList}}
</th>
{{/if}}
{{#if tableOptions.showIssue1}}
<th class="hidden-480">Comment 1</th>
<th>Comment 1</th>
{{/if}}
{{#if tableOptions.showIssue2}}
<th class="hidden-480">Comment 2</th>
<th>Comment 2</th>
{{/if}}
{{#if tableOptions.showIssue3}}
<th class="hidden-480">Comment 3</th>
<th>Comment 3</th>
{{/if}}
<th>
<i class="ace-icon fa fa-cogs bigger-110 hidden-480"></i>
@ -121,11 +121,14 @@
{{/if}}
{{#if tableOptions.showYears}}
<td>
{{!--
{{#if (eq it.years.length 1)}}
{{it.years.firstObject}}
{{else if (gt it.years.length 1)}}
{{it.years.firstObject}}-{{it.years.lastObject}}
{{/if}}
--}}
{{it.years.lastObject}}
</td>
{{/if}}
{{#if tableOptions.showYtdSale}}
@ -134,11 +137,11 @@
{{#if it.sumYtdSales}}
{{#if it.expand}}
<a style="cursor: pointer;" {{action (route-action 'collapseYtdSales' it)}}>
<i class="ui-icon ace-icon fa fa-minus center blue"></i>
<i class="ui-icon ace-icon fa fa-minus center blue bigger-110"></i>
</a>
{{else}}
<a style="cursor: pointer;" {{action (route-action 'expandYtdSales' it)}}>
<i class="ui-icon ace-icon fa fa-plus center blue"></i>
<i class="ui-icon ace-icon fa fa-plus center blue bigger-110"></i>
</a>
{{/if}}
{{/if}}
@ -162,12 +165,12 @@
</td>
{{/if}}
{{#if tableOptions.showMs}}
<td class="hidden-480">
<td>
{{it.ms}}
</td>
{{/if}}
{{#if tableOptions.showRegion}}
<td class="hidden-480">
<td>
{{it.region}}
</td>
{{/if}}

View File

@ -10,29 +10,29 @@
<div class="widget-body">
<!-- #section:custom/scrollbar -->
<div class="widget-main no-padding">
<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>
{{sortable-th name='employeeId' text='Employee ID'}}
{{sortable-th name='account' text='Account' class="hidden-480"}}
{{sortable-th name='employeeId' text='Emp ID' style='min-width: 105px;'}}
{{sortable-th name='account' text='Acct'}}
<th>
Name
</th>
{{sortable-th name='enName' text='Name(EN)' class="hidden-480"}}
{{sortable-th name='enName' text='Name(EN)' style='min-width: 130px;'}}
<th>
Admin
</th>
<th class="hidden-480">
<i class="ace-icon fa fa-sticky-note-o bigger-110 hidden-480"></i>
<th>
<i class="ace-icon fa fa-sticky-note-o bigger-110"></i>
Remark
</th>
<th class="hidden-480">
<i class="ace-icon fa fa-exchange bigger-110 hidden-480"></i>
<th>
<i class="ace-icon fa fa-exchange bigger-110"></i>
Status
</th>
<th>
<i class="ace-icon fa fa-cogs bigger-110 hidden-480"></i>
<i class="ace-icon fa fa-cogs bigger-110"></i>
Settings
</th>
</tr>
@ -44,22 +44,22 @@
<td>
{{it.employeeId}}
</td>
<td class="hidden-480">
<td>
{{it.account}}
</td>
<td>
{{it.name}}
</td>
<td class="hidden-480">
<td>
{{it.enName}}
</td>
<td>
{{status-cell model=it field='admin' enabledText='YES' disabledText='NO'}}
</td>
<td class="hidden-480">
<td>
{{editable-cell model=it field='note'}}
</td>
<td class="hidden-480">
<td>
{{status-cell model=it field='enabled' enabledText='ACTIVE' disabledText='BLOCKED'}}
</td>
<td>

View File

@ -0,0 +1,71 @@
{{#main-content}}
<div class="widget-box transparent">
{{grid-header}}
<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 full-width=true name='quarter' text='Week' label='Quarter' options=quarters}}
</th>
<th>
Goal
</th>
<th>
Done
</th>
<th>
Rate
</th>
<th>
<i class="ace-icon fa fa-sticky-note-o bigger-110"></i> Remark
</th>
</tr>
</thead>
<tbody>
{{#each model.data as |it|}}
<tr>
<td>
{{date-cell value=it.dateStart format='M.D'}} - {{date-cell value=it.dateEnd format='M.D'}}
</td>
<td>
{{editable-cell type='number' model=it field='goal' min=0}}
</td>
<td>
{{editable-cell type='number' model=it field='done' min=0 max=it.goal}}
</td>
<td>
{{week-goal/completion-rate it}}
</td>
<td>
{{editable-cell model=it field='note'}}
</td>
</tr>
{{/each}}
<tr>
<td>
Total
</td>
<td>
{{week-goal/total-goal model.data}}
</td>
<td>
{{week-goal/total-done model.data}}
</td>
<td>
{{week-goal/total-completion-rate model.data}}
</td>
<td>
</td>
</tr>
</tbody>
</table>
</div>
{{!pagination-bar}}
</div>
</div>
{{/main-content}}

View File

@ -18,9 +18,7 @@
"jquery-colorbox": "^1.6.4",
"fuelux": "^3.15.4",
"jquery.hotkeys": "*",
"bootstrap-wysiwyg": "*",
"bootstrap-treeview": "^1.2.0",
"chosen": "^1.7.0"
"bootstrap-wysiwyg": "*"
},
"resolutions": {
"ember": "release"

View File

@ -51,7 +51,6 @@ module.exports = function(defaults) {
}
}
let bd = app.bowerDirectory;
importVendor(app, 'bower_components/bootstrap/dist/css/bootstrap.css');
// importVendor(app, 'bower_components/bootstrap/dist/css/bootstrap-theme.css');
importVendor(app, 'bower_components/bootstrap/dist/js/bootstrap.js');
@ -82,14 +81,6 @@ module.exports = function(defaults) {
destDir: '/assets/fonts'
});
// jquery choosen
importVendor(app, 'bower_components/chosen/chosen.jquery.js');
importVendor(app, 'bower_components/chosen/chosen.css');
let chosenAssets = new Funnel('bower_components/chosen', {
include: ['*.png'],
destDir: '/assets/css'
});
// bootstrap color picker
importVendor(app, 'bower_components/mjolnic-bootstrap-colorpicker/dist/css/bootstrap-colorpicker.min.css');
importVendor(app, 'bower_components/mjolnic-bootstrap-colorpicker/dist/js/bootstrap-colorpicker.min.js');
@ -98,6 +89,15 @@ module.exports = function(defaults) {
destDir: '/assets/img'
});
// Select2, must before ace
importVendor(app, 'vendor/ace/js/select2.js');
importVendor(app, 'vendor/ace/css/select2.css');
let select2Images = new Funnel('vendor/ace/css', {
include: ['select2-spinner.gif', 'select2.png', 'select2x2.png'],
destDir: '/assets/css'
});
// ACE CSS
importVendor(app, 'vendor/ace/css/ace.css');
importVendor(app, 'vendor/ace/css/ace-fonts.css');
@ -151,10 +151,10 @@ module.exports = function(defaults) {
return app.toTree([bootstrapAssets,
jqColorboxAssets,
faAssets,
chosenAssets,
bcpAssets,
aceFonts,
aceImages,
select2Images,
publicJs
]);
};

View File

@ -1,7 +1,7 @@
import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';
moduleForComponent('form-input-chosen-select', 'Integration | Component | form input chosen select', {
moduleForComponent('form-input-select2', 'Integration | Component | form input select2', {
integration: true
});
@ -10,15 +10,15 @@ test('it renders', function(assert) {
// Set any properties with this.set('myProperty', 'value');
// Handle any actions with this.on('myAction', function(val) { ... });
this.render(hbs`{{form-input-chosen-select}}`);
this.render(hbs`{{form-input-select2}}`);
assert.equal(this.$().text().trim(), '');
// Template block usage:
this.render(hbs`
{{#form-input-chosen-select}}
{{#form-input-select2}}
template block text
{{/form-input-chosen-select}}
{{/form-input-select2}}
`);
assert.equal(this.$().text().trim(), 'template block text');

View File

@ -0,0 +1,25 @@
import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';
moduleForComponent('input-spinner', 'Integration | Component | input spinner', {
integration: true
});
test('it renders', function(assert) {
// Set any properties with this.set('myProperty', 'value');
// Handle any actions with this.on('myAction', function(val) { ... });
this.render(hbs`{{input-spinner}}`);
assert.equal(this.$().text().trim(), '');
// Template block usage:
this.render(hbs`
{{#input-spinner}}
template block text
{{/input-spinner}}
`);
assert.equal(this.$().text().trim(), 'template block text');
});

View File

@ -0,0 +1,25 @@
import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';
moduleForComponent('week-goal-completion-rate', 'Integration | Component | week goal completion rate', {
integration: true
});
test('it renders', function(assert) {
// Set any properties with this.set('myProperty', 'value');
// Handle any actions with this.on('myAction', function(val) { ... });
this.render(hbs`{{week-goal-completion-rate}}`);
assert.equal(this.$().text().trim(), '');
// Template block usage:
this.render(hbs`
{{#week-goal-completion-rate}}
template block text
{{/week-goal-completion-rate}}
`);
assert.equal(this.$().text().trim(), 'template block text');
});

View File

@ -0,0 +1,25 @@
import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';
moduleForComponent('week-goal/completion-rate', 'Integration | Component | week goal/completion rate', {
integration: true
});
test('it renders', function(assert) {
// Set any properties with this.set('myProperty', 'value');
// Handle any actions with this.on('myAction', function(val) { ... });
this.render(hbs`{{week-goal/completion-rate}}`);
assert.equal(this.$().text().trim(), '');
// Template block usage:
this.render(hbs`
{{#week-goal/completion-rate}}
template block text
{{/week-goal/completion-rate}}
`);
assert.equal(this.$().text().trim(), 'template block text');
});

View File

@ -0,0 +1,25 @@
import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';
moduleForComponent('week-goal/total-completion-rate', 'Integration | Component | week goal/total completion rate', {
integration: true
});
test('it renders', function(assert) {
// Set any properties with this.set('myProperty', 'value');
// Handle any actions with this.on('myAction', function(val) { ... });
this.render(hbs`{{week-goal/total-completion-rate}}`);
assert.equal(this.$().text().trim(), '');
// Template block usage:
this.render(hbs`
{{#week-goal/total-completion-rate}}
template block text
{{/week-goal/total-completion-rate}}
`);
assert.equal(this.$().text().trim(), 'template block text');
});

View File

@ -0,0 +1,25 @@
import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';
moduleForComponent('week-goal/total-done', 'Integration | Component | week goal/total done', {
integration: true
});
test('it renders', function(assert) {
// Set any properties with this.set('myProperty', 'value');
// Handle any actions with this.on('myAction', function(val) { ... });
this.render(hbs`{{week-goal/total-done}}`);
assert.equal(this.$().text().trim(), '');
// Template block usage:
this.render(hbs`
{{#week-goal/total-done}}
template block text
{{/week-goal/total-done}}
`);
assert.equal(this.$().text().trim(), 'template block text');
});

View File

@ -0,0 +1,25 @@
import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';
moduleForComponent('week-goal/total-goal', 'Integration | Component | week goal/total goal', {
integration: true
});
test('it renders', function(assert) {
// Set any properties with this.set('myProperty', 'value');
// Handle any actions with this.on('myAction', function(val) { ... });
this.render(hbs`{{week-goal/total-goal}}`);
assert.equal(this.$().text().trim(), '');
// Template block usage:
this.render(hbs`
{{#week-goal/total-goal}}
template block text
{{/week-goal/total-goal}}
`);
assert.equal(this.$().text().trim(), 'template block text');
});

View File

@ -0,0 +1,11 @@
import { moduleFor, test } from 'ember-qunit';
moduleFor('route:week-goal/list', 'Unit | Route | week goal/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:week-goal/service', 'Unit | Service | week goal/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);
});