summaryrefslogtreecommitdiffstats
path: root/client
diff options
context:
space:
mode:
Diffstat (limited to 'client')
-rw-r--r--client/components/activities/activities.jade3
-rw-r--r--client/components/activities/activities.js5
-rw-r--r--client/components/boards/boardHeader.jade1
-rw-r--r--client/components/boards/boardHeader.js4
-rw-r--r--client/components/cards/cardCustomFields.jade76
-rw-r--r--client/components/cards/cardCustomFields.js179
-rw-r--r--client/components/cards/cardDate.jade16
-rw-r--r--client/components/cards/cardDate.js4
-rw-r--r--client/components/cards/cardDate.styl19
-rw-r--r--client/components/cards/cardDetails.jade17
-rw-r--r--client/components/cards/cardDetails.js1
-rw-r--r--client/components/cards/cardDetails.styl8
-rw-r--r--client/components/forms/datepicker.jade15
-rw-r--r--client/components/forms/datepicker.styl17
-rw-r--r--client/components/forms/forms.styl3
-rw-r--r--client/components/lists/listBody.js7
-rw-r--r--client/components/main/popup.styl3
-rw-r--r--client/components/sidebar/sidebar.js1
-rw-r--r--client/components/sidebar/sidebar.styl61
-rw-r--r--client/components/sidebar/sidebarCustomFields.jade52
-rw-r--r--client/components/sidebar/sidebarCustomFields.js130
-rw-r--r--client/lib/datepicker.js86
22 files changed, 647 insertions, 61 deletions
diff --git a/client/components/activities/activities.jade b/client/components/activities/activities.jade
index 2054777a..d3e3d5ba 100644
--- a/client/components/activities/activities.jade
+++ b/client/components/activities/activities.jade
@@ -53,6 +53,9 @@ template(name="boardActivities")
if($eq activityType 'createCard')
| {{{_ 'activity-added' cardLink boardLabel}}}.
+ if($eq activityType 'createCustomField')
+ | {{_ 'activity-customfield-created' customField}}.
+
if($eq activityType 'createList')
| {{_ 'activity-added' list.title boardLabel}}.
diff --git a/client/components/activities/activities.js b/client/components/activities/activities.js
index ccb064f3..95699961 100644
--- a/client/components/activities/activities.js
+++ b/client/components/activities/activities.js
@@ -91,6 +91,11 @@ BlazeComponent.extendComponent({
}, attachment.name()));
},
+ customField() {
+ const customField = this.currentData().customField();
+ return customField.name;
+ },
+
events() {
return [{
// XXX We should use Popup.afterConfirmation here
diff --git a/client/components/boards/boardHeader.jade b/client/components/boards/boardHeader.jade
index fe0771cb..b4ccd3b3 100644
--- a/client/components/boards/boardHeader.jade
+++ b/client/components/boards/boardHeader.jade
@@ -113,6 +113,7 @@ template(name="boardHeaderBar")
template(name="boardMenuPopup")
ul.pop-over-list
+ li: a.js-custom-fields {{_ 'custom-fields'}}
li: a.js-open-archives {{_ 'archived-items'}}
if currentUser.isBoardAdmin
li: a.js-change-board-color {{_ 'board-change-color'}}
diff --git a/client/components/boards/boardHeader.js b/client/components/boards/boardHeader.js
index 2b587831..e0b19246 100644
--- a/client/components/boards/boardHeader.js
+++ b/client/components/boards/boardHeader.js
@@ -1,5 +1,9 @@
Template.boardMenuPopup.events({
'click .js-rename-board': Popup.open('boardChangeTitle'),
+ 'click .js-custom-fields'() {
+ Sidebar.setView('customFields');
+ Popup.close();
+ },
'click .js-open-archives'() {
Sidebar.setView('archives');
Popup.close();
diff --git a/client/components/cards/cardCustomFields.jade b/client/components/cards/cardCustomFields.jade
new file mode 100644
index 00000000..65081e3b
--- /dev/null
+++ b/client/components/cards/cardCustomFields.jade
@@ -0,0 +1,76 @@
+template(name="cardCustomFieldsPopup")
+ ul.pop-over-list
+ each board.customFields
+ li.item(class="")
+ a.name.js-select-field(href="#")
+ span.full-name
+ = name
+ if hasCustomField
+ i.fa.fa-check
+ hr
+ a.quiet-button.full.js-settings
+ i.fa.fa-cog
+ span {{_ 'settings'}}
+
+template(name="cardCustomField")
+ +Template.dynamic(template=getTemplate)
+
+template(name="cardCustomField-text")
+ if canModifyCard
+ +inlinedForm(classNames="js-card-customfield-text")
+ +editor(autofocus=true)
+ = value
+ .edit-controls.clearfix
+ button.primary(type="submit") {{_ 'save'}}
+ a.fa.fa-times-thin.js-close-inlined-form
+ else
+ a.js-open-inlined-form
+ if value
+ +viewer
+ = value
+ else
+ | {{_ 'edit'}}
+
+template(name="cardCustomField-number")
+ if canModifyCard
+ +inlinedForm(classNames="js-card-customfield-number")
+ input(type="number" value=data.value)
+ .edit-controls.clearfix
+ button.primary(type="submit") {{_ 'save'}}
+ a.fa.fa-times-thin.js-close-inlined-form
+ else
+ a.js-open-inlined-form
+ if value
+ = value
+ else
+ | {{_ 'edit'}}
+
+template(name="cardCustomField-date")
+ if canModifyCard
+ a.js-edit-date(title="{{showTitle}}" class="{{classes}}")
+ if value
+ div.card-date
+ time(datetime="{{showISODate}}")
+ | {{showDate}}
+ else
+ | {{_ 'edit'}}
+
+template(name="cardCustomField-dropdown")
+ if canModifyCard
+ +inlinedForm(classNames="js-card-customfield-dropdown")
+ select.inline
+ each items
+ if($eq data.value this._id)
+ option(value=_id selected="selected") {{name}}
+ else
+ option(value=_id) {{name}}
+ .edit-controls.clearfix
+ button.primary(type="submit") {{_ 'save'}}
+ a.fa.fa-times-thin.js-close-inlined-form
+ else
+ a.js-open-inlined-form
+ if value
+ +viewer
+ = selectedItem
+ else
+ | {{_ 'edit'}} \ No newline at end of file
diff --git a/client/components/cards/cardCustomFields.js b/client/components/cards/cardCustomFields.js
new file mode 100644
index 00000000..e014de4a
--- /dev/null
+++ b/client/components/cards/cardCustomFields.js
@@ -0,0 +1,179 @@
+Template.cardCustomFieldsPopup.helpers({
+ hasCustomField() {
+ const card = Cards.findOne(Session.get('currentCard'));
+ const customFieldId = this._id;
+ return card.customFieldIndex(customFieldId) > -1;
+ },
+});
+
+Template.cardCustomFieldsPopup.events({
+ 'click .js-select-field'(evt) {
+ const card = Cards.findOne(Session.get('currentCard'));
+ const customFieldId = this._id;
+ card.toggleCustomField(customFieldId);
+ evt.preventDefault();
+ },
+ 'click .js-settings'(evt) {
+ EscapeActions.executeUpTo('detailsPane');
+ Sidebar.setView('customFields');
+ evt.preventDefault();
+ }
+});
+
+// cardCustomField
+const CardCustomField = BlazeComponent.extendComponent({
+
+ getTemplate() {
+ return 'cardCustomField-' + this.data().definition.type;
+ },
+
+ onCreated() {
+ const self = this;
+ self.card = Cards.findOne(Session.get('currentCard'));
+ self.customFieldId = this.data()._id;
+ },
+
+ canModifyCard() {
+ return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly();
+ },
+});
+CardCustomField.register('cardCustomField');
+
+// cardCustomField-text
+(class extends CardCustomField {
+
+ onCreated() {
+ super.onCreated();
+ }
+
+ events() {
+ return [{
+ 'submit .js-card-customfield-text'(evt) {
+ evt.preventDefault();
+ const value = this.currentComponent().getValue();
+ this.card.setCustomField(this.customFieldId, value);
+ },
+ }];
+ }
+
+}).register('cardCustomField-text');
+
+// cardCustomField-number
+(class extends CardCustomField {
+
+ onCreated() {
+ super.onCreated();
+ }
+
+ events() {
+ return [{
+ 'submit .js-card-customfield-number'(evt) {
+ evt.preventDefault();
+ const value = parseInt(this.find('input').value);
+ this.card.setCustomField(this.customFieldId, value);
+ },
+ }];
+ }
+
+}).register('cardCustomField-number');
+
+// cardCustomField-date
+(class extends CardCustomField {
+
+ onCreated() {
+ super.onCreated();
+ const self = this;
+ self.date = ReactiveVar();
+ self.now = ReactiveVar(moment());
+ window.setInterval(() => {
+ self.now.set(moment());
+ }, 60000);
+
+ self.autorun(() => {
+ self.date.set(moment(self.data().value));
+ });
+ }
+
+ showDate() {
+ // this will start working once mquandalle:moment
+ // is updated to at least moment.js 2.10.5
+ // until then, the date is displayed in the "L" format
+ return this.date.get().calendar(null, {
+ sameElse: 'llll',
+ });
+ }
+
+ showISODate() {
+ return this.date.get().toISOString();
+ }
+
+ classes() {
+ if (this.date.get().isBefore(this.now.get(), 'minute') &&
+ this.now.get().isBefore(this.data().value)) {
+ return 'current';
+ }
+ return '';
+ }
+
+ showTitle() {
+ return `${TAPi18n.__('card-start-on')} ${this.date.get().format('LLLL')}`;
+ }
+
+ events() {
+ return [{
+ 'click .js-edit-date': Popup.open('cardCustomField-date'),
+ }];
+ }
+
+}).register('cardCustomField-date');
+
+// cardCustomField-datePopup
+(class extends DatePicker {
+ onCreated() {
+ super.onCreated();
+ const self = this;
+ self.card = Cards.findOne(Session.get('currentCard'));
+ self.customFieldId = this.data()._id;
+ this.data().value && this.date.set(moment(this.data().value));
+ }
+
+ _storeDate(date) {
+ this.card.setCustomField(this.customFieldId, date);
+ }
+
+ _deleteDate() {
+ this.card.setCustomField(this.customFieldId, '');
+ }
+}).register('cardCustomField-datePopup');
+
+// cardCustomField-dropdown
+(class extends CardCustomField {
+
+ onCreated() {
+ super.onCreated();
+ this._items = this.data().definition.settings.dropdownItems;
+ this.items = this._items.slice(0);
+ this.items.unshift({
+ _id: "",
+ name: TAPi18n.__('custom-field-dropdown-none')
+ });
+ }
+
+ selectedItem() {
+ const selected = this._items.find((item) => {
+ return item._id == this.data().value;
+ });
+ return (selected) ? selected.name : TAPi18n.__('custom-field-dropdown-unknown');
+ }
+
+ events() {
+ return [{
+ 'submit .js-card-customfield-dropdown'(evt) {
+ evt.preventDefault();
+ const value = this.find('select').value;
+ this.card.setCustomField(this.customFieldId, value);
+ },
+ }];
+ }
+
+}).register('cardCustomField-dropdown'); \ No newline at end of file
diff --git a/client/components/cards/cardDate.jade b/client/components/cards/cardDate.jade
index 525f27ed..2e447506 100644
--- a/client/components/cards/cardDate.jade
+++ b/client/components/cards/cardDate.jade
@@ -1,19 +1,3 @@
-template(name="editCardDate")
- .edit-card-date
- form.edit-date
- .fields
- .left
- label(for="date") {{_ 'date'}}
- input.js-date-field#date(type="text" name="date" value=showDate placeholder=dateFormat autofocus)
- .right
- label(for="time") {{_ 'time'}}
- input.js-time-field#time(type="text" name="time" value=showTime placeholder=timeFormat)
- .js-datepicker
- if error.get
- .warning {{_ error.get}}
- button.primary.wide.left.js-submit-date(type="submit") {{_ 'save'}}
- button.js-delete-date.negate.wide.right.js-delete-date {{_ 'delete'}}
-
template(name="dateBadge")
if canModifyCard
a.js-edit-date.card-date(title="{{showTitle}}" class="{{classes}}")
diff --git a/client/components/cards/cardDate.js b/client/components/cards/cardDate.js
index 7c0ad6ab..f33e8c19 100644
--- a/client/components/cards/cardDate.js
+++ b/client/components/cards/cardDate.js
@@ -110,7 +110,7 @@ Template.dateBadge.helpers({
// editCardStartDatePopup
-(class extends EditCardDate {
+(class extends DatePicker {
onCreated() {
super.onCreated();
this.data().startAt && this.date.set(moment(this.data().startAt));
@@ -133,7 +133,7 @@ Template.dateBadge.helpers({
}).register('editCardStartDatePopup');
// editCardDueDatePopup
-(class extends EditCardDate {
+(class extends DatePicker {
onCreated() {
super.onCreated();
this.data().dueAt && this.date.set(moment(this.data().dueAt));
diff --git a/client/components/cards/cardDate.styl b/client/components/cards/cardDate.styl
index 1ad3adb3..9775e82b 100644
--- a/client/components/cards/cardDate.styl
+++ b/client/components/cards/cardDate.styl
@@ -1,22 +1,3 @@
-.edit-card-date
- .fields
- .left
- width: 56%
- .right
- width: 38%
- .datepicker
- width: 100%
- table
- width: 100%
- border: none
- border-spacing: 0
- border-collapse: collapse
- thead
- background: none
- td, th
- box-sizing: border-box
-
-
.card-date
display: block
border-radius: 4px
diff --git a/client/components/cards/cardDetails.jade b/client/components/cards/cardDetails.jade
index 047d7518..b888210b 100644
--- a/client/components/cards/cardDetails.jade
+++ b/client/components/cards/cardDetails.jade
@@ -65,6 +65,22 @@ template(name="cardDetails")
a.card-label.add-label.js-add-labels(title="{{_ 'card-labels-title'}}")
i.fa.fa-plus
+ if startAt
+ .card-details-item.card-details-item-start
+ h3.card-details-item-title {{_ 'card-start'}}
+ +cardStartDate
+
+ if dueAt
+ .card-details-item.card-details-item-due
+ h3.card-details-item-title {{_ 'card-due'}}
+ +cardDueDate
+
+ each customFieldsWD
+ .card-details-item.card-details-item-customfield
+ h3.card-details-item-title
+ = definition.name
+ +cardCustomField
+
.card-details-items
if spentTime
.card-details-item.card-details-item-spent
@@ -144,6 +160,7 @@ template(name="cardDetailsActionsPopup")
li: a.js-labels {{_ 'card-edit-labels'}}
li: a.js-attachments {{_ 'card-edit-attachments'}}
li: a.js-received-date {{_ 'editCardReceivedDatePopup-title'}}
+ li: a.js-custom-fields {{_ 'card-edit-custom-fields'}}
li: a.js-start-date {{_ 'editCardStartDatePopup-title'}}
li: a.js-due-date {{_ 'editCardDueDatePopup-title'}}
li: a.js-end-date {{_ 'editCardEndDatePopup-title'}}
diff --git a/client/components/cards/cardDetails.js b/client/components/cards/cardDetails.js
index cdd027e6..26549fda 100644
--- a/client/components/cards/cardDetails.js
+++ b/client/components/cards/cardDetails.js
@@ -216,6 +216,7 @@ Template.cardDetailsActionsPopup.events({
'click .js-labels': Popup.open('cardLabels'),
'click .js-attachments': Popup.open('cardAttachments'),
'click .js-received-date': Popup.open('editCardReceivedDate'),
+ 'click .js-custom-fields': Popup.open('cardCustomFields'),
'click .js-start-date': Popup.open('editCardStartDate'),
'click .js-due-date': Popup.open('editCardDueDate'),
'click .js-end-date': Popup.open('editCardEndDate'),
diff --git a/client/components/cards/cardDetails.styl b/client/components/cards/cardDetails.styl
index e5739a93..e18c07a1 100644
--- a/client/components/cards/cardDetails.styl
+++ b/client/components/cards/cardDetails.styl
@@ -69,10 +69,11 @@
.card-details-items
display: flex
- margin: 15px 0
+ flex-wrap: wrap
+ margin: 0 0 15px
.card-details-item
- margin-right: 0.5em
+ margin: 15px 0.5em 0 0
&:last-child
margin-right: 0
&.card-details-item-labels,
@@ -83,6 +84,9 @@
&.card-details-item-end
width: 50%
flex-shrink: 1
+ &.card-details-item-customfield
+ max-width: 50%
+ flex-grow: 1
.card-details-item-title
font-size: 16px
diff --git a/client/components/forms/datepicker.jade b/client/components/forms/datepicker.jade
new file mode 100644
index 00000000..96f63bc4
--- /dev/null
+++ b/client/components/forms/datepicker.jade
@@ -0,0 +1,15 @@
+template(name="datepicker")
+ .datepicker-container
+ form.edit-date
+ .fields
+ .left
+ label(for="date") {{_ 'date'}}
+ input.js-date-field#date(type="text" name="date" value=showDate placeholder=dateFormat autofocus)
+ .right
+ label(for="time") {{_ 'time'}}
+ input.js-time-field#time(type="text" name="time" value=showTime placeholder=timeFormat)
+ .js-datepicker
+ if error.get
+ .warning {{_ error.get}}
+ button.primary.wide.left.js-submit-date(type="submit") {{_ 'save'}}
+ button.js-delete-date.negate.wide.right.js-delete-date {{_ 'delete'}} \ No newline at end of file
diff --git a/client/components/forms/datepicker.styl b/client/components/forms/datepicker.styl
new file mode 100644
index 00000000..a2558094
--- /dev/null
+++ b/client/components/forms/datepicker.styl
@@ -0,0 +1,17 @@
+.datepicker-container
+ .fields
+ .left
+ width: 56%
+ .right
+ width: 38%
+ .datepicker
+ width: 100%
+ table
+ width: 100%
+ border: none
+ border-spacing: 0
+ border-collapse: collapse
+ thead
+ background: none
+ td, th
+ box-sizing: border-box \ No newline at end of file
diff --git a/client/components/forms/forms.styl b/client/components/forms/forms.styl
index 1947c11d..0a905943 100644
--- a/client/components/forms/forms.styl
+++ b/client/components/forms/forms.styl
@@ -85,6 +85,9 @@ select
width: 256px
margin-bottom: 8px
+ &.inline
+ width: 100%
+
option[disabled]
color: #8c8c8c
diff --git a/client/components/lists/listBody.js b/client/components/lists/listBody.js
index 52f34fab..24e5cf5d 100644
--- a/client/components/lists/listBody.js
+++ b/client/components/lists/listBody.js
@@ -35,6 +35,10 @@ BlazeComponent.extendComponent({
const members = formComponent.members.get();
const labelIds = formComponent.labels.get();
+ const customFields = formComponent.customFields.get();
+ console.log("members", members);
+ console.log("labelIds", labelIds);
+ console.log("customFields", customFields);
const boardId = this.data().board()._id;
let swimlaneId = '';
@@ -49,6 +53,7 @@ BlazeComponent.extendComponent({
title,
members,
labelIds,
+ customFields,
listId: this.data()._id,
boardId: this.data().board()._id,
sort: sortIndex,
@@ -146,11 +151,13 @@ BlazeComponent.extendComponent({
onCreated() {
this.labels = new ReactiveVar([]);
this.members = new ReactiveVar([]);
+ this.customFields = new ReactiveVar([]);
},
reset() {
this.labels.set([]);
this.members.set([]);
+ this.customFields.set([]);
},
getLabels() {
diff --git a/client/components/main/popup.styl b/client/components/main/popup.styl
index b7c9e264..ff00eef3 100644
--- a/client/components/main/popup.styl
+++ b/client/components/main/popup.styl
@@ -33,6 +33,9 @@ $popupWidth = 300px
textarea
height: 72px
+ form a span
+ padding: 0 0.5rem
+
.header
height: 36px
position: relative
diff --git a/client/components/sidebar/sidebar.js b/client/components/sidebar/sidebar.js
index bff96dcb..5a9de74b 100644
--- a/client/components/sidebar/sidebar.js
+++ b/client/components/sidebar/sidebar.js
@@ -6,6 +6,7 @@ const viewTitles = {
filter: 'filter-cards',
search: 'search-cards',
multiselection: 'multi-selection',
+ customFields: 'custom-fields',
archives: 'archives',
};
diff --git a/client/components/sidebar/sidebar.styl b/client/components/sidebar/sidebar.styl
index 8f2f493e..740186b5 100644
--- a/client/components/sidebar/sidebar.styl
+++ b/client/components/sidebar/sidebar.styl
@@ -45,28 +45,45 @@
display: flex
flex-direction: column
- li > a
- display: flex
- height: 30px
- margin: 0
- padding: 4px
- border-radius: 3px
- align-items: center
-
- &:hover
- &, i, .quiet
- color white
-
- .member, .card-label
- margin-right: 7px
- margin-top: 5px
-
- .sidebar-list-item-description
- flex: 1
- overflow: ellipsis
-
- .fa.fa-check
- margin: 0 4px
+ li
+ & > a
+ display: flex
+ height: 30px
+ margin: 0
+ padding: 4px
+ border-radius: 3px
+ align-items: center
+
+ &:hover
+ &, i, .quiet
+ color white
+
+ .member, .card-label
+ margin-right: 7px
+ margin-top: 5px
+
+ .minicard-edit-button
+ float: right
+ padding: 8px
+ border-radius: 3px
+
+ .sidebar-list-item-description
+ flex: 1
+ overflow: ellipsis
+
+ .fa.fa-check
+ margin: 0 4px
+
+ .minicard
+ padding: 6px 8px 4px
+
+ .minicard-edit-button
+ float: right
+ padding: 4px
+ border-radius: 3px
+
+ &:hover
+ background: #dbdbdb
.sidebar-btn
display: block
diff --git a/client/components/sidebar/sidebarCustomFields.jade b/client/components/sidebar/sidebarCustomFields.jade
new file mode 100644
index 00000000..def083e9
--- /dev/null
+++ b/client/components/sidebar/sidebarCustomFields.jade
@@ -0,0 +1,52 @@
+template(name="customFieldsSidebar")
+ ul.sidebar-list
+ each customFields
+ li
+ div.minicard-wrapper.js-minicard
+ div.minicard
+ a.fa.fa-pencil.js-edit-custom-field.minicard-edit-button
+ div.minicard-title
+ | {{ name }} ({{ type }})
+
+ if currentUser.isBoardMember
+ hr
+ a.sidebar-btn.js-open-create-custom-field
+ i.fa.fa-plus
+ span {{_ 'createCustomField'}}
+
+template(name="createCustomFieldPopup")
+ form
+ label
+ | {{_ 'name'}}
+ unless _id
+ input.js-field-name(type="text" autofocus)
+ else
+ input.js-field-name(type="text" value=name)
+
+ label
+ | {{_ 'type'}}
+ select.js-field-type(disabled="{{#if _id}}disabled{{/if}}")
+ each types
+ if selected
+ option(value=value selected="selected") {{name}}
+ else
+ option(value=value) {{name}}
+ div.js-field-settings.js-field-settings-dropdown(class="{{#if isTypeNotSelected 'dropdown'}}hide{{/if}}")
+ label
+ | {{_ 'custom-field-dropdown-options'}}
+ each dropdownItems.get
+ input.js-dropdown-item(type="text" value=name placeholder="")
+ input.js-dropdown-item.last(type="text" value="" placeholder="{{_ 'custom-field-dropdown-options-placeholder'}}")
+ a.flex.js-field-show-on-card
+ .materialCheckBox(class="{{#if showOnCard}}is-checked{{/if}}")
+
+ span {{_ 'show-field-on-card'}}
+ button.primary.wide.left(type="button")
+ | {{_ 'save'}}
+ if _id
+ button.negate.wide.right.js-delete-custom-field(type="button")
+ | {{_ 'delete'}}
+
+template(name="deleteCustomFieldPopup")
+ p {{_ "custom-field-delete-pop"}}
+ button.js-confirm.negate.full(type="submit") {{_ 'delete'}} \ No newline at end of file
diff --git a/client/components/sidebar/sidebarCustomFields.js b/client/components/sidebar/sidebarCustomFields.js
new file mode 100644
index 00000000..139b8a42
--- /dev/null
+++ b/client/components/sidebar/sidebarCustomFields.js
@@ -0,0 +1,130 @@
+BlazeComponent.extendComponent({
+
+ customFields() {
+ return CustomFields.find({
+ boardId: Session.get('currentBoard'),
+ });
+ },
+
+ events() {
+ return [{
+ 'click .js-open-create-custom-field': Popup.open('createCustomField'),
+ 'click .js-edit-custom-field': Popup.open('editCustomField'),
+ }];
+ },
+
+}).register('customFieldsSidebar');
+
+const CreateCustomFieldPopup = BlazeComponent.extendComponent({
+
+ _types: ['text', 'number', 'checkbox', 'date', 'dropdown'],
+
+ onCreated() {
+ this.type = new ReactiveVar((this.data().type) ? this.data().type : this._types[0]);
+ this.dropdownItems = new ReactiveVar((this.data().settings && this.data().settings.dropdownItems) ? this.data().settings.dropdownItems : []);
+ },
+
+ types() {
+ const currentType = this.data().type;
+ return this._types.
+ map(type => {return {
+ value: type,
+ name: TAPi18n.__('custom-field-' + type),
+ selected: type == currentType,
+ }});
+ },
+
+ isTypeNotSelected(type) {
+ return this.type.get() !== type;
+ },
+
+ getDropdownItems() {
+ var items = this.dropdownItems.get();
+ Array.from(this.findAll('.js-field-settings-dropdown input')).forEach((el, index) => {
+ //console.log('each item!', index, el.value);
+ if (!items[index]) items[index] = {
+ _id: Random.id(6),
+ };
+ items[index].name = el.value.trim();
+ });
+ return items;
+ },
+
+ getSettings() {
+ let settings = {};
+ switch (this.type.get()) {
+ case 'dropdown':
+ let dropdownItems = this.getDropdownItems().filter(item => !!item.name.trim());
+ settings.dropdownItems = dropdownItems;
+ break;
+ }
+ return settings;
+ },
+
+ events() {
+ return [{
+ 'change .js-field-type'(evt) {
+ const value = evt.target.value;
+ this.type.set(value);
+ },
+ 'keydown .js-dropdown-item.last'(evt) {
+ if (evt.target.value.trim() && evt.keyCode === 13) {
+ let items = this.getDropdownItems();
+ this.dropdownItems.set(items);
+ evt.target.value = '';
+ }
+ },
+ 'click .js-field-show-on-card'(evt) {
+ let $target = $(evt.target);
+ if(!$target.hasClass('js-field-show-on-card')){
+ $target = $target.parent();
+ }
+ $target.find('.materialCheckBox').toggleClass('is-checked');
+ $target.toggleClass('is-checked');
+ },
+ 'click .primary'(evt) {
+ evt.preventDefault();
+
+ const data = {
+ boardId: Session.get('currentBoard'),
+ name: this.find('.js-field-name').value.trim(),
+ type: this.type.get(),
+ settings: this.getSettings(),
+ showOnCard: this.find('.js-field-show-on-card.is-checked') != null
+ }
+
+ // insert or update
+ if (!this.data()._id) {
+ CustomFields.insert(data);
+ } else {
+ CustomFields.update(this.data()._id, {$set: data});
+ }
+
+ Popup.back();
+ },
+ 'click .js-delete-custom-field': Popup.afterConfirm('deleteCustomField', function() {
+ const customFieldId = this._id;
+ CustomFields.remove(customFieldId);
+ Popup.close();
+ }),
+ }];
+ },
+
+});
+CreateCustomFieldPopup.register('createCustomFieldPopup');
+
+(class extends CreateCustomFieldPopup {
+
+ template() {
+ return 'createCustomFieldPopup';
+ }
+
+}).register('editCustomFieldPopup');
+
+/*Template.deleteCustomFieldPopup.events({
+ 'submit'(evt) {
+ const customFieldId = this._id;
+ CustomFields.remove(customFieldId);
+ Popup.close();
+ }
+});*/ \ No newline at end of file
diff --git a/client/lib/datepicker.js b/client/lib/datepicker.js
new file mode 100644
index 00000000..aac061cf
--- /dev/null
+++ b/client/lib/datepicker.js
@@ -0,0 +1,86 @@
+DatePicker = BlazeComponent.extendComponent({
+ template() {
+ return 'datepicker';
+ },
+
+ onCreated() {
+ this.error = new ReactiveVar('');
+ this.card = this.data();
+ this.date = new ReactiveVar(moment.invalid());
+ },
+
+ onRendered() {
+ const $picker = this.$('.js-datepicker').datepicker({
+ todayHighlight: true,
+ todayBtn: 'linked',
+ language: TAPi18n.getLanguage(),
+ }).on('changeDate', function(evt) {
+ this.find('#date').value = moment(evt.date).format('L');
+ this.error.set('');
+ this.find('#time').focus();
+ }.bind(this));
+
+ if (this.date.get().isValid()) {
+ $picker.datepicker('update', this.date.get().toDate());
+ }
+ },
+
+ showDate() {
+ if (this.date.get().isValid())
+ return this.date.get().format('L');
+ return '';
+ },
+ showTime() {
+ if (this.date.get().isValid())
+ return this.date.get().format('LT');
+ return '';
+ },
+ dateFormat() {
+ return moment.localeData().longDateFormat('L');
+ },
+ timeFormat() {
+ return moment.localeData().longDateFormat('LT');
+ },
+
+ events() {
+ return [{
+ 'keyup .js-date-field'() {
+ // parse for localized date format in strict mode
+ const dateMoment = moment(this.find('#date').value, 'L', true);
+ if (dateMoment.isValid()) {
+ this.error.set('');
+ this.$('.js-datepicker').datepicker('update', dateMoment.toDate());
+ }
+ },
+ 'keyup .js-time-field'() {
+ // parse for localized time format in strict mode
+ const dateMoment = moment(this.find('#time').value, 'LT', true);
+ if (dateMoment.isValid()) {
+ this.error.set('');
+ }
+ },
+ 'submit .edit-date'(evt) {
+ evt.preventDefault();
+
+ // if no time was given, init with 12:00
+ const time = evt.target.time.value || moment(new Date().setHours(12, 0, 0)).format('LT');
+
+ const dateString = `${evt.target.date.value} ${time}`;
+ const newDate = moment(dateString, 'L LT', true);
+ if (newDate.isValid()) {
+ this._storeDate(newDate.toDate());
+ Popup.close();
+ }
+ else {
+ this.error.set('invalid-date');
+ evt.target.date.focus();
+ }
+ },
+ 'click .js-delete-date'(evt) {
+ evt.preventDefault();
+ this._deleteDate();
+ Popup.close();
+ },
+ }];
+ },
+}); \ No newline at end of file