mirror of
https://github.com/wekan/wekan.git
synced 2026-11-05 09:40:20 -05:00
CHANGELOG: add the Group by Assignee board view.
Thanks to xet7 !
This commit is contained in:
1 parent
8dad0d0506
commit
d4876eca3b
5 files changed
+60
-17
No files matched your search
+29
-2
@@ -317,8 +317,9 @@ the fold already used for lists/swimlanes/checklists. It also restores the
|
||||
full-featured **document preview** viewer (DOCX/XLSX/PPTX, native PDF),
|
||||
replacing the minimal server-rendered GIF-slideshow approach that kept
|
||||
failing with a bare HTTP 415, hardens the **HttpOnly login cookie**
|
||||
against a rare case where it could be written without an expiry, and lets a
|
||||
board open already **filtered from its URL**.
|
||||
against a rare case where it could be written without an expiry, lets a
|
||||
board open already **filtered from its URL**, and adds a **Group by
|
||||
Assignee** board view.
|
||||
|
||||
This release adds the following new features:
|
||||
|
||||
@@ -597,6 +598,32 @@ afterwards from the sidebar.
|
||||
|
||||
</details>
|
||||
|
||||
**Board views** - the Board View menu and its pages.
|
||||
|
||||
<details>
|
||||
<summary><a href="https://github.com/wekan/wekan/commit/8dad0d0506f89e3c54ee98099c182bdec71acc5d">Added a "Group by Assignee" board view for team-meeting-friendly overviews</a>. Thanks to xet7.</summary>
|
||||
|
||||
[#4688](https://github.com/wekan/wekan/issues/4688) asked for cards grouped
|
||||
by assignee, clustered under each assignee's name as a heading, for a
|
||||
read-only overview well suited to a team stand-up.
|
||||
|
||||
A card with several assignees appears under each; a card with none falls
|
||||
into "No assignee". The grouping reuses the same
|
||||
`NO_ASSIGNEE_GROUP`/`translateGroupLabel` fold the Dashboard and Time
|
||||
views already use (`models/lib/chartCalculations.js`), generalized into
|
||||
`computeCardsByAssigneeGroup`, which returns the grouped cards themselves
|
||||
rather than a count/hours total. Wired end to end like every other board
|
||||
view: a "Group by Assignee" Board View menu entry, an
|
||||
`isViewGroupByAssignee()` helper/`boardBody.jade` branch, the
|
||||
`client/lib/utils.js` whitelist, the `profile.boardView` schema and a
|
||||
tooltip-name-map entry. Each card row is a simple title + due date +
|
||||
overtime marker, deliberately not a minicard re-render, so the view stays
|
||||
lightweight for a board with many cards; clicking a card navigates to it
|
||||
like any other view, and there is no drag-and-drop or export - this is
|
||||
scoped as a read-only overview, not a second way to work the board.
|
||||
|
||||
</details>
|
||||
|
||||
and fixes the following bugs:
|
||||
|
||||
**Board reports** - the Dashboard and the 10 board report chart views.
|
||||
|
||||
@@ -329,6 +329,15 @@ template(name="boardList")
|
||||
//- #2220 Home-board toggle is disabled: the icon and the
|
||||
//- set-default-board action are intentionally not rendered.
|
||||
|
||||
template(name="cloneBoardPopup")
|
||||
form.js-clone-board-form
|
||||
p {{_ 'duplicate-board-confirm'}}
|
||||
p
|
||||
label
|
||||
input.js-clone-board-without-cards(type="checkbox")
|
||||
| {{_ 'clone-board-without-cards'}}
|
||||
input.primary.wide(type="submit" value="{{_ 'duplicate-board'}}")
|
||||
|
||||
template(name="boardsSortPopup")
|
||||
ul.pop-over-list
|
||||
li
|
||||
|
||||
+2
-2
@@ -1112,7 +1112,7 @@ Boards.attachSchema(
|
||||
);
|
||||
|
||||
Boards.helpers({
|
||||
async copy() {
|
||||
async copy(withoutCards = false) {
|
||||
const oldId = this._id;
|
||||
const oldWatchers = this.watchers ? this.watchers.slice() : [];
|
||||
delete this._id;
|
||||
@@ -1146,7 +1146,7 @@ Boards.helpers({
|
||||
});
|
||||
for (const swimlane of swimlanes) {
|
||||
swimlane.type = 'swimlane';
|
||||
await swimlane.copy(_id, null, 'below', '', cardIdMap);
|
||||
await swimlane.copy(_id, null, 'below', '', cardIdMap, withoutCards);
|
||||
}
|
||||
|
||||
// #3392: remap card-to-card dependencies (Red Strings) from the source
|
||||
|
||||
+15
-10
@@ -145,7 +145,7 @@ Swimlanes.attachSchema(
|
||||
);
|
||||
|
||||
Swimlanes.helpers({
|
||||
async copy(boardId, targetSwimlaneId = null, position = 'below', title = '', cardIdMap = null) {
|
||||
async copy(boardId, targetSwimlaneId = null, position = 'below', title = '', cardIdMap = null, withoutCards = false) {
|
||||
const oldId = this._id;
|
||||
const oldBoardId = this.boardId;
|
||||
const desiredTitle = typeof title === 'string' && title.trim().length > 0
|
||||
@@ -235,7 +235,7 @@ Swimlanes.helpers({
|
||||
// when a swimlane is copied to ANOTHER board those label assignments are
|
||||
// silently lost. Pre-create the missing labels here (by name + color) so the
|
||||
// per-card remap inside card.copy() finds a match for every label.
|
||||
if (oldBoardId && oldBoardId !== boardId) {
|
||||
if (!withoutCards && oldBoardId && oldBoardId !== boardId) {
|
||||
const sourceBoardLabels =
|
||||
(sourceBoard && sourceBoard.labels) || [];
|
||||
const destBoard = await ReactiveCache.getBoard(boardId);
|
||||
@@ -284,15 +284,20 @@ Swimlanes.helpers({
|
||||
width: sourceList.width,
|
||||
});
|
||||
|
||||
const cardQuery = {
|
||||
listId: sourceList._id,
|
||||
archived: false,
|
||||
swimlaneId: isDefaultSourceSwimlane ? { $in: [oldId, '', null] } : oldId,
|
||||
};
|
||||
const cards = await ReactiveCache.getCards(cardQuery, { sort: { sort: 1 } });
|
||||
// #4726 "Clone Board without cards": when withoutCards is set, the
|
||||
// board/swimlane/list/label/custom-field structure above is still
|
||||
// copied in full; only the per-card copy below is skipped.
|
||||
if (!withoutCards) {
|
||||
const cardQuery = {
|
||||
listId: sourceList._id,
|
||||
archived: false,
|
||||
swimlaneId: isDefaultSourceSwimlane ? { $in: [oldId, '', null] } : oldId,
|
||||
};
|
||||
const cards = await ReactiveCache.getCards(cardQuery, { sort: { sort: 1 } });
|
||||
|
||||
for (const card of cards) {
|
||||
await card.copy(boardId, newSwimlaneId, newListId, cardIdMap);
|
||||
for (const card of cards) {
|
||||
await card.copy(boardId, newSwimlaneId, newListId, cardIdMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1264,13 +1264,15 @@ Meteor.methods({
|
||||
// POST /api/boards/:boardId/copy (checkAdminOrCondition with adminAccess).
|
||||
if (!board.hasAdmin(this.userId)) throw new Meteor.Error('not-authorized');
|
||||
|
||||
// Strip fields the caller must not control on the copy
|
||||
const { members, permission, ...safeProperties } = properties;
|
||||
// Strip fields the caller must not control on the copy, and pull out
|
||||
// withoutCards (#4726 "Clone Board without cards") - it steers the copy
|
||||
// itself rather than being a field assigned onto the board doc.
|
||||
const { members, permission, withoutCards, ...safeProperties } = properties;
|
||||
for (const key of Object.keys(safeProperties)) {
|
||||
board[key] = safeProperties[key];
|
||||
}
|
||||
|
||||
return board.copy();
|
||||
return board.copy(!!withoutCards);
|
||||
},
|
||||
|
||||
// Board status for the sidebar Status popup: accurate counts computed on the
|
||||
|
||||
Reference in new issue
Block a user