src/main/java/kr/wisestone/owl/service/IssueHistoryService.java | ●●●●● 패치 | 보기 | raw | blame | 히스토리 | |
src/main/java/kr/wisestone/owl/service/impl/IssueCommentServiceImpl.java | ●●●●● 패치 | 보기 | raw | blame | 히스토리 | |
src/main/java/kr/wisestone/owl/service/impl/IssueHistoryServiceImpl.java | ●●●●● 패치 | 보기 | raw | blame | 히스토리 | |
src/main/java/kr/wisestone/owl/service/impl/IssueServiceImpl.java | ●●●●● 패치 | 보기 | raw | blame | 히스토리 | |
src/main/java/kr/wisestone/owl/vo/IssueCommentVo.java | ●●●●● 패치 | 보기 | raw | blame | 히스토리 | |
src/main/java/kr/wisestone/owl/vo/IssueHistoryVo.java | ●●●●● 패치 | 보기 | raw | blame | 히스토리 | |
src/main/webapp/assets/styles/main.css | ●●●●● 패치 | 보기 | raw | blame | 히스토리 | |
src/main/webapp/i18n/ko/global.json | ●●●●● 패치 | 보기 | raw | blame | 히스토리 | |
src/main/webapp/scripts/app/issue/issueDetail.controller.js | ●●●●● 패치 | 보기 | raw | blame | 히스토리 | |
src/main/webapp/views/issue/issueDetail.html | ●●●●● 패치 | 보기 | raw | blame | 히스토리 |
src/main/java/kr/wisestone/owl/service/IssueHistoryService.java
@@ -27,7 +27,7 @@ void findIssueHistory(Map<String, Object> resJsonData, IssueHistoryCondition issueHistoryCondition); List<IssueHistoryVo> findIssueHistory(Long issueId); List<IssueHistoryVo> findIssueHistory(Issue issue); StringBuilder detectIssueChange(Issue issue, IssueForm issueForm, Project project, IssueStatus oldIssueStatus, IssueStatus issueStatus, IssueType issueType, Priority priority, Severity severity, List<MultipartFile> files); StringBuilder detectIssueChange(IssueForm issueForm, CheckIssueData data, List<MultipartFile> files); src/main/java/kr/wisestone/owl/service/impl/IssueCommentServiceImpl.java
@@ -133,7 +133,14 @@ @Override @Transactional(readOnly = true) public List<IssueCommentVo> findIssueComment(Long issueId) { return this.issueCommentRepository.findByIssueId(issueId); List<IssueCommentVo> issueCommentVos = this.issueCommentRepository.findByIssueId(issueId); Issue issue = this.issueService.getIssue(issueId); if (issueCommentVos != null && issueCommentVos.size() > 0) { for (IssueCommentVo issueCommentVo : issueCommentVos) { issueCommentVo.setTitle(issue.getTitle()); } } return issueCommentVos; } } src/main/java/kr/wisestone/owl/service/impl/IssueHistoryServiceImpl.java
@@ -312,8 +312,14 @@ // 이슈 아이디에 해당하는 기록 정보를 가져온다. @Override @Transactional(readOnly = true) public List<IssueHistoryVo> findIssueHistory(Long issueId) { return this.issueHistoryRepository.findByIssueId(issueId); public List<IssueHistoryVo> findIssueHistory(Issue issue) { List<IssueHistoryVo> issueHistoryVos = this.issueHistoryRepository.findByIssueId(issue.getId()); if (issueHistoryVos != null && issueHistoryVos.size() > 0) { for (IssueHistoryVo issueHistoryVo : issueHistoryVos) { issueHistoryVo.setTitle(issue.getTitle()); } } return issueHistoryVos; } // 이슈 변경 내역을 추출한다. src/main/java/kr/wisestone/owl/service/impl/IssueServiceImpl.java
@@ -1522,6 +1522,8 @@ this.setRegister(downIssue, downIssueVo); // 등록자 this.setIssueDepartment(downIssue, downIssueVo); // 담당부서 정보 셋팅 this.setIssueCustomFields(downIssue, downIssueVo); // 사용자정의필드 정보 세팅 this.setIssueHistory(downIssue, downIssueVo); // 이슈 기록 정보 셋팅 this.setIssueComments(downIssue, downIssueVo); // 댓글 정보 셋팅 resultList.add(downIssueVo); } @@ -1545,10 +1547,10 @@ this.setIssueDepartment(issue, issueVo); // 담당부서 정보 셋팅 this.setAttachedFiles(issue, issueVo); // 첨부 파일 정보 셋팅 this.setIssueCustomFields(issue, issueVo); // 사용자 정의 필드 값 정보 셋팅 this.setIssueComments(issue, issueVo); // 댓글 정보 셋팅 this.setIssueHistory(issue, issueVo); // 이슈 기록 정보 셋팅 this.setRelationIssue(issue, issueVo); //연관 일감 셋팅 this.setDownIssues(issue, issueVo); //하위 일감 세팅 this.setIssueComments(issue, issueVo); // 댓글 정보 셋팅 this.setIssueHistory(issue, issueVo); // 이슈 기록 정보 셋팅 IssueType issueType = this.issueTypeService.getIssueType(issueVo.getIssueTypeVo().getId()); // 이슈의 이슈유형 객체 Integer using = issueType.getUsePartner() != null ? issueType.getUsePartner().intValue() : 0; // 이슈유형별로 사용중인 업체/ISP/호스팅 값 @@ -1730,7 +1732,7 @@ // 이슈 기록 정보를 셋팅한다. private void setIssueHistory(Issue issue, IssueVo issueVo) { issueVo.setIssueHistoryVos(this.issueHistoryService.findIssueHistory(issue.getId())); issueVo.setIssueHistoryVos(this.issueHistoryService.findIssueHistory(issue)); } // 사용자 정의 필드 값이 같은 이슈 찾기 src/main/java/kr/wisestone/owl/vo/IssueCommentVo.java
@@ -9,6 +9,7 @@ public class IssueCommentVo extends BaseVo { private Long id; private String title; private String description; private IssueVo issueVo; private String registerName; @@ -34,6 +35,14 @@ this.id = id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getDescription() { return description; } src/main/java/kr/wisestone/owl/vo/IssueHistoryVo.java
@@ -10,6 +10,7 @@ */ public class IssueHistoryVo extends BaseVo{ private Long id; private String title; private String description; private String registerName; private String profile; @@ -40,6 +41,14 @@ this.id = id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getDescription() { return description; } src/main/webapp/assets/styles/main.css
@@ -19392,6 +19392,11 @@ position: relative; } .re-title { margin-top: 11px; margin-left: 18px; } .ae-item { display: -webkit-box; display: -ms-flexbox; @@ -29030,6 +29035,12 @@ font-weight: bold; } .dot { color: #161617; font-size: 0.9rem; font-weight: bold; } .activity-text h6.creat .dot { width: 5px; height: 5px; src/main/webapp/i18n/ko/global.json
@@ -201,6 +201,7 @@ "requireIssueTitle": "이슈 제목을 입력하세요.", "issueList": "이슈 목록", "summaryIssueActivity": "이슈 활동 내역 요약", "summaryDownIssueActivity": "하위 이슈 활동 내역 요약", "modifyIssues": "이슈 수정", "continueCreateIssue": "이 화면에서 이슈를 계속 생성합니다.", "initializedAssigneeCustomField": "프로젝트를 변경하면 담당자, 사용자 정의 필드 값이 초기화 됩니다.", @@ -259,7 +260,7 @@ "issueContentIsWrittenApplyTheTemplate": "이슈 내용이 작성되어 있습니다. 템플릿을 적용하겠습니까? 템플릿이 적용되면 이미 작성된 내용이 사라집니다.", "failedIssueStatusCheck": "이동 가능한 이슈 상태 조회 실패", "failedIssueStatusWorkflowDepartmentCheck": "이슈 상태 담당 부서 조회 실패", "failedToChangeMultipleIssueStatus": "다중 이슈 상태 변경 실패", "failedToChangeMultipleIssueStatus": "이슈 상태 변경 실패", "completedSavingIssueTable": "이슈 테이블 설정 저장 완료", "saveIssueTableSettingsInformation": "이슈 테이블 설정 정보가 저장되었습니다.", "failedToSaveIssueTableSetting": "이슈 테이블 설정 저장 실패", @@ -726,6 +727,8 @@ "fullView": "전체보기", "comments": "댓글", "record": "기록", "recordDown": "하위이슈 기록", "downComments": "하위이슈 댓글", "noAssignee": "담당자가 없습니다.", "noContent": "내용이 없습니다.", "noRecord": "기록이 없습니다.", @@ -736,6 +739,7 @@ "writeWaring2": "<li>제목 컬럼은 최대 300글자까지 입력할 수 있습니다.</li><li>내용 컬럼은 최대 5000글자까지 입력할 수 있습니다.</li><li>프로젝트 컬럼은 프로젝트 키를 입력해야 합니다.<dl><dt>프로젝트 키는 프로젝트 목록에서 확인할 수 있습니다.</dt></dl></li><li>유형 컬럼은 이슈 컬럼 명을 입력해야 합니다.<dl><dt>유형 명은 유형 목록에서 확인할 수 있습니다.</dt></dl></li><li>우선순위 컬럼은 낮음, 보통, 높음 중에서 1개만 입력해야 합니다.</li><li>중요도 컬럼은 낮음, 보통, 심각 중에서 1개만 입력해야 합니다.</li><li>담당자는 이메일로 지정할 수 있습니다.<dl><dt>여러 담당자를 지정하려면 #으로 구분하여 등록할 수 있습니다.</dt><dt>ex) wisestoneowl@gmail.com#support@wisestone.kr </dt><dt>해당 담당자가 등록하려는 이슈의 프로젝트에 참여하고 있어야 합니다.</dt></dl></li><li>시작일은 다음과 같은 형식으로 입력해야 합니다.<dl><dt>날짜 입력 값 결과 2018-11-22 10:30:00 AM O </dt></dl></li><li>J 컬럼부터는 사용자 정의 필드 입니다. 원하는 필드 값을 입력해야 합니다.<dl><dt>해당 사용자 정의 필드가 문자열 입력 필드일 경우 최대 100글자까지 입력 가능합니다.</dt><dt>해당 사용자 정의 필드가 다중 선택 필드면 #를 사용하여 여러 값을 입력할 수 있습니다.</dt><dt>ex) 옵션 값이 고양이, 강아지, 토끼일 경우 고양이와 토끼를 값으로 정하기 위해서는 <span class=\"fc-blue\">고양이#토끼</span>와 같이 입력해야 합니다. </dt></dl></li>", "color": "색상", "activityLogs": "활동 내역", "writeComment": "댓글 쓰기", "updatableStatus": "변경 가능한 상태", "testCase": "테스트 케이스", "updateStatus": "상태 변경", @@ -792,6 +796,8 @@ "createIssue": "이슈 생성", "updateIssue": "이슈 변경", "deleteIssue": "이슈 삭제", "createDownIssue": "하위 이슈 생성", "updateDownIssue": "하위 이슈 변경", "deleteDownIssue": "하위 이슈 삭제", "sendMailIssue": "이슈 메일 전송", "updateTitle": "제목이 변경되었습니다.", src/main/webapp/scripts/app/issue/issueDetail.controller.js
@@ -995,8 +995,45 @@ // 활동 이력 만들기 function makeActiveHistory() { $scope.vm.viewer.activeHistory = $scope.vm.viewer.issueCommentVos.concat($scope.vm.viewer.issueHistoryVos); $scope.vm.viewer.activeHistory.sort(function (a, b) { if ($scope.vm.viewer.issueDownVos != null && $scope.vm.viewer.issueDownVos.length > 0) { $scope.vm.viewer.activeHistory = ""; $scope.vm.viewer.activeParentHistory = ""; $scope.vm.viewer.activeDownHistory = ""; $scope.vm.viewer.activeDownAllHistory = ""; // 하위이슈 히스토리 for (var i=0; i<$scope.vm.viewer.issueDownVos.length; i++){ $scope.vm.viewer.activeDownHistory = $scope.vm.viewer.issueDownVos[i].issueHistoryVos.concat($scope.vm.viewer.issueDownVos[i+1].issueHistoryVos); i++; } for (var i=0; i<$scope.vm.viewer.activeDownHistory.length; i++){ $scope.vm.viewer.activeDownHistory[i].description = "<div class='dot'>하위이슈 : "+$scope.vm.viewer.activeDownHistory[i].title+"</div>" + $scope.vm.viewer.activeDownHistory[i].description; } $scope.vm.viewer.activeDownHistory.sort(function (a, b) { //내림차순 return a.registerDate > b.registerDate ? -1 : a.registerDate < b.registerDate ? 1 : 0; }); // 하위이슈 댓글 히스토리 for (var i=0; i<$scope.vm.viewer.issueDownVos.length; i++) { $scope.vm.viewer.downComment = $scope.vm.viewer.issueDownVos[i].issueCommentVos.concat($scope.vm.viewer.issueDownVos[i+1].issueCommentVos); i++; } /*for (var i=0; i<$scope.vm.viewer.downComment.length; i++){ $scope.vm.viewer.downComment[i].description = $scope.vm.viewer.downComment[i].title +"의 댓글 : "+ $scope.vm.viewer.downComment[i].description; }*/ $scope.vm.viewer.issueDownVos.sort(function (a, b) { //내림차순 return a.registerDate > b.registerDate ? -1 : a.registerDate < b.registerDate ? 1 : 0; }); $scope.vm.viewer.activeDownAllHistory = $scope.vm.viewer.downComment.concat($scope.vm.viewer.activeDownHistory); $scope.vm.viewer.activeParentHistory = $scope.vm.viewer.issueCommentVos.concat($scope.vm.viewer.issueHistoryVos); $scope.vm.viewer.activeHistory = $scope.vm.viewer.activeParentHistory.concat($scope.vm.viewer.activeDownAllHistory); }else { $scope.vm.viewer.activeHistory = $scope.vm.viewer.issueCommentVos.concat($scope.vm.viewer.issueHistoryVos); } $scope.vm.viewer.activeHistory.sort(function (a, b) { //내림차순 return a.registerDate > b.registerDate ? -1 : a.registerDate < b.registerDate ? 1 : 0; }); } @@ -1073,6 +1110,7 @@ if (result.data.message.status === "success") { $scope.vm.viewer.issueCommentVos = result.data.data; $scope.fn.updateActiveHistory(); } else { SweetAlert.swal({ src/main/webapp/views/issue/issueDetail.html
@@ -631,6 +631,32 @@ <span translate="common.noAttachedFile">첨부된 파일이 없습니다.</span> </div> </div> <div class="row"> <div class="col-sm-12"> <h6 class="todo-content-subheader mt-20" translate="common.writeComment">댓글 쓰기</h6> <form name="commentForm" ng-enter="fn.addComment()"> <div class="form-group row "> <div class="col-sm-12"> <div class="input-group"> <input class="form-control" kr-input maxlength="300" placeholder="{{'common.pleaseEnterComments' | translate}}" ng-model="vm.viewer.comment" type="text"> <div class="input-group-prepend"> <div class="input-group-text2" ng-class="{ 'disable' : !$root.isDefined(vm.viewer.comment) }" ng-click="fn.addComment()"> <i class="os-icon os-icon-mail-18"></i> </div> </div> </div> </div> </div> </form> </div> </div> <!-- 활동 내역 --> <div class="row"> @@ -644,14 +670,22 @@ ng-click="fn.updateActiveHistory()"><span translate="common.fullView">전체보기</span></a> </li> <li class="nav-item cursor"> <!--<li class="nav-item cursor"> <a class="nav-link" ng-class="{ 'active' : vm.activeTab == 'REPLY' }" ng-click="vm.activeTab = 'REPLY'"><span translate="common.comments">댓글</span></a> </li> </li>--> <li class="nav-item cursor"> <a class="nav-link" ng-class="{ 'active' : vm.activeTab == 'RECODE' }" ng-click="vm.activeTab = 'RECODE'"><span translate="common.record">기록</span></a> </li> <li class="nav-item cursor"> <a class="nav-link" ng-class="{ 'active' : vm.activeTab == 'RECODE_DOWN' }" ng-click="vm.activeTab = 'RECODE_DOWN'"><span translate="common.recordDown">하위이슈 기록</span></a> </li> <!--<li class="nav-item cursor"> <a class="nav-link" ng-class="{ 'active' : vm.activeTab == 'REPLY_DOWN' }" ng-click="vm.activeTab = 'REPLY_DOWN'"><span translate="common.downComments">하위이슈 댓글</span></a> </li>--> </ul> </div> @@ -662,12 +696,18 @@ <!-- 기록인 경우 --> <div class="aei-content" ng-if="$root.isDefined(activeHistory.issueHistoryType)"> <h6 ng-if="activeHistory.issueHistoryType == 'TOTAL'" <!--<h6 ng-if="activeHistory.issueHistoryType == 'TOTAL'" translate="issue.summaryIssueActivity">이슈 활동 내역 요약 <i class="os-icon os-icon-arrow-down"></i></h6> class="os-icon os-icon-arrow-down"></i></h6>--> <div dom-append dom="activeHistory.description"></div> </div> <!-- 댓글인 경우 --> <div ng-if="!$root.isDefined(activeHistory.issueHistoryType)" ng-show="vm.viewer.title !== activeHistory.title"> <div> 하위이슈 : {{::activeHistory.title}} </div> </div> <div class="aei-image" ng-if="!$root.isDefined(activeHistory.issueHistoryType)"> <div class="user-avatar-w"> <img alt="image" ng-src="{{::activeHistory.profile}}"> @@ -728,53 +768,135 @@ <span class="issue-detail-word-break">{{::comment.description}}</span> </div> </div> </div> </div> </div> <form name="commentForm" ng-enter="fn.addComment()"> <div class="form-group row "> <div class="col-sm-12"> <div class="input-group"> <input class="form-control" kr-input maxlength="300" placeholder="{{'common.pleaseEnterComments' | translate}}" ng-model="vm.viewer.comment" type="text"> <div class="input-group-prepend"> <div class="input-group-text2" ng-class="{ 'disable' : !$root.isDefined(vm.viewer.comment) }" ng-click="fn.addComment()"> <i class="os-icon os-icon-mail-18"></i> </div> </div> </div> </div> </div> </form> </div> <!-- 기록 보기 --> <div ng-if="vm.activeTab == 'RECODE'"> <div class="no-cont2" ng-if="vm.viewer.issueHistoryVos.length < 1" translate="common.noRecord"> <div class="no-cont2" ng-if="vm.viewer.activeParentHistory.length < 1" translate="common.noRecord"> 기록이 없습니다. </div> <!--<div> {{vm.viewer.issueHistoryVos}} </div>--> <div class="activity-item" ng-repeat="issueHistory in vm.viewer.issueHistoryVos"> <h6 ng-if="issueHistory.issueHistoryType == 'TOTAL'" <div class="activity-item" ng-repeat="issueHistory in vm.viewer.activeParentHistory"> <!--<h6 ng-if="issueHistory.issueHistoryType == 'TOTAL'" translate="issue.summaryIssueActivity">이슈 활동 내역 요약 <i class="os-icon os-icon-arrow-down"></i> </h6> <div class="activity-content"> </h6>--> <div class="activity-content" ng-if="$root.isDefined(issueHistory.issueHistoryType)"> <div dom-append dom="issueHistory.description"></div> </div> <!-- 댓글인 경우 --> <div class="aei-image" ng-if="!$root.isDefined(issueHistory.issueHistoryType)"> <div class="user-avatar-w"> <img alt="image" ng-src="{{::issueHistory.profile}}"> </div> </div> <div class="aei-content" ng-if="!$root.isDefined(issueHistory.issueHistoryType)"> <h6 class="aei-title"> {{::issueHistory.registerName}} </h6> <div class="date-break"> <span>{{::issueHistory.registerDate}}</span> </div> <div class="aei-delete" ng-click="fn.removeComment(comment)" ng-show="$root.user.id == comment.registerId"> <i class="os-icon os-icon-close"></i> </div> <div class="chat-messages"> <div class="message"> <div class="message-content"> <span class="issue-detail-word-break">{{::issueHistory.description}}</span> </div> </div> </div> </div> </div> </div> <!-- 하위이슈 기록 보기 --> <div ng-if="vm.activeTab == 'RECODE_DOWN'"> <div class="no-cont2" ng-if="vm.viewer.activeDownAllHistory.length < 1" translate="common.noRecord"> 기록이 없습니다. </div> <div class="activity-item" ng-repeat="issueDownHistory in vm.viewer.activeDownAllHistory"> <!-- 기록인 경우 --> <div class="activity-content" ng-if="$root.isDefined(issueDownHistory.issueHistoryType)"> <div dom-append dom="issueDownHistory.description"></div> </div> <!-- 댓글인 경우 --> <div class="aei-content" ng-if="!$root.isDefined(issueDownHistory.issueHistoryType)"> <div class="aei-title"> 하위이슈 : {{::issueDownHistory.title}} </div> </div> <div class="aei-image" ng-if="!$root.isDefined(issueDownHistory.issueHistoryType)"> <div class="user-avatar-w"> <img alt="image" ng-src="{{::issueDownHistory.profile}}"> </div> </div> <div class="aei-content" ng-if="!$root.isDefined(issueDownHistory.issueHistoryType)"> <h6 class="aei-title"> {{::issueDownHistory.registerName}} </h6> <div class="date-break"> <span>{{::issueDownHistory.registerDate}}</span> </div> <div class="aei-delete" ng-click="fn.removeComment(comment)" ng-show="$root.user.id == comment.registerId"> <i class="os-icon os-icon-close"></i> </div> <div class="chat-messages"> <div class="message"> <div class="message-content"> <span class="issue-detail-word-break">{{::issueDownHistory.description}}</span> </div> </div> </div> </div> </div> </div> <!-- 하위이슈 댓글 보기 --> <div ng-if="vm.activeTab == 'REPLY_DOWN'"> <div class="no-cont2" ng-if="$scope.vm.viewer.downComment.length < 1" translate="common.noComments"> 댓글이 없습니다. </div> <div class="ae-item" ng-repeat="comment in vm.viewer.downComment"> <div class="re-title"> <div class='dot'> 하위이슈: {{::comment.title}} </div> </div> <div class="aei-image"> <div class="user-avatar-w"> <img alt="image" ng-src="{{::comment.profile}}"> </div> </div> <div class="aei-content"> <h6 class="aei-title"> {{::comment.registerName}} </h6> <div class="date-break"> <span>{{::comment.registerDate}}</span> </div> <div class="aei-delete" ng-click="fn.removeComment(comment)" ng-show="$root.user.id == comment.registerId"> <i class="os-icon os-icon-close"></i> </div> <div class="chat-messages"> <div class="message"> <div class="message-content"> <span class="issue-detail-word-break">{{::comment.description}}</span> </div> </div> </div> </div> </div> </div> </div>