2
0
Эх сурвалжийг харах

Initial work on due cards page

John R. Supplee 4 жил өмнө
parent
commit
1abdd5177d

+ 76 - 0
client/components/main/dueCards.jade

@@ -0,0 +1,76 @@
+template(name="dueCardsHeaderBar")
+  h1
+    //a.back-btn(href="{{pathFor 'home'}}")
+    //  i.fa.fa-chevron-left
+    | {{_ 'due-cards'}}
+
+  .board-header-btns.left
+    a.board-header-btn.js-toggle-due-cards-choose-sort(title="{{_ 'due-cards-sort'}}")
+      //i.fa.fa-caret-down
+      i.fa.fa-sort
+      if $eq dueCardsView 'user'
+        i.fa.fa-th-large
+        | {{_ 'due-cards-sort-board'}}
+      if $eq dueCardsView 'all'
+        i.fa.fa-calendar
+        | {{_ 'due-cards-sort-dueat'}}
+
+template(name="dueCardsModalTitle")
+  h2
+    i.fa.fa-keyboard-o
+    | {{_ 'due-cards'}}
+
+template(name="dueCards")
+  .wrapper
+    .due-cards-dueat-list-wrapper
+      each card in dueCardsList
+        .due-cards-card-wrapper
+          a.minicard-wrapper.card-title(href=card.absoluteUrl)
+            +minicard(card)
+          ul.due-cards-context-list
+            li.due-cards-context(title="{{_ 'board'}}")
+              +viewer
+                = card.board.title
+            li.due-cards-context.due-cards-context-separator
+              = ' '
+              | {{_ 'context-separator'}}
+              = ' '
+            li.due-cards-context(title="{{_ 'swimlane'}}")
+              +viewer
+                = card.swimlane.title
+            li.due-cards-context
+              = ' '
+              | {{_ 'context-separator'}}
+              = ' '
+            li.due-cards-context(title="{{_ 'list'}}")
+              +viewer
+                = card.list.title
+
+
+template(name="dueCardsViewChangePopup")
+  ul.pop-over-list
+    li
+      with "due-cards-view-user"
+        a.js-due-cards-sort-board
+          i.fa.fa-th-large.colorful
+          | {{_ 'due-cards-view-user'}}
+          if $eq Utils.dueCardsView "user"
+            i.fa.fa-check
+    li
+      with "due-cards-view-all"
+        a.js-due-cards-sort-dueat
+          i.fa.fa-calendar.colorful
+          | {{_ 'due-cards-view-all'}}
+          if $eq Utils.dueCardsView "all"
+            i.fa.fa-check
+
+//template(name="dueCardsViewChangePopup")
+//  ul.pop-over-list
+//    li
+//      a.js-due-cards-sort-board
+//        i.fa.fa-th-large.colorful
+//        | {{_ 'due-cards-sort-board'}}
+//    li
+//      a.js-due-cards-sort-dueat
+//        i.fa.fa-calendar.colorful
+//        | {{_ 'due-cards-sort-dueat'}}

+ 163 - 0
client/components/main/dueCards.js

@@ -0,0 +1,163 @@
+BlazeComponent.extendComponent({
+  dueCardsView() {
+    // eslint-disable-next-line no-console
+    // console.log('sort:', Utils.dueCardsView());
+    return Utils.dueCardsView();
+  },
+
+  events() {
+    return [
+      {
+        'click .js-toggle-my-cards-choose-sort'() {
+          // eslint-disable-next-line no-console
+          // console.log('open sort');
+          // Popup.open('dueCardsViewChange');
+          Utils.dueCardsViewToggle();
+        },
+      },
+    ];
+  },
+}).register('dueCardsHeaderBar');
+
+Template.dueCards.helpers({
+  userId() {
+    return Meteor.userId();
+  },
+});
+
+BlazeComponent.extendComponent({
+  events() {
+    return [
+      {
+        'click .js-my-cards-sort-board'() {
+          Utils.setMyCardsSort('board');
+          Popup.close();
+        },
+
+        'click .js-my-cards-sort-dueat'() {
+          Utils.setMyCardsSort('dueAt');
+          Popup.close();
+        },
+      },
+    ];
+  },
+}).register('dueCardsViewChangePopup');
+
+BlazeComponent.extendComponent({
+  onCreated() {
+    Meteor.subscribe('setting');
+    Meteor.subscribe('dueCards');
+  },
+
+  dueCardsView() {
+    // eslint-disable-next-line no-console
+    console.log('sort:', Utils.dueCardsView());
+    return Utils.dueCardsView();
+  },
+
+  sortByBoard() {
+    return this.dueCardsView() === 'board';
+  },
+
+  dueCardsList() {
+    const allUsers = false;
+
+    const user = Meteor.user();
+
+    const archivedBoards = [];
+    Boards.find({ archived: true }).forEach(board => {
+      archivedBoards.push(board._id);
+    });
+
+    const permiitedBoards = [];
+    let selector = {
+      archived: false,
+    };
+    // if user is not an admin allow her to see cards only from public boards
+    // or those where she is a member
+    if (!user.isAdmin) {
+      selector.$or = [
+        { permission: 'public' },
+        { members: { $elemMatch: { userId: user._id, isActive: true } } },
+      ];
+    }
+    Boards.find(selector).forEach(board => {
+      permiitedBoards.push(board._id);
+    });
+
+    const archivedSwimlanes = [];
+    Swimlanes.find({ archived: true }).forEach(swimlane => {
+      archivedSwimlanes.push(swimlane._id);
+    });
+
+    const archivedLists = [];
+    Lists.find({ archived: true }).forEach(list => {
+      archivedLists.push(list._id);
+    });
+
+    selector = {
+      archived: false,
+      boardId: {
+        $nin: archivedBoards,
+        $in: permiitedBoards,
+      },
+      swimlaneId: { $nin: archivedSwimlanes },
+      listId: { $nin: archivedLists },
+      dueAt: { $ne: null },
+      endAt: null,
+    };
+
+    if (!allUsers) {
+      selector.$or = [{ members: user._id }, { assignees: user._id }];
+    }
+
+    const cards = [];
+
+    // eslint-disable-next-line no-console
+    // console.log('cards selector:', selector);
+    Cards.find(selector).forEach(card => {
+      cards.push(card);
+      // eslint-disable-next-line no-console
+      // console.log(
+      //   'board:',
+      //   card.board(),
+      //   'swimlane:',
+      //   card.swimlane(),
+      //   'list:',
+      //   card.list(),
+      // );
+    });
+
+    cards.sort((a, b) => {
+      const x = a.dueAt === null ? Date('2100-12-31') : a.dueAt;
+      const y = b.dueAt === null ? Date('2100-12-31') : b.dueAt;
+
+      if (x > y) return 1;
+      else if (x < y) return -1;
+
+      return 0;
+    });
+
+    // eslint-disable-next-line no-console
+    // console.log('cards:', cards);
+    return cards;
+  },
+
+  events() {
+    return [
+      {
+        // 'click .js-my-card'(evt) {
+        //   const card = this.currentData().card;
+        //   // eslint-disable-next-line no-console
+        //   console.log('currentData():', this.currentData());
+        //   // eslint-disable-next-line no-console
+        //   console.log('card:', card);
+        //   if (card) {
+        //     Utils.goCardId(card._id);
+        //   }
+        //   evt.preventDefault();
+        // },
+      },
+    ];
+  },
+}).register('dueCards');

+ 69 - 0
client/components/main/dueCards.styl

@@ -0,0 +1,69 @@
+.due-cards-board-wrapper
+  border-radius: 8px
+  //padding: 0.5rem
+  min-width: 400px
+  border-width: 8px
+  border-color: grey
+  border-style: solid
+  margin-bottom: 2rem
+  margin-right: auto
+  margin-left: auto
+
+.due-cards-board-title
+  font-size: 1.4rem
+  font-weight: bold
+  padding: 0.5rem
+  background-color: grey
+  color: white
+
+.due-cards-swimlane-title
+  font-size: 1.1rem
+  font-weight: bold
+  padding: 0.5rem
+  padding-bottom: 0.4rem
+  margin-top: 0
+  margin-bottom: 0.5rem
+  //border-top: black 1px solid
+  //border-bottom: black 1px solid
+  text-align: center
+
+.swimlane-default-color
+  background-color: lightgrey
+
+.due-cards-list-title
+  font-weight: bold
+  font-size: 1.1rem
+  //padding-bottom: 0
+  //margin-bottom: 0
+  text-align: center
+  margin-bottom: 0.7rem
+
+.due-cards-list-wrapper
+  margin: 1rem
+  border-radius: 5px
+  padding: 1.5rem
+  padding-top: 0.75rem
+  display: inline-block
+  min-width: 250px
+  max-width: 350px
+
+.due-cards-card-wrapper
+  margin-top: 0
+  margin-bottom: 10px
+
+.due-cards-dueat-list-wrapper
+  max-width: 500px
+  margin-right: auto
+  margin-left: auto
+
+.due-cards-field-name
+  font-weight: bold
+
+.due-cards-context
+  display: inline-block
+
+.due-cards-context-separator
+  font-weight: bold
+
+.due-cards-context-list
+  margin-bottom: 0.7rem

+ 7 - 1
client/components/main/myCards.js

@@ -200,7 +200,13 @@ BlazeComponent.extendComponent({
 
     const cards = [];
     cursor.forEach(card => {
-      cards.push(card);
+      if (
+        !card.board().archived &&
+        !card.swimlane().archived &&
+        !card.list().archived
+      ) {
+        cards.push(card);
+      }
     });
 
     cards.sort((a, b) => {

+ 58 - 0
client/lib/utils.js

@@ -69,6 +69,64 @@ Utils = {
     location.reload();
   },
 
+  archivedBoardIds() {
+    const archivedBoards = [];
+    Boards.find({ archived: false }).forEach(board => {
+      archivedBoards.push(board._id);
+    });
+    return archivedBoards;
+  },
+
+  dueCardsView() {
+    let view = window.localStorage.getItem('dueCardsView');
+
+    if (!view || !['user', 'all'].includes(view)) {
+      window.localStorage.setItem('dueCardsView', 'user');
+      location.reload();
+      view = 'user';
+    }
+
+    return view;
+  },
+
+  dueBoardsSelector() {
+    const user = Meteor.user();
+
+    const selector = {
+      archived: false,
+    };
+
+    // if user is not an admin allow her to see cards only from boards where
+    // she is a member
+    if (!user.isAdmin()) {
+      selector.$or = [
+        { permission: 'public' },
+        { members: { $elemMatch: { userId: user._id, isActive: true } } },
+      ];
+    }
+
+    return selector;
+  },
+
+  dueCardsSelector() {
+    const user = Meteor.user();
+
+    const selector = {
+      archived: false,
+    };
+
+    // if user is not an admin allow her to see cards only from boards where
+    // she is a member
+    if (!user.isAdmin()) {
+      selector.$or = [
+        { permission: 'public' },
+        { members: { $elemMatch: { userId: user._id, isActive: true } } },
+      ];
+    }
+
+    return selector;
+  },
+
   // XXX We should remove these two methods
   goBoardId(_id) {
     const board = Boards.findOne(_id);

+ 26 - 0
config/router.js

@@ -139,6 +139,32 @@ FlowRouter.route('/my-cards', {
   },
 });
 
+FlowRouter.route('/due-cards', {
+  name: 'my-cards',
+  action() {
+    const dueCardsTemplate = 'dueCards';
+
+    Filter.reset();
+    // EscapeActions.executeAll();
+    EscapeActions.executeUpTo('popup-close');
+
+    Utils.manageCustomUI();
+    Utils.manageMatomo();
+
+    // if (previousPath) {
+    //   Modal.open(dueCardsTemplate, {
+    //     header: 'dueCardsModalTitle',
+    //     onCloseGoTo: previousPath,
+    //   });
+    // } else {
+    BlazeLayout.render('defaultLayout', {
+      headerBar: 'dueCardsHeaderBar',
+      content: dueCardsTemplate,
+    });
+    // }
+  },
+});
+
 FlowRouter.route('/import/:source', {
   name: 'import',
   triggersEnter: [AccountsTemplates.ensureSignedIn],

+ 84 - 0
server/publications/cards.js

@@ -32,3 +32,87 @@ Meteor.publish('myCards', function() {
     },
   );
 });
+
+Meteor.publish('dueCards', function(allUsers = false) {
+  check(allUsers, Boolean);
+
+  const user = Users.findOne(this.userId);
+
+  const archivedBoards = [];
+  Boards.find({ archived: true }).forEach(board => {
+    archivedBoards.push(board._id);
+  });
+
+  const permiitedBoards = [];
+  let selector = {
+    archived: false,
+  };
+  // if user is not an admin allow her to see cards only from boards where
+  // she is a member
+  if (!user.isAdmin) {
+    selector.$or = [
+      { permission: 'public' },
+      { members: { $elemMatch: { userId: user._id, isActive: true } } },
+    ];
+  }
+  Boards.find(selector).forEach(board => {
+    permiitedBoards.push(board._id);
+  });
+
+  const archivedSwimlanes = [];
+  Swimlanes.find({ archived: true }).forEach(swimlane => {
+    archivedSwimlanes.push(swimlane._id);
+  });
+
+  const archivedLists = [];
+  Lists.find({ archived: true }).forEach(list => {
+    archivedLists.push(list._id);
+  });
+
+  selector = {
+    archived: false,
+    boardId: { $nin: archivedBoards, $in: permiitedBoards },
+    swimlaneId: { $nin: archivedSwimlanes },
+    listId: { $nin: archivedLists },
+    dueAt: { $ne: null },
+    endAt: null,
+  };
+
+  if (!allUsers) {
+    selector.$or = [{ members: user._id }, { assignees: user._id }];
+  }
+
+  const cards = Cards.find(selector, {
+    fields: {
+      _id: 1,
+      archived: 1,
+      boardId: 1,
+      swimlaneId: 1,
+      listId: 1,
+      title: 1,
+      type: 1,
+      sort: 1,
+      members: 1,
+      assignees: 1,
+      colors: 1,
+      dueAt: 1,
+    },
+  });
+
+  const boards = [];
+  const swimlanes = [];
+  const lists = [];
+
+  cards.forEach(card => {
+    boards.push(card.boardId);
+    swimlanes.push(card.swimlaneId);
+    lists.push(card.listId);
+  });
+
+  return [
+    cards,
+    Boards.find({ _id: { $in: boards } }),
+    Swimlanes.find({ _id: { $in: swimlanes } }),
+    Lists.find({ _id: { $in: lists } }),
+  ];
+});