/**
|
* Created by maprex on 2021-03-23
|
*/
|
|
'use strict';
|
|
define([
|
'app', 'angular'
|
],
|
function (app, angular) {
|
app.controller('ganttController', ['$scope', '$rootScope', '$log', '$q', '$resourceProvider', '$controller', '$injector', 'Gantt', 'SweetAlert', '$filter', 'IssueType', 'Priority', 'Severity', 'IssueStatus', 'CustomField','IssueSearch',
|
function ($scope, $rootScope, $log, $q, $resourceProvider, $controller, $injector, Gantt, SweetAlert, $filter, IssueType, Priority, Severity, IssueStatus, CustomField, IssueSearch) {
|
$scope.fn = {
|
initSearch : initSearch, // 검색 초기화
|
getPageList : getPageList, // 목록을 조회한다.
|
makeSearchConditions : makeSearchConditions, // 검색조건을 가져옴
|
getIssueTypes : getIssueTypes, // 이슈 유형 목록을 가져온다.
|
getPriorities : getPriorities, // 우선순위 목록을 가져온다.
|
getSeverities : getSeverities, // 중요도 목록을 가져온다.
|
getIssueStatuses : getIssueStatuses, // 이슈 상태 목록을 가져온다.
|
getCustomFields : getCustomFields, // 사용자 정의 필드 목록을 가져온다.
|
startExecute : startExecute
|
};
|
|
$scope.vm = {
|
search : {
|
title : "", // 제목
|
description : "", // 내용
|
combinationIssueNumber : "", // 이슈 번호
|
projectType : "BTS_PROJECT", // 프로젝트 유형
|
registerDateRange : "", // 등록일 기간 검색
|
startDateRange : "", // 시작일 기간 검색
|
completeDateRange : "", // 완료일 기간 검색
|
severityIds : [], // 중요도 검색
|
priorityIds : [], // 우선순위 검색
|
issueStatusIds : [], // 이슈 상태 검색
|
issueTypeIds : [] // 이슈 유형 검색
|
},
|
arrProjects : [], // 프로젝트 배열
|
projectIssues : new Object(), // 프로젝트 전체 이슈
|
projectCompleteIssues : new Object(), // 프로젝트 완료 이슈
|
searchView : false, // 상세 검색 조건 표시 여부
|
detailView : false, // 상세 모드 변경 값
|
tableConfigs : [], // 테이블 셋팅 정보
|
responseData : {
|
data : []
|
},
|
projectName : "", // 프로젝트 검색
|
userName : "", // 담당자 검색
|
registerName : "", // 등록자 검색
|
projects : [], // 프로젝트
|
issueStatuses : [], // 이슈 상태
|
issueTypes : [], // 이슈 유형
|
priorities : [], // 우선 순위
|
severities : [], // 중요도
|
users : [], // 담당자
|
registers : [], // 등록자
|
customFields : [], // 사용자 정의 필드
|
issueTableConfigs : [], // 이슈 테이블 설정
|
page : {
|
selectedPage : 0,
|
selectedPageRowCount : String(99999)
|
},
|
relationIssues :[], // 연관 이슈 배열
|
useGantt : false
|
};
|
|
angular.extend(this, $controller('autoCompleteController', {$scope : $scope, $injector : $injector}));
|
|
// 검색 필드 초기화
|
function initSearch() {
|
$state.go($state.current, {}, {reload : true});
|
}
|
|
// 이슈 검색 조건을 만든다.
|
function makeSearchConditions() {
|
var conditions = {
|
title : $scope.vm.search.title,
|
description : $scope.vm.search.description,
|
projectType : $scope.vm.search.projectType,
|
combinationIssueNumber : $scope.vm.search.combinationIssueNumber.trim(),
|
beginRegisterDate : "",
|
endRegisterDate : "",
|
beginStartDate : "",
|
endStartDate : "",
|
beginCompleteDate : "",
|
endCompleteDate : "",
|
projectIds : (function () {
|
var projectIds = [];
|
|
angular.forEach($scope.vm.projects, function (project) {
|
projectIds.push(project.id);
|
});
|
|
return projectIds;
|
})(),
|
issueStatusIds : (function () {
|
var issueStatusIds = [];
|
|
angular.forEach($scope.vm.search.issueStatusIds, function (issueStatusId) {
|
issueStatusIds.push(issueStatusId.fieldKey);
|
});
|
|
return issueStatusIds;
|
})(),
|
issueTypeIds : (function () {
|
var issueTypeIds = [];
|
|
angular.forEach($scope.vm.search.issueTypeIds, function (issueTypeId) {
|
issueTypeIds.push(issueTypeId.fieldKey);
|
});
|
|
return issueTypeIds;
|
})(),
|
priorityIds : (function () {
|
var priorityIds = [];
|
|
angular.forEach($scope.vm.search.priorityIds, function (priorityId) {
|
priorityIds.push(priorityId.fieldKey);
|
});
|
|
return priorityIds;
|
})(),
|
severityIds : (function () {
|
var severityIds = [];
|
|
angular.forEach($scope.vm.search.severityIds, function (severityId) {
|
severityIds.push(severityId.fieldKey);
|
});
|
|
return severityIds;
|
})(),
|
userIds : (function () {
|
var userIds = [];
|
|
angular.forEach($scope.vm.users, function (user) {
|
userIds.push(user.id);
|
});
|
|
return userIds;
|
})(),
|
registerIds : (function () {
|
var registerIds = [];
|
|
angular.forEach($scope.vm.registers, function (register) {
|
registerIds.push(register.id);
|
});
|
|
return registerIds;
|
})(),
|
issueCustomFields : (function () {
|
var issueCustomFields = [];
|
|
angular.forEach($scope.vm.customFields, function (customField) {
|
var useValues = [];
|
|
if (angular.isArray(customField.useValues)) {
|
angular.forEach(customField.useValues, function (useValue) {
|
useValues.push(useValue.value);
|
});
|
}
|
else {
|
useValues.push(customField.useValues);
|
}
|
|
// useValues 를 배열로 변환한다.
|
var temp = angular.copy(customField);
|
temp.useValues = useValues;
|
issueCustomFields.push(temp);
|
});
|
|
return issueCustomFields;
|
})()
|
};
|
|
// 등록일
|
if ($rootScope.isDefined($scope.vm.search.registerDateRange)) {
|
var registerDateRange = $scope.vm.search.registerDateRange.split("~");
|
conditions.beginRegisterDate = registerDateRange[0].trim();
|
conditions.endRegisterDate = registerDateRange[1].trim();
|
}
|
|
// 시작일
|
if ($rootScope.isDefined($scope.vm.search.startDateRange)) {
|
var startDateRange = $scope.vm.search.startDateRange.split("~");
|
conditions.beginStartDate = startDateRange[0].trim();
|
conditions.endStartDate = startDateRange[1].trim();
|
}
|
// 종료일
|
if ($rootScope.isDefined($scope.vm.search.completeDateRange)) {
|
var completeDateRange = $scope.vm.search.completeDateRange.split("~");
|
conditions.beginCompleteDate = completeDateRange[0].trim();
|
conditions.endCompleteDate = completeDateRange[1].trim();
|
}
|
|
return conditions;
|
}
|
|
// 이슈 상태 목록
|
function getIssueStatuses() {
|
var deferred = $q.defer();
|
$scope.vm.issueStatuses = [];
|
|
IssueStatus.find($resourceProvider.getContent({},
|
$resourceProvider.getPageContent(0, 1000))).then(function (result) {
|
if (result.data.message.status === "success") {
|
angular.forEach(result.data.data, function (issueType) {
|
$scope.vm.issueStatuses.push({
|
fieldKey : issueType.id,
|
fieldValue : issueType.name
|
});
|
});
|
}
|
else {
|
SweetAlert.swal($filter("translate")("common.failedToIssueStatusListLookup"), result.data.message.message, "error"); // 이슈 상태 목록 조회 실패
|
}
|
|
deferred.resolve(result.data.data);
|
});
|
|
return deferred.promise;
|
}
|
|
// 이슈 유형 목록
|
function getIssueTypes() {
|
var deferred = $q.defer();
|
$scope.vm.issueTypes = [];
|
|
IssueType.find($resourceProvider.getContent({},
|
$resourceProvider.getPageContent(0, 1000))).then(function (result) {
|
if (result.data.message.status === "success") {
|
angular.forEach(result.data.data, function (issueType) {
|
$scope.vm.issueTypes.push({
|
fieldKey : issueType.id,
|
fieldValue : issueType.name
|
});
|
});
|
}
|
else {
|
SweetAlert.swal($filter("translate")("issue.failedToIssueTypeListLookup"), result.data.message.message, "error"); // 이슈 유형 목록 조회 실패
|
}
|
|
deferred.resolve(result.data.data);
|
});
|
|
return deferred.promise;
|
}
|
|
// 우선순위 목록
|
function getPriorities() {
|
var deferred = $q.defer();
|
$scope.vm.priorities = [];
|
|
Priority.find($resourceProvider.getContent({},
|
$resourceProvider.getPageContent(0, 1000))).then(function (result) {
|
if (result.data.message.status === "success") {
|
angular.forEach(result.data.data, function (prioritiy) {
|
$scope.vm.priorities.push({
|
fieldKey : prioritiy.id,
|
fieldValue : prioritiy.name
|
});
|
});
|
}
|
else {
|
SweetAlert.swal($filter("translate")("issue.failedToPriorityListLookup"), result.data.message.message, "error"); // 우선순위 목록 조회 실패
|
}
|
|
deferred.resolve(result.data.data);
|
});
|
|
return deferred.promise;
|
}
|
|
// 중요도 목록
|
function getSeverities() {
|
var deferred = $q.defer();
|
$scope.vm.severities = [];
|
|
Severity.find($resourceProvider.getContent({},
|
$resourceProvider.getPageContent(0, 1000))).then(function (result) {
|
if (result.data.message.status === "success") {
|
angular.forEach(result.data.data, function (severity) {
|
$scope.vm.severities.push({
|
fieldKey : severity.id,
|
fieldValue : severity.name
|
});
|
});
|
}
|
else {
|
SweetAlert.swal($filter("translate")("issue.failedToCriticalListLookup"), result.data.message.message, "error"); // 중요도 목록 조회 실패
|
}
|
|
deferred.resolve(result.data.data);
|
});
|
|
return deferred.promise;
|
}
|
|
// 사용자 정의 필드 목록
|
function getCustomFields() {
|
var deferred = $q.defer();
|
$scope.vm.customFields = [];
|
|
CustomField.find($resourceProvider.getContent({},
|
$resourceProvider.getPageContent(0, 1000))).then(function (result) {
|
if (result.data.message.status === "success") {
|
|
angular.forEach(result.data.data, function (customField) {
|
switch (customField.customFieldType) {
|
case "INPUT" :
|
customField.useValues = "";
|
break;
|
|
case "MULTI_SELECT" :
|
case "SINGLE_SELECT" :
|
customField.useValues = [];
|
break;
|
}
|
|
$scope.vm.customFields.push(customField);
|
});
|
}
|
else {
|
SweetAlert.swal($filter("translate")("issue.failedToUserDefinedFieldListLookup"), result.data.message.message, "error"); // 사용자 정의 필드 목록 조회 실패
|
}
|
|
deferred.resolve(result.data.data);
|
});
|
|
return deferred.promise;
|
}
|
|
|
function drawGanttChart(useProject = false) {
|
google.charts.load('current', {'packages':['gantt'], 'language': 'ko'});
|
if (!useProject) {
|
google.charts.setOnLoadCallback(drawChart);
|
} else {
|
google.charts.setOnLoadCallback(drawChartProject);
|
}
|
$rootScope.spinner = false;
|
}
|
|
function toMilliseconds(minutes) {
|
return minutes * 60 * 1000;
|
}
|
|
// 검색조건에서 해당 프로젝트 찾기
|
function findProjectSearch(projectId) {
|
var projects = $scope.vm.projects;
|
var find = false;
|
|
for (let i = 0; i < projects.length; i++) {
|
if (projects[i].id == projectId) {
|
find = true;
|
break;
|
}
|
}
|
return find;
|
}
|
|
function getPageList(selectedPage) {
|
// 현재 페이지 정보
|
var currentPage = 0;
|
|
$rootScope.spinner = true;
|
|
// 현재 선택된 프로젝트를 검색 기본으로 추가
|
if ($rootScope.workProject != null && $rootScope.workProject.id > -1) {
|
var find = findProjectSearch($rootScope.workProject.id);
|
if (!find) {
|
$scope.vm.projects.push($rootScope.workProject);
|
}
|
}
|
|
var conditions = $scope.fn.makeSearchConditions();
|
|
Gantt.find($resourceProvider.getContent(conditions,
|
$resourceProvider.getPageContent(currentPage, $scope.vm.page.selectedPageRowCount))).then(function (result) {
|
|
if (result.data.message.status === "success") {
|
$scope.vm.page.selectedPage = currentPage + 1;
|
$scope.vm.responseData = result.data;
|
|
if (conditions.projectIds.length > 0) {
|
drawGanttChart();
|
} else {
|
drawGanttChart(true);
|
}
|
}
|
else {
|
//SweetAlert.error($filter("translate")("issue.failedIssueLookup"), result.data.message.message); // 이슈 조회 실패
|
}
|
});
|
}
|
|
// 이슈리스트에 해당 아이디가 존재하는지 여부 확인
|
function containsIssue(issueId) {
|
var responseData = $scope.vm.responseData;
|
if (responseData != null) {
|
var data = responseData.data;
|
for (var i=0; i < data.length; i++) {
|
var el = data[i];
|
if (el.id == issueId) {
|
return true;
|
}
|
}
|
}
|
return false
|
}
|
|
// 순환구조여부 확인
|
// 연관 일감이 양방향으로 적용되어 있을때 차트에서는 오류가 발생하므로 양방향일 경우 처리 안하기 위함
|
function isCycle(parentId, childId) {
|
var data = $scope.vm.relationIssues;
|
if (data != null && data.length > 0) {
|
for (var i=0; i < data.length; i++) {
|
if (data[i].chartParent == childId && data[i].chartChild == parentId) {
|
return true;
|
}
|
}
|
}
|
return false;
|
}
|
|
function drawChart() {
|
var responseData = $scope.vm.responseData;
|
var page = responseData.page;
|
|
var otherData = new google.visualization.DataTable();
|
otherData.addColumn('string', 'Task ID');
|
otherData.addColumn('string', 'Task Name');
|
otherData.addColumn('string', 'Resource');
|
otherData.addColumn('date', 'Start');
|
otherData.addColumn('date', 'End');
|
otherData.addColumn('number', '기간');
|
otherData.addColumn('number', 'Percent Complete');
|
otherData.addColumn('string', 'Dependencies');
|
var data = responseData.data;
|
var dataCount = data.length;
|
var trackHeight = 22;
|
var bottomHeight = 50;
|
var chartHeight = dataCount * trackHeight + bottomHeight;
|
var arrPalette = [];
|
|
if (page.totalCount > 0) {
|
$scope.vm.relationIssues = [];
|
data.forEach(el => {
|
var start = null;
|
if (el.startDate != null) {
|
start = new Date(el.startDate);
|
start.setHours(0,0,0);
|
}
|
|
var end = null;
|
if (el.completeDate != null) {
|
end = new Date(el.completeDate);
|
end.setHours(23, 59, 59);
|
}
|
|
var duration = 0;
|
if (start != null && end != null) {
|
duration = end.getTime() - start.getTime();
|
}
|
|
var p = {
|
"color": el.issueStatusColor,
|
"dark": el.issueStatusColor,
|
"light": el.issueStatusColor
|
};
|
|
arrPalette.push(p);
|
|
// 연관이슈 설정
|
var relationIssue = null;
|
if ( el.issueRelationIssueVos != null && el.issueRelationIssueVos.length > 0) {
|
var i = 0;
|
relationIssue = "";
|
el.issueRelationIssueVos.forEach(rel => {
|
if (i > 0) {
|
relationIssue += ",";
|
}
|
if (containsIssue(rel.id) && !isCycle(el.id, rel.id)) {
|
relationIssue += String(rel.id);
|
var pair = {
|
chartParent : el.id,
|
chartChild : rel.id
|
}
|
$scope.vm.relationIssues.push(pair);
|
i++;
|
}
|
});
|
}
|
otherData.addRow([String(el.id), el.title, String(el.id), start, end, duration, 100, relationIssue]);
|
|
})
|
$scope.vm.useGantt = true;
|
} else {
|
var p = {
|
"color": "0066cc",
|
"dark": "0066cc",
|
"light": "0066cc"
|
};
|
|
arrPalette.push(p);
|
otherData.addRow(["none", "일감이 없습니다", "none", new Date(), null, toMilliseconds(0), 100, null]);
|
$scope.vm.useGantt = false;
|
}
|
|
var options = {
|
gantt: {
|
defaultStartDate : new Date(),
|
barHeight : 15,
|
barCornerRadius : 1,
|
criticalPathEnabled: true, // Critical path arrows will be the same as other arrows.
|
arrow: {
|
angle: 50,
|
width: 1,
|
color: '#0066cc',
|
radius: 2
|
},
|
labelStyle: {
|
fontName: 'NanumSquare',
|
fontSize: 12,
|
color: '#d5c209'
|
},
|
palette: arrPalette,
|
trackHeight : trackHeight
|
},
|
height: chartHeight,
|
animation: {"startup": true}
|
};
|
|
var container = document.getElementById('chart_div');
|
if (container == null) {
|
return;
|
}
|
var chart = new google.visualization.Gantt(container);
|
hideChartTooltip(container, chart);
|
|
google.visualization.events.addListener(chart, 'onmouseover', function (e) {
|
});
|
|
google.visualization.events.addListener(chart, 'click', function() {
|
});
|
|
google.visualization.events.addListener(chart, 'select', function() {
|
var selection = chart.getSelection();
|
|
var responseData = $scope.vm.responseData;
|
var data = responseData.data;
|
|
var issue = data[selection[0].row];
|
|
// 이슈 번호를 저장한 후 이슈 목록으로 이동한다.
|
$rootScope.$broadcast("makeIssueSearch", issue);
|
});
|
|
window.addEventListener('resize', function() { chart.draw(otherData, options); }, false); //화면 크기에 따라 그래프 크기 변경
|
chart.draw(otherData, options);
|
}
|
|
|
// 이슈를 배열에 추가
|
function addIssue(projectId, issue) {
|
if ($scope.vm.projectCompleteIssues[projectId] == null)
|
$scope.vm.projectCompleteIssues[projectId] = [];
|
|
if ($scope.vm.projectIssues[projectId] == null)
|
$scope.vm.projectIssues[projectId] = [];
|
|
if (issue.issueStatusType == "CLOSE") {
|
$scope.vm.projectCompleteIssues[projectId].push(issue);
|
}
|
$scope.vm.projectIssues[projectId].push(issue);
|
}
|
|
// google chart 기본 tooltip 가리기
|
function hideChartTooltip(container, chart) {
|
google.visualization.events.addOneTimeListener(chart, 'ready', function () {
|
var observer = new MutationObserver(function (nodes) {
|
Array.prototype.forEach.call(nodes, function(node) {
|
if (node.addedNodes.length > 0) {
|
Array.prototype.forEach.call(node.addedNodes, function(addedNode) {
|
if ((addedNode.tagName === 'rect') && (addedNode.getAttribute('fill') === 'white')) {
|
addedNode.setAttribute('fill', 'transparent');
|
addedNode.setAttribute('stroke', 'transparent');
|
Array.prototype.forEach.call(addedNode.parentNode.getElementsByTagName('text'), function(label) {
|
label.setAttribute('fill', 'transparent');
|
});
|
}
|
});
|
}
|
});
|
});
|
observer.observe(container, {
|
childList: true,
|
subtree: true
|
});
|
});
|
}
|
|
function drawChartProject() {
|
var responseData = $scope.vm.responseData;
|
var page = responseData.page;
|
|
var otherData = new google.visualization.DataTable();
|
otherData.addColumn('string', 'Task ID');
|
otherData.addColumn('string', 'Task Name');
|
otherData.addColumn('string', 'Resource');
|
otherData.addColumn('date', '시작');
|
otherData.addColumn('date', '종료');
|
otherData.addColumn('number', '기간');
|
otherData.addColumn('number', '진행율');
|
otherData.addColumn('string', '상위프로젝트');
|
var data = responseData.data;
|
|
var trackHeight = 35;
|
var bottomHeight = 50;
|
|
$scope.vm.arrProjects = [];
|
if (page.totalCount > 0) {
|
data.forEach(el => {
|
addIssue(el.projectId, el);
|
});
|
|
var workProjects = $rootScope.projects;
|
workProjects.forEach(projectObj => {
|
if (projectObj.id > 0) {
|
var issues = $scope.vm.projectIssues[projectObj.id];
|
var completeIssues = $scope.vm.projectCompleteIssues[projectObj.id];
|
var start = null;
|
if (projectObj.startDate != null) {
|
start = new Date(projectObj.startDate);
|
start.setHours(0, 0, 0);
|
}
|
|
var end = null;
|
if (projectObj.endDate != null) {
|
end = new Date(projectObj.endDate);
|
end.setHours(23, 59, 59);
|
}
|
|
var duration = 0;
|
if (start != null && end != null) {
|
duration = end.getTime() - start.getTime();
|
}
|
|
var percent = 0;
|
var endCount = completeIssues == null ? 0 : completeIssues.length.toFixed(2);
|
var totalCount = issues == null ? 0 : issues.length;
|
if (totalCount > 0) {
|
percent = parseInt(endCount / totalCount * 100.0);
|
}
|
|
var depend = null;
|
if (projectObj.parentProjectId != null) {
|
depend = String(projectObj.parentProjectId);
|
}
|
|
$scope.vm.arrProjects.push(projectObj.id);
|
otherData.addRow([String(projectObj.id), projectObj.name, String(projectObj.id), start, end, duration, percent, depend]);
|
}
|
});
|
|
$scope.vm.useGantt = true;
|
} else {
|
otherData.addRow(["none", "일감이 없습니다", "none", new Date(), null, toMilliseconds(0), 100, null]);
|
$scope.vm.useGantt = false;
|
}
|
|
var dataCount = $scope.vm.arrProjects.length;
|
var chartHeight = dataCount * trackHeight + bottomHeight;
|
|
var options = {
|
gantt: {
|
defaultStartDate : new Date(),
|
barHeight : 18,
|
barCornerRadius : 1,
|
criticalPathEnabled: true, // Critical path arrows will be the same as other arrows.
|
arrow: {
|
angle: 50,
|
width: 1,
|
color: '#0066cc',
|
radius: 2
|
},
|
labelStyle: {
|
fontName: 'NanumSquare',
|
fontSize: 12,
|
color: '#d5c209'
|
},
|
trackHeight : trackHeight
|
},
|
height: chartHeight,
|
animation: {"startup": true}
|
};
|
|
var container = document.getElementById('chart_div');
|
if (container == null) {
|
return;
|
}
|
var chart = new google.visualization.Gantt(container);
|
|
hideChartTooltip(container, chart);
|
|
google.visualization.events.addListener(chart, 'onmouseover', function (e) {
|
});
|
|
google.visualization.events.addListener(chart, 'click', function() {
|
});
|
|
google.visualization.events.addListener(chart, 'select', function() {
|
var selection = chart.getSelection();
|
$rootScope.changeLastProject($scope.vm.arrProjects[selection[0].row]);
|
});
|
|
window.addEventListener('resize', function() { chart.draw(otherData, options); }, false); //화면 크기에 따라 그래프 크기 변경
|
chart.draw(otherData, options);
|
|
}
|
|
function startExecute() {
|
var promises = {
|
getIssueTypes: $scope.fn.getIssueTypes(),
|
getPriorities: $scope.fn.getPriorities(),
|
getSeverities: $scope.fn.getSeverities(),
|
getIssueStatuses: $scope.fn.getIssueStatuses(),
|
getCustomFields: $scope.fn.getCustomFields()
|
}
|
|
$q.all(promises).then(function (results) {
|
$log.debug("promises 결과 ", results);
|
$scope.fn.getPageList();
|
});
|
}
|
|
$scope.fn.startExecute();
|
|
}]);
|
});
|