Commit 92f8df4a authored by Paul Slaughter's avatar Paul Slaughter Committed by Phil Hughes

Extract EE specific files/lines for app/assets/javascripts/boards

parent de46bf61
...@@ -28,29 +28,29 @@ export default { ...@@ -28,29 +28,29 @@ export default {
}, },
}, },
methods: { methods: {
buildUpdateRequest(list) {
return {
add_label_ids: [list.label.id],
};
},
addIssues() { addIssues() {
const firstListIndex = 1; const firstListIndex = 1;
const list = this.modal.selectedList || this.state.lists[firstListIndex]; const list = this.modal.selectedList || this.state.lists[firstListIndex];
const selectedIssues = ModalStore.getSelectedIssues(); const selectedIssues = ModalStore.getSelectedIssues();
const issueIds = selectedIssues.map(issue => issue.id); const issueIds = selectedIssues.map(issue => issue.id);
const { currentBoard } = this.state; const req = this.buildUpdateRequest(list);
const boardLabelIds = currentBoard.labels.map(label => label.id);
const assigneeIds = currentBoard.assignee && [currentBoard.assignee.id];
// Post the data to the backend // Post the data to the backend
gl.boardService.bulkUpdate(issueIds, { gl.boardService
add_label_ids: [list.label.id, ...boardLabelIds], .bulkUpdate(issueIds, req)
milestone_id: currentBoard.milestone_id, .catch(() => {
assignee_ids: assigneeIds, Flash(__('Failed to update issues, please try again.'));
weight: currentBoard.weight,
}).catch(() => {
Flash(__('Failed to update issues, please try again.'));
selectedIssues.forEach((issue) => { selectedIssues.forEach((issue) => {
list.removeIssue(issue); list.removeIssue(issue);
list.issuesSize -= 1; list.issuesSize -= 1;
});
}); });
});
// Add the issues on the frontend // Add the issues on the frontend
selectedIssues.forEach((issue) => { selectedIssues.forEach((issue) => {
......
...@@ -121,8 +121,7 @@ ...@@ -121,8 +121,7 @@
<div <div
v-if="issuesCount > 0 && issues.length === 0" v-if="issuesCount > 0 && issues.length === 0"
class="empty-state add-issues-empty-state-filter text-center"> class="empty-state add-issues-empty-state-filter text-center">
<div <div class="svg-content">
class="svg-content">
<img :src="emptyStateSvg" /> <img :src="emptyStateSvg" />
</div> </div>
<div class="text-content"> <div class="text-content">
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
const Store = gl.issueBoards.BoardsStore; const Store = gl.issueBoards.BoardsStore;
export default { export default Vue.extend({
props: { props: {
issue: { issue: {
type: Object, type: Object,
...@@ -23,42 +23,16 @@ ...@@ -23,42 +23,16 @@
}, },
methods: { methods: {
removeIssue() { removeIssue() {
const board = Store.state.currentBoard;
const { issue } = this; const { issue } = this;
const lists = issue.getLists(); const lists = issue.getLists();
const boardLabelIds = board.labels.map(label => label.id); const req = this.buildPatchRequest(issue, lists);
const listLabelIds = lists.map(list => list.label.id);
let labelIds = issue.labels
.map(label => label.id)
.filter(id => !listLabelIds.includes(id))
.filter(id => !boardLabelIds.includes(id));
if (labelIds.length === 0) {
labelIds = [''];
}
let assigneeIds = issue.assignees
.map(assignee => assignee.id)
.filter(id => id !== board.assignee.id);
if (assigneeIds.length === 0) {
// for backend to explicitly set No Assignee
assigneeIds = ['0'];
}
const data = { const data = {
issue: { issue: this.seedPatchRequest(issue, req),
label_ids: labelIds,
assignee_ids: assigneeIds,
},
}; };
if (board.milestone_id) { if (data.issue.label_ids.length === 0) {
data.issue.milestone_id = -1; data.issue.label_ids = [''];
}
if (board.weight) {
data.issue.weight = null;
} }
// Post the remove data // Post the remove data
...@@ -77,8 +51,30 @@ ...@@ -77,8 +51,30 @@
Store.detail.issue = {}; Store.detail.issue = {};
}, },
/**
* Build the default patch request.
*/
buildPatchRequest(issue, lists) {
const listLabelIds = lists.map(list => list.label.id);
const labelIds = issue.labels
.map(label => label.id)
.filter(id => !listLabelIds.includes(id));
return {
label_ids: labelIds,
};
},
/**
* Seed the given patch request.
*
* (This is overridden in EE)
*/
seedPatchRequest(issue, req) {
return req;
},
}, },
}; });
</script> </script>
<template> <template>
<div <div
......
...@@ -12,13 +12,10 @@ import '~/vue_shared/models/assignee'; ...@@ -12,13 +12,10 @@ import '~/vue_shared/models/assignee';
import FilteredSearchBoards from './filtered_search_boards'; import FilteredSearchBoards from './filtered_search_boards';
import eventHub from './eventhub'; import eventHub from './eventhub';
import sidebarEventHub from '~/sidebar/event_hub'; // eslint-disable-line import/first import sidebarEventHub from '~/sidebar/event_hub'; // eslint-disable-line import/first
import './models/issue';
import './models/list';
import './models/milestone'; import './models/milestone';
import './models/project'; import './models/project';
import './stores/boards_store'; import './stores/boards_store';
import ModalStore from './stores/modal_store'; import ModalStore from './stores/modal_store';
import BoardService from './services/board_service';
import modalMixin from './mixins/modal_mixins'; import modalMixin from './mixins/modal_mixins';
import './mixins/sortable_default_options'; import './mixins/sortable_default_options';
import './filters/due_date_filters'; import './filters/due_date_filters';
...@@ -28,7 +25,12 @@ import './components/new_list_dropdown'; ...@@ -28,7 +25,12 @@ import './components/new_list_dropdown';
import BoardAddIssuesModal from './components/modal/index.vue'; import BoardAddIssuesModal from './components/modal/index.vue';
import '~/vue_shared/vue_resource_interceptor'; // eslint-disable-line import/first import '~/vue_shared/vue_resource_interceptor'; // eslint-disable-line import/first
import 'ee/boards/models/list'; // eslint-disable-line import/first
import 'ee/boards/models/issue'; // eslint-disable-line import/first
import 'ee/boards/models/project'; // eslint-disable-line import/first import 'ee/boards/models/project'; // eslint-disable-line import/first
import BoardService from 'ee/boards/services/board_service'; // eslint-disable-line import/first
import 'ee/boards/components/board_sidebar'; // eslint-disable-line import/first
import 'ee/boards/components/modal/index'; // eslint-disable-line import/first
import 'ee/boards/components/boards_selector'; // eslint-disable-line import/first import 'ee/boards/components/boards_selector'; // eslint-disable-line import/first
import collapseIcon from 'ee/boards/icons/fullscreen_collapse.svg'; // eslint-disable-line import/first import collapseIcon from 'ee/boards/icons/fullscreen_collapse.svg'; // eslint-disable-line import/first
import expandIcon from 'ee/boards/icons/fullscreen_expand.svg'; // eslint-disable-line import/first import expandIcon from 'ee/boards/icons/fullscreen_expand.svg'; // eslint-disable-line import/first
......
...@@ -4,7 +4,8 @@ ...@@ -4,7 +4,8 @@
/* global ListAssignee */ /* global ListAssignee */
import Vue from 'vue'; import Vue from 'vue';
import IssueProject from 'ee/boards/models/project'; import '~/vue_shared/models/label';
import IssueProject from './project';
class ListIssue { class ListIssue {
constructor (obj, defaultAvatar) { constructor (obj, defaultAvatar) {
...@@ -20,18 +21,14 @@ class ListIssue { ...@@ -20,18 +21,14 @@ class ListIssue {
this.position = obj.relative_position || Infinity; this.position = obj.relative_position || Infinity;
this.isFetching = { this.isFetching = {
subscriptions: true, subscriptions: true,
weight: true,
};
this.isLoading = {
weight: false,
}; };
this.isLoading = {};
this.sidebarInfoEndpoint = obj.issue_sidebar_endpoint; this.sidebarInfoEndpoint = obj.issue_sidebar_endpoint;
this.referencePath = obj.reference_path; this.referencePath = obj.reference_path;
this.path = obj.real_path; this.path = obj.real_path;
this.toggleSubscriptionEndpoint = obj.toggle_subscription_endpoint; this.toggleSubscriptionEndpoint = obj.toggle_subscription_endpoint;
this.milestone_id = obj.milestone_id; this.milestone_id = obj.milestone_id;
this.project_id = obj.project_id; this.project_id = obj.project_id;
this.weight = obj.weight;
if (obj.project) { if (obj.project) {
this.project = new IssueProject(obj.project); this.project = new IssueProject(obj.project);
......
...@@ -7,6 +7,24 @@ import queryData from '../utils/query_data'; ...@@ -7,6 +7,24 @@ import queryData from '../utils/query_data';
const PER_PAGE = 20; const PER_PAGE = 20;
const TYPES = {
backlog: {
isPreset: true,
isExpandable: true,
isBlank: false,
},
closed: {
isPreset: true,
isExpandable: true,
isBlank: false,
},
blank: {
isPreset: true,
isExpandable: false,
isBlank: true,
},
};
class List { class List {
constructor(obj, defaultAvatar) { constructor(obj, defaultAvatar) {
this.id = obj.id; this.id = obj.id;
...@@ -14,8 +32,10 @@ class List { ...@@ -14,8 +32,10 @@ class List {
this.position = obj.position; this.position = obj.position;
this.title = obj.title; this.title = obj.title;
this.type = obj.list_type; this.type = obj.list_type;
this.preset = ['backlog', 'closed', 'blank', 'promotion'].indexOf(this.type) > -1;
this.isExpandable = ['backlog', 'closed'].indexOf(this.type) > -1; const typeInfo = this.getTypeInfo(this.type);
this.preset = !!typeInfo.isPreset;
this.isExpandable = !!typeInfo.isExpandable;
this.isExpanded = true; this.isExpanded = true;
this.page = 1; this.page = 1;
this.loading = true; this.loading = true;
...@@ -31,7 +51,7 @@ class List { ...@@ -31,7 +51,7 @@ class List {
this.title = this.assignee.name; this.title = this.assignee.name;
} }
if (this.type !== 'blank' && this.type !== 'promotion' && this.id) { if (!typeInfo.isBlank && this.id) {
this.getIssues().catch(() => { this.getIssues().catch(() => {
// TODO: handle request error // TODO: handle request error
}); });
...@@ -126,23 +146,7 @@ class List { ...@@ -126,23 +146,7 @@ class List {
return gl.boardService return gl.boardService
.newIssue(this.id, issue) .newIssue(this.id, issue)
.then(res => res.data) .then(res => res.data)
.then(data => { .then(data => this.onNewIssueResponse(issue, data));
issue.id = data.id;
issue.iid = data.iid;
issue.milestone = data.milestone;
issue.project = data.project;
issue.assignees = Array.isArray(data.assignees)
? data.assignees.map(assignee => new ListAssignee(assignee))
: data.assignees;
issue.labels = data.labels;
issue.path = data.real_path;
issue.referencePath = data.reference_path;
if (this.issuesSize > 1) {
const moveBeforeId = this.issues[1].id;
gl.boardService.moveIssue(issue.id, null, null, null, moveBeforeId);
}
});
} }
createIssues(data) { createIssues(data) {
...@@ -222,6 +226,25 @@ class List { ...@@ -222,6 +226,25 @@ class List {
return !matchesRemove; return !matchesRemove;
}); });
} }
getTypeInfo (type) {
return TYPES[type] || {};
}
onNewIssueResponse (issue, data) {
issue.id = data.id;
issue.iid = data.iid;
issue.project = data.project;
issue.path = data.real_path;
issue.referencePath = data.reference_path;
if (this.issuesSize > 1) {
const moveBeforeId = this.issues[1].id;
gl.boardService.moveIssue(issue.id, null, null, null, moveBeforeId);
}
}
} }
window.List = List; window.List = List;
export default List;
...@@ -22,36 +22,6 @@ export default class BoardService { ...@@ -22,36 +22,6 @@ export default class BoardService {
return `${gon.relative_url_root}/-/boards/${boardId ? `${boardId}` : ''}/issues${id ? `/${id}` : ''}`; return `${gon.relative_url_root}/-/boards/${boardId ? `${boardId}` : ''}/issues${id ? `/${id}` : ''}`;
} }
allBoards() {
return axios.get(this.generateBoardsPath());
}
createBoard(board) {
const boardPayload = { ...board };
boardPayload.label_ids = (board.labels || []).map(b => b.id);
if (boardPayload.label_ids.length === 0) {
boardPayload.label_ids = [''];
}
if (boardPayload.assignee) {
boardPayload.assignee_id = boardPayload.assignee.id;
}
if (boardPayload.milestone) {
boardPayload.milestone_id = boardPayload.milestone.id;
}
if (boardPayload.id) {
return axios.put(this.generateBoardsPath(boardPayload.id), { board: boardPayload });
}
return axios.post(this.generateBoardsPath(), { board: boardPayload });
}
deleteBoard({ id }) {
return axios.delete(this.generateBoardsPath(id));
}
all() { all() {
return axios.get(this.listsEndpoint); return axios.get(this.listsEndpoint);
} }
...@@ -122,12 +92,6 @@ export default class BoardService { ...@@ -122,12 +92,6 @@ export default class BoardService {
return axios.get(endpoint); return axios.get(endpoint);
} }
static updateWeight(endpoint, weight = null) {
return axios.put(endpoint, {
weight,
});
}
static toggleIssueSubscription(endpoint) { static toggleIssueSubscription(endpoint) {
return axios.post(endpoint); return axios.post(endpoint);
} }
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
import $ from 'jquery'; import $ from 'jquery';
import _ from 'underscore'; import _ from 'underscore';
import { __ } from '~/locale'; import { __ } from '~/locale';
import '~/gl_dropdown';
import axios from './lib/utils/axios_utils'; import axios from './lib/utils/axios_utils';
import { timeFor } from './lib/utils/datetime_utility'; import { timeFor } from './lib/utils/datetime_utility';
import ModalStore from './boards/stores/modal_store'; import ModalStore from './boards/stores/modal_store';
......
...@@ -135,8 +135,7 @@ js-project-select form-control prepend-top-10 append-bottom-10" ...@@ -135,8 +135,7 @@ js-project-select form-control prepend-top-10 append-bottom-10"
<option <option
v-for="project in projects" v-for="project in projects"
:key="project.id" :key="project.id"
:value="project.id :value="project.id">
">
{{ project.name }} {{ project.name }}
</option> </option>
</select> </select>
......
import '~/boards/components/board_sidebar';
import RemoveBtn from './sidebar/remove_issue';
const base = gl.issueBoards.BoardSidebar;
gl.issueBoards.BoardSidebar = base.extend({
components: {
RemoveBtn,
},
});
import Vue from 'vue'; import Vue from 'vue';
import $ from 'jquery'; import $ from 'jquery';
import { throttle } from 'underscore'; import { throttle } from 'underscore';
import '~/boards/stores/boards_store';
import BoardForm from './board_form.vue'; import BoardForm from './board_form.vue';
import AssigneesList from './assignees_list'; import AssigneesList from './assignees_list';
......
import Vue from 'vue';
import base from '~/boards/components/modal/footer.vue';
export default Vue.extend(base, {
methods: {
buildUpdateRequest(list) {
const { currentBoard } = this.state;
const boardLabelIds = currentBoard.labels.map(label => label.id);
const assigneeIds = currentBoard.assignee && [currentBoard.assignee.id];
return {
add_label_ids: [list.label.id, ...boardLabelIds],
milestone_id: currentBoard.milestone_id,
assignee_ids: assigneeIds,
weight: currentBoard.weight,
};
},
},
});
import Vue from 'vue';
import base from '~/boards/components/modal/index.vue';
import ModalFooter from './footer';
gl.issueBoards.IssuesModal = Vue.extend(base, {
components: {
ModalFooter,
},
});
import base from '~/boards/components/sidebar/remove_issue.vue';
const Store = gl.issueBoards.BoardsStore;
export default base.extend({
methods: {
seedPatchRequest(issue, req) {
const board = Store.state.currentBoard;
const boardLabelIds = board.labels.map(label => label.id);
req.label_ids = req.label_ids.filter(id => !boardLabelIds.includes(id));
if (board.milestone_id) {
req.milestone_id = -1;
}
if (board.weight) {
req.weight = null;
}
const boardAssignee = board.assignee ? board.assignee.id : null;
const assigneeIds = issue.assignees
.map(assignee => assignee.id)
.filter(id => id !== boardAssignee);
return {
...req,
assignee_ids: assigneeIds.length ? assigneeIds : ['0'],
};
},
},
});
import ListIssue from '~/boards/models/issue';
import IssueProjectEE from './project';
class ListIssueEE extends ListIssue {
constructor(obj, defaultAvatar) {
super(obj, defaultAvatar, {
IssueProject: IssueProjectEE,
});
this.isFetching.weight = true;
this.isLoading.weight = false;
this.weight = obj.weight;
if (obj.project) {
this.project = new IssueProjectEE(obj.project);
}
}
}
window.ListIssue = ListIssueEE;
export default ListIssueEE;
/* eslint-disable no-param-reassign */
import List from '~/boards/models/list';
import ListAssignee from '~/vue_shared/models/assignee';
const EE_TYPES = {
promotion: {
isPreset: true,
isExpandable: false,
isBlank: true,
},
};
class ListEE extends List {
getTypeInfo(type) {
return EE_TYPES[type] || super.getTypeInfo(type);
}
onNewIssueResponse(issue, data) {
issue.milestone = data.milestone;
issue.assignees = Array.isArray(data.assignees)
? data.assignees.map(assignee => new ListAssignee(assignee))
: data.assignees;
issue.labels = data.labels;
super.onNewIssueResponse(issue, data);
}
}
window.List = ListEE;
export default ListEE;
import axios from '~/lib/utils/axios_utils';
import BoardService from '~/boards/services/board_service';
export default class BoardServiceEE extends BoardService {
allBoards() {
return axios.get(this.generateBoardsPath());
}
createBoard(board) {
const boardPayload = { ...board };
boardPayload.label_ids = (board.labels || []).map(b => b.id);
if (boardPayload.label_ids.length === 0) {
boardPayload.label_ids = [''];
}
if (boardPayload.assignee) {
boardPayload.assignee_id = boardPayload.assignee.id;
}
if (boardPayload.milestone) {
boardPayload.milestone_id = boardPayload.milestone.id;
}
if (boardPayload.id) {
return axios.put(this.generateBoardsPath(boardPayload.id), { board: boardPayload });
}
return axios.post(this.generateBoardsPath(), { board: boardPayload });
}
deleteBoard({ id }) {
return axios.delete(this.generateBoardsPath(id));
}
static updateWeight(endpoint, weight = null) {
return axios.put(endpoint, {
weight,
});
}
}
import Vue from 'vue'; import Vue from 'vue';
import BoardService from '~/boards/services/board_service'; import BoardService from 'ee/boards/services/board_service';
import 'ee/boards/components/boards_selector'; import 'ee/boards/components/boards_selector';
import setTimeoutPromiseHelper from 'spec/helpers/set_timeout_promise_helper'; import setTimeoutPromiseHelper from 'spec/helpers/set_timeout_promise_helper';
import mountComponent from 'spec/helpers/vue_mount_component_helper'; import mountComponent from 'spec/helpers/vue_mount_component_helper';
......
import Vue from 'vue'; import Vue from 'vue';
import '~/boards/models/issue';
import IssueCardInner from '~/boards/components/issue_card_inner.vue'; import IssueCardInner from '~/boards/components/issue_card_inner.vue';
import ListIssue from '~/boards/models/issue'; import ListIssue from 'ee/boards/models/issue';
import mountComponent from 'spec/helpers/vue_mount_component_helper'; import mountComponent from 'spec/helpers/vue_mount_component_helper';
import { listObj } from './mock_data'; import { listObj } from 'spec/boards/mock_data';
describe('Issue card component', () => { describe('Issue card component', () => {
let vm; let vm;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment