Prechádzať zdrojové kódy

many custom fields model and UI enhancements

Pouyan Savoli 7 rokov pred
rodič
commit
afd87e3caa

+ 2 - 2
client/components/activities/activities.js

@@ -92,8 +92,8 @@ BlazeComponent.extendComponent({
   },
   },
 
 
   customField() {
   customField() {
-    const customField = this.currentData().customFieldId;
-    return customField;
+    const customField = this.currentData().customField();
+    return customField.name;
   },
   },
 
 
   events() {
   events() {

+ 1 - 1
client/components/boards/boardHeader.jade

@@ -103,7 +103,7 @@ template(name="boardHeaderBar")
 
 
 template(name="boardMenuPopup")
 template(name="boardMenuPopup")
   ul.pop-over-list
   ul.pop-over-list
-    li: a.js-custom-fields {{_ 'custom-fields'}}
+    li: a.js-configure-custom-fields {{_ 'configure-custom-fields'}}
     li: a.js-open-archives {{_ 'archived-items'}}
     li: a.js-open-archives {{_ 'archived-items'}}
     if currentUser.isBoardAdmin
     if currentUser.isBoardAdmin
       li: a.js-change-board-color {{_ 'board-change-color'}}
       li: a.js-change-board-color {{_ 'board-change-color'}}

+ 1 - 1
client/components/boards/boardHeader.js

@@ -1,6 +1,6 @@
 Template.boardMenuPopup.events({
 Template.boardMenuPopup.events({
   'click .js-rename-board': Popup.open('boardChangeTitle'),
   'click .js-rename-board': Popup.open('boardChangeTitle'),
-  'click .js-custom-fields'() {
+  'click .js-configure-custom-fields'() {
     Sidebar.setView('customFields');
     Sidebar.setView('customFields');
     Popup.close();
     Popup.close();
   },
   },

+ 15 - 0
client/components/cards/cardDetails.jade

@@ -107,6 +107,7 @@ template(name="cardDetailsActionsPopup")
       li: a.js-members {{_ 'card-edit-members'}}
       li: a.js-members {{_ 'card-edit-members'}}
       li: a.js-labels {{_ 'card-edit-labels'}}
       li: a.js-labels {{_ 'card-edit-labels'}}
       li: a.js-attachments {{_ 'card-edit-attachments'}}
       li: a.js-attachments {{_ 'card-edit-attachments'}}
+      li: a.js-custom-fields {{_ 'card-edit-custom-fields'}}
       li: a.js-start-date {{_ 'editCardStartDatePopup-title'}}
       li: a.js-start-date {{_ 'editCardStartDatePopup-title'}}
       li: a.js-due-date {{_ 'editCardDueDatePopup-title'}}
       li: a.js-due-date {{_ 'editCardDueDatePopup-title'}}
     hr
     hr
@@ -143,6 +144,20 @@ template(name="cardMembersPopup")
           if isCardMember
           if isCardMember
             i.fa.fa-check
             i.fa.fa-check
 
 
+template(name="cardCustomFieldsPopup")
+  ul.pop-over-list
+    each board.customFields
+      li.item(class="")
+        a.name.js-select-field(href="#")
+          span.full-name
+            = name
+          if isCardMember
+            i.fa.fa-check
+  hr
+  a.quiet-button.full.js-configure-custom-fields
+    i.fa.fa-cog
+    span {{_ 'configure-custom-fields'}}
+
 template(name="cardMorePopup")
 template(name="cardMorePopup")
   p.quiet
   p.quiet
     span.clearfix
     span.clearfix

+ 15 - 0
client/components/cards/cardDetails.js

@@ -154,6 +154,7 @@ Template.cardDetailsActionsPopup.events({
   'click .js-members': Popup.open('cardMembers'),
   'click .js-members': Popup.open('cardMembers'),
   'click .js-labels': Popup.open('cardLabels'),
   'click .js-labels': Popup.open('cardLabels'),
   'click .js-attachments': Popup.open('cardAttachments'),
   'click .js-attachments': Popup.open('cardAttachments'),
+  'click .js-custom-fields': Popup.open('cardCustomFields'),
   'click .js-start-date': Popup.open('editCardStartDate'),
   'click .js-start-date': Popup.open('editCardStartDate'),
   'click .js-due-date': Popup.open('editCardDueDate'),
   'click .js-due-date': Popup.open('editCardDueDate'),
   'click .js-move-card': Popup.open('moveCard'),
   'click .js-move-card': Popup.open('moveCard'),
@@ -196,6 +197,20 @@ Template.editCardTitleForm.events({
   },
   },
 });
 });
 
 
+Template.cardCustomFieldsPopup.events({
+  'click .js-select-field'(evt) {
+    const card = Cards.findOne(Session.get('currentCard'));
+    const customFieldId = this.customFieldId;
+    card.toggleCustomField(customFieldId);
+    evt.preventDefault();
+  },
+  'click .js-configure-custom-fields'(evt) {
+    EscapeActions.executeUpTo('detailsPane');
+    Sidebar.setView('customFields');
+    evt.preventDefault();
+  }
+});
+
 Template.moveCardPopup.events({
 Template.moveCardPopup.events({
   'click .js-select-list' () {
   'click .js-select-list' () {
     // XXX We should *not* get the currentCard from the global state, but
     // XXX We should *not* get the currentCard from the global state, but

+ 1 - 1
client/components/sidebar/sidebar.js

@@ -5,7 +5,7 @@ const defaultView = 'home';
 const viewTitles = {
 const viewTitles = {
   filter: 'filter-cards',
   filter: 'filter-cards',
   multiselection: 'multi-selection',
   multiselection: 'multi-selection',
-  customFields: 'custom-fields',
+  customFields: 'configure-custom-fields',
   archives: 'archives',
   archives: 'archives',
 };
 };
 
 

+ 39 - 22
client/components/sidebar/sidebar.styl

@@ -45,28 +45,45 @@
       display: flex
       display: flex
       flex-direction: column
       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
     .sidebar-btn
       display: block
       display: block

+ 31 - 13
client/components/sidebar/sidebarCustomFields.jade

@@ -1,31 +1,49 @@
 template(name="customFieldsSidebar")
 template(name="customFieldsSidebar")
     ul.sidebar-list
     ul.sidebar-list
-        each customsFields
+        each customFields
             li
             li
-                a.name
-                    span.sidebar-list-item-description
-                        {{_ 'some name'}}
+                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
     if currentUser.isBoardMember
         hr
         hr
         a.sidebar-btn.js-open-create-custom-field
         a.sidebar-btn.js-open-create-custom-field
             i.fa.fa-plus
             i.fa.fa-plus
-            span {{_ 'Create Custom Field'}}
+            span {{_ 'createCustomField'}}
 
 
 template(name="createCustomFieldPopup")
 template(name="createCustomFieldPopup")
     form
     form
         label
         label
             | {{_ 'name'}}
             | {{_ 'name'}}
-            input.js-field-name(type="text" name="field-name" autofocus)
+            unless _id
+                input.js-field-name(type="text" name="field-name" autofocus)
+            else
+                input.js-field-name(type="text" name="field-name" value=name)
+
         label
         label
             | {{_ 'type'}}
             | {{_ 'type'}}
-            select.js-field-type(name="field-type")
-                option(value="string") String
-                option(value="number") Number
-                option(value="checkbox") Checkbox
-                option(value="date") Date
-                option(value="DropdownList") Dropdown List
+            select.js-field-type(name="field-type" disabled="{{#if _id}}disabled{{/if}}")
+                each types
+                    if selected
+                        option(value=type selected="selected") {{name}}
+                    else
+                        option(value=type) {{name}}
         a.flex.js-field-show-on-card
         a.flex.js-field-show-on-card
             .materialCheckBox(class="{{#if showOnCard}}is-checked{{/if}}")
             .materialCheckBox(class="{{#if showOnCard}}is-checked{{/if}}")
 
 
             span {{_ 'show-field-on-card'}}
             span {{_ 'show-field-on-card'}}
-        input.primary.wide(type="submit" value="{{_ 'save'}}")
+        button.primary.wide.left(type="submit")
+            | {{_ 'save'}}
+        if _id
+            button.negate.wide.right.js-delete-custom-field
+                | {{_ 'delete'}}
+
+template(name="editCustomFieldPopup")
+    | {{> createCustomFieldPopup}}
+
+template(name="deleteCustomFieldPopup")
+    p {{_ "custom-field-delete-pop"}}
+    button.js-confirm.negate.full(type="submit") {{_ 'delete'}}

+ 25 - 11
client/components/sidebar/sidebarCustomFields.js

@@ -9,21 +9,22 @@ BlazeComponent.extendComponent({
   events() {
   events() {
     return [{
     return [{
       'click .js-open-create-custom-field': Popup.open('createCustomField'),
       'click .js-open-create-custom-field': Popup.open('createCustomField'),
-      'click .js-edit-custom-field'() {
-        // todo
-      },
-      'click .js-delete-custom-field': Popup.afterConfirm('customFieldDelete', function() {
-        const customFieldId = this._id;
-        CustomFields.remove(customFieldId);
-        Popup.close();
-      }),
+      'click .js-edit-custom-field': Popup.open('editCustomField'),
     }];
     }];
   },
   },
 
 
 }).register('customFieldsSidebar');
 }).register('customFieldsSidebar');
 
 
 Template.createCustomFieldPopup.helpers({
 Template.createCustomFieldPopup.helpers({
-
+  types() {
+    var currentType = this.type;
+    return ['text', 'number', 'checkbox', 'date', 'dropdown'].
+      map(type => {return {
+        type: type,
+        name: TAPi18n.__('custom-field-' + type),
+        selected: type == currentType,
+      }});
+  },
 });
 });
 
 
 Template.createCustomFieldPopup.events({
 Template.createCustomFieldPopup.events({
@@ -41,7 +42,7 @@ Template.createCustomFieldPopup.events({
     const name = tpl.find('.js-field-name').value.trim();
     const name = tpl.find('.js-field-name').value.trim();
     const type = tpl.find('.js-field-type').value.trim();
     const type = tpl.find('.js-field-type').value.trim();
     const showOnCard = tpl.find('.js-field-show-on-card.is-checked') != null;
     const showOnCard = tpl.find('.js-field-show-on-card.is-checked') != null;
-    //console.log("Create",name,type,showOnCard);
+    //console.log('Create',name,type,showOnCard);
 
 
     CustomFields.insert({
     CustomFields.insert({
       boardId: Session.get('currentBoard'),
       boardId: Session.get('currentBoard'),
@@ -52,4 +53,17 @@ Template.createCustomFieldPopup.events({
 
 
     Popup.back();
     Popup.back();
   },
   },
-});
+  'click .js-delete-custom-field': Popup.afterConfirm('deleteCustomField', function() {
+    const customFieldId = this._id;
+    CustomFields.remove(customFieldId);
+    Popup.close();
+  }),
+});
+
+/*Template.deleteCustomFieldPopup.events({
+  'submit'(evt) {
+    const customFieldId = this._id;
+    CustomFields.remove(customFieldId);
+    Popup.close();
+  }
+});*/

+ 13 - 4
i18n/en.i18n.json

@@ -103,6 +103,7 @@
     "card-due": "Due",
     "card-due": "Due",
     "card-due-on": "Due on",
     "card-due-on": "Due on",
     "card-edit-attachments": "Edit attachments",
     "card-edit-attachments": "Edit attachments",
+    "card-edit-custom-fields": "Edit custom fields",
     "card-edit-labels": "Edit labels",
     "card-edit-labels": "Edit labels",
     "card-edit-members": "Edit members",
     "card-edit-members": "Edit members",
     "card-labels-title": "Change the labels for the card.",
     "card-labels-title": "Change the labels for the card.",
@@ -110,6 +111,7 @@
     "card-start": "Start",
     "card-start": "Start",
     "card-start-on": "Starts on",
     "card-start-on": "Starts on",
     "cardAttachmentsPopup-title": "Attach From",
     "cardAttachmentsPopup-title": "Attach From",
+    "cardCustomFieldsPopup-title": "Edit custom fields",
     "cardDeletePopup-title": "Delete Card?",
     "cardDeletePopup-title": "Delete Card?",
     "cardDetailsActionsPopup-title": "Card Actions",
     "cardDetailsActionsPopup-title": "Card Actions",
     "cardLabelsPopup-title": "Labels",
     "cardLabelsPopup-title": "Labels",
@@ -153,16 +155,22 @@
     "create": "Create",
     "create": "Create",
     "createBoardPopup-title": "Create Board",
     "createBoardPopup-title": "Create Board",
     "chooseBoardSourcePopup-title": "Import board",
     "chooseBoardSourcePopup-title": "Import board",
+    "configure-custom-fields": "Configure Custom Fields",
     "createLabelPopup-title": "Create Label",
     "createLabelPopup-title": "Create Label",
-    "createCustomField": "Create Custom Field",
-    "createCustomFieldPopup-title": "Create Custom Field",
+    "createCustomField": "Create Field",
+    "createCustomFieldPopup-title": "Create Field",
     "current": "current",
     "current": "current",
-    "custom-fields": "Custom Fields",
-    "customFieldDeletePopup-title": "Delete Card?",
+    "custom-field-delete-pop": "There is no undo. This will remove this custom field from all cards and destroy its history.",
+    "custom-field-checkbox": "Checkbox",
+    "custom-field-date": "Date",
+    "custom-field-dropdown": "Dropdown List",
+    "custom-field-number": "Number",
+    "custom-field-text": "Text",
     "date": "Date",
     "date": "Date",
     "decline": "Decline",
     "decline": "Decline",
     "default-avatar": "Default avatar",
     "default-avatar": "Default avatar",
     "delete": "Delete",
     "delete": "Delete",
+    "deleteCustomFieldPopup-title": "Delete Custom Field?",
     "deleteLabelPopup-title": "Delete Label?",
     "deleteLabelPopup-title": "Delete Label?",
     "description": "Description",
     "description": "Description",
     "disambiguateMultiLabelPopup-title": "Disambiguate Label Action",
     "disambiguateMultiLabelPopup-title": "Disambiguate Label Action",
@@ -175,6 +183,7 @@
     "edit-profile": "Edit Profile",
     "edit-profile": "Edit Profile",
     "editCardStartDatePopup-title": "Change start date",
     "editCardStartDatePopup-title": "Change start date",
     "editCardDueDatePopup-title": "Change due date",
     "editCardDueDatePopup-title": "Change due date",
+    "editCustomFieldPopup-title": "Edit Field",
     "editLabelPopup-title": "Change Label",
     "editLabelPopup-title": "Change Label",
     "editNotificationPopup-title": "Edit Notification",
     "editNotificationPopup-title": "Edit Notification",
     "editProfilePopup-title": "Edit Profile",
     "editProfilePopup-title": "Edit Profile",

+ 4 - 0
models/boards.js

@@ -235,6 +235,10 @@ Boards.helpers({
     return `board-color-${this.color}`;
     return `board-color-${this.color}`;
   },
   },
 
 
+  customFields() {
+    return CustomFields.find({ boardId: this._id }, { sort: { name: 1 } });
+  },
+
   // XXX currently mutations return no value so we have an issue when using addLabel in import
   // XXX currently mutations return no value so we have an issue when using addLabel in import
   // XXX waiting on https://github.com/mquandalle/meteor-collection-mutations/issues/1 to remove...
   // XXX waiting on https://github.com/mquandalle/meteor-collection-mutations/issues/1 to remove...
   pushLabel(name, color) {
   pushLabel(name, color) {

+ 9 - 12
models/customFields.js

@@ -25,7 +25,7 @@ CustomFields.allow({
   remove(userId, doc) {
   remove(userId, doc) {
     return allowIsBoardMember(userId, Boards.findOne(doc.boardId));
     return allowIsBoardMember(userId, Boards.findOne(doc.boardId));
   },
   },
-  fetch: ['boardId'],
+  fetch: ['userId', 'boardId'],
 });
 });
 
 
 // not sure if we need this?
 // not sure if we need this?
@@ -41,21 +41,18 @@ function customFieldCreation(userId, doc){
 }
 }
 
 
 if (Meteor.isServer) {
 if (Meteor.isServer) {
-  // Comments are often fetched within a card, so we create an index to make these
-  // queries more efficient.
-  Meteor.startup(() => {
-    CardComments._collection._ensureIndex({ cardId: 1, createdAt: -1 });
-  });
+  /*Meteor.startup(() => {
+    CustomFields._collection._ensureIndex({ boardId: 1});
+  });*/
 
 
   CustomFields.after.insert((userId, doc) => {
   CustomFields.after.insert((userId, doc) => {
     customFieldCreation(userId, doc);
     customFieldCreation(userId, doc);
   });
   });
 
 
   CustomFields.after.remove((userId, doc) => {
   CustomFields.after.remove((userId, doc) => {
-    const activity = Activities.findOne({ customFieldId: doc._id });
-    if (activity) {
-      Activities.remove(activity._id);
-    }
+    Activities.remove({
+      customFieldId: doc._id,
+    });
   });
   });
 }
 }
 
 
@@ -70,7 +67,7 @@ if (Meteor.isServer) {
     });
     });
   });
   });
 
 
-  JsonRoutes.add('GET', '/api/boards/:boardId/comments/:customFieldId', function (req, res, next) {
+  JsonRoutes.add('GET', '/api/boards/:boardId/custom-fields/:customFieldId', function (req, res, next) {
     Authentication.checkUserId( req.userId);
     Authentication.checkUserId( req.userId);
     const paramBoardId = req.params.boardId;
     const paramBoardId = req.params.boardId;
     const paramCustomFieldId = req.params.customFieldId;
     const paramCustomFieldId = req.params.customFieldId;
@@ -90,7 +87,7 @@ if (Meteor.isServer) {
       boardId: paramBoardId,
       boardId: paramBoardId,
     });
     });
 
 
-    const customField = CustomFields.findOne({_id: id, cardId:paramCardId, boardId: paramBoardId });
+    const customField = CustomFields.findOne({_id: id, boardId: paramBoardId });
     customFieldCreation(req.body.authorId, customField);
     customFieldCreation(req.body.authorId, customField);
 
 
     JsonRoutes.sendResult(res, {
     JsonRoutes.sendResult(res, {

+ 1 - 0
server/publications/boards.js

@@ -74,6 +74,7 @@ Meteor.publishRelations('board', function(boardId) {
   }, { limit: 1 }), function(boardId, board) {
   }, { limit: 1 }), function(boardId, board) {
     this.cursor(Lists.find({ boardId }));
     this.cursor(Lists.find({ boardId }));
     this.cursor(Integrations.find({ boardId }));
     this.cursor(Integrations.find({ boardId }));
+    this.cursor(CustomFields.find({ boardId }, { sort: { name: 1 } }));
 
 
     // Cards and cards comments
     // Cards and cards comments
     // XXX Originally we were publishing the card documents as a child of the
     // XXX Originally we were publishing the card documents as a child of the