diff options
author | Lauri Ojansivu <x@xet7.org> | 2018-05-18 16:22:53 +0300 |
---|---|---|
committer | Lauri Ojansivu <x@xet7.org> | 2018-05-18 16:22:53 +0300 |
commit | 50f751e0a9f8214d690f6fd39136d09a46a8e69a (patch) | |
tree | 3a0e58883030abbfa8eb454d181356a8be299d79 /client/components | |
parent | 61b240d126bcce7adcdb303c5b01a202dc7fa395 (diff) | |
parent | 1984a6b1c80dbd8b4eff89aa92913f943bacc63b (diff) | |
download | wekan-50f751e0a9f8214d690f6fd39136d09a46a8e69a.tar.gz wekan-50f751e0a9f8214d690f6fd39136d09a46a8e69a.tar.bz2 wekan-50f751e0a9f8214d690f6fd39136d09a46a8e69a.zip |
Merge branch 'devel' into greenkeeper/initial
Diffstat (limited to 'client/components')
21 files changed, 552 insertions, 62 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..aa50caec --- /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, 10); + 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'); 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..55ee8d32 100644 --- a/client/components/cards/cardDetails.jade +++ b/client/components/cards/cardDetails.jade @@ -66,6 +66,13 @@ template(name="cardDetails") i.fa.fa-plus .card-details-items + 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 if isOvertime @@ -144,6 +151,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..7dbe8732 100644 --- a/client/components/cards/cardDetails.styl +++ b/client/components/cards/cardDetails.styl @@ -69,6 +69,7 @@ .card-details-items display: flex + flex-wrap: wrap margin: 15px 0 .card-details-item @@ -80,9 +81,10 @@ &.card-details-item-received, &.card-details-item-start, &.card-details-item-due, - &.card-details-item-end - width: 50% - flex-shrink: 1 + &.card-details-item-end, + &.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..4bf7b369 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..e56d744e --- /dev/null +++ b/client/components/sidebar/sidebarCustomFields.js @@ -0,0 +1,131 @@ +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', '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() { + const 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() { + const settings = {}; + switch (this.type.get()) { + case 'dropdown': { + const 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) { + const 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(); + } +});*/ |