diff --git a/bugtracker/src/main/java/bugtracker/SecurityConfig.java b/bugtracker/src/main/java/bugtracker/SecurityConfig.java index 14e29e0..59ba6fa 100644 --- a/bugtracker/src/main/java/bugtracker/SecurityConfig.java +++ b/bugtracker/src/main/java/bugtracker/SecurityConfig.java @@ -42,6 +42,7 @@ protected void configure(HttpSecurity httpSecurity) throws Exception { .antMatchers("/api/user/current/**").permitAll() .antMatchers("/api/**").access("hasRole('ROLE_USER') or hasRole('ROLE_ADMIN') or hasRole('ROLE_DEVELOPER') or hasRole('ROLE_APPROVER')") .antMatchers("/ticket/**").access("hasRole('ROLE_USER') or hasRole('ROLE_ADMIN') or hasRole('ROLE_DEVELOPER') or hasRole('ROLE_APPROVER')") + .antMatchers("/comment/**").access("hasRole('ROLE_USER') or hasRole('ROLE_ADMIN') or hasRole('ROLE_DEVELOPER') or hasRole('ROLE_APPROVER')") .and() .csrf().disable() .formLogin() diff --git a/bugtracker/src/main/java/bugtracker/comment/CommentController.java b/bugtracker/src/main/java/bugtracker/comment/CommentController.java new file mode 100644 index 0000000..fe438bb --- /dev/null +++ b/bugtracker/src/main/java/bugtracker/comment/CommentController.java @@ -0,0 +1,39 @@ +package bugtracker.comment; + +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import javax.inject.Inject; +import java.util.List; + +@RestController +@RequestMapping("/api/comment") +public class CommentController { + + @Inject + CommentService commentService; + + @RequestMapping( + value = "/create", + method = RequestMethod.POST, + consumes = MediaType.APPLICATION_JSON_VALUE, + produces = MediaType.APPLICATION_JSON_VALUE + ) + @ResponseBody() + public ResponseEntity addNewComment(@RequestBody CommentEntity commentEntity) { + try { + commentService.createComment(commentEntity); + } catch (Exception e) { + e.printStackTrace(); + return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + } + return new ResponseEntity<>(HttpStatus.OK); + } + + @GetMapping("/all/{ticketId}") + public ResponseEntity> getAllByTicketID(@PathVariable Long ticketId) { + return new ResponseEntity<>(commentService.getAllByTicketId(ticketId), HttpStatus.OK); + } +} diff --git a/bugtracker/src/main/java/bugtracker/comment/CommentEntity.java b/bugtracker/src/main/java/bugtracker/comment/CommentEntity.java new file mode 100644 index 0000000..8c96f67 --- /dev/null +++ b/bugtracker/src/main/java/bugtracker/comment/CommentEntity.java @@ -0,0 +1,70 @@ +package bugtracker.comment; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.Table; +import java.time.LocalDateTime; + +@Entity +@Table(name = "KOMMENT") +public class CommentEntity { + @Id + @GeneratedValue + private long id; + + private long ticketId; + private long userId; + + private String commentText; + private String userName; + private LocalDateTime commentTime; + + public long getId() { + return id; + } + + public long getTicketId() { + return ticketId; + } + + public long getUserId() { + return userId; + } + + public String getCommentText() { + return commentText; + } + + public LocalDateTime getCommentTime() { + return commentTime; + } + + public void setId(long id) { + this.id = id; + } + + public String getUserName() { + return userName; + } + + public void setUserName(String userName) { + this.userName = userName; + } + + public void setTicketId(long ticketId) { + this.ticketId = ticketId; + } + + public void setUserId(long userId) { + this.userId = userId; + } + + public void setCommentText(String commentText) { + this.commentText = commentText; + } + + public void setCommentTime(LocalDateTime commentTime) { + this.commentTime = commentTime; + } +} diff --git a/bugtracker/src/main/java/bugtracker/comment/CommentRepository.java b/bugtracker/src/main/java/bugtracker/comment/CommentRepository.java new file mode 100644 index 0000000..46f8dc9 --- /dev/null +++ b/bugtracker/src/main/java/bugtracker/comment/CommentRepository.java @@ -0,0 +1,9 @@ +package bugtracker.comment; + +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; + +public interface CommentRepository extends JpaRepository { + List findAllByTicketId(Long ticketId); +} diff --git a/bugtracker/src/main/java/bugtracker/comment/CommentService.java b/bugtracker/src/main/java/bugtracker/comment/CommentService.java new file mode 100644 index 0000000..285e59e --- /dev/null +++ b/bugtracker/src/main/java/bugtracker/comment/CommentService.java @@ -0,0 +1,21 @@ +package bugtracker.comment; + +import org.springframework.stereotype.Service; + +import javax.inject.Inject; +import java.util.List; + +@Service +public class CommentService { + + @Inject + CommentRepository commentRepository; + + public CommentEntity createComment(CommentEntity commentEntity) { + return commentRepository.save(commentEntity); + } + + public List getAllByTicketId(Long ticketId) { + return commentRepository.findAllByTicketId(ticketId); + } +} diff --git a/bugtracker/src/main/java/bugtracker/user/BTUserDetails.java b/bugtracker/src/main/java/bugtracker/user/BTUserDetails.java index 7f686cf..94b623d 100644 --- a/bugtracker/src/main/java/bugtracker/user/BTUserDetails.java +++ b/bugtracker/src/main/java/bugtracker/user/BTUserDetails.java @@ -65,6 +65,10 @@ public boolean isCredentialsNonExpired() { @Override public boolean isEnabled() { - return true; + return user.getDeletedTs() == null; + } + + public Long getId() { + return user.getId(); } } diff --git a/bugtracker/src/main/java/bugtracker/user/UserController.java b/bugtracker/src/main/java/bugtracker/user/UserController.java index 440c8fa..e14d6a2 100644 --- a/bugtracker/src/main/java/bugtracker/user/UserController.java +++ b/bugtracker/src/main/java/bugtracker/user/UserController.java @@ -110,8 +110,18 @@ public ResponseEntity> getAllSimpleUser(){ return new ResponseEntity<>(userService.getUsersByType(2), HttpStatus.OK); } + @GetMapping("/current/") + public ResponseEntity getCurrentUser(Authentication authentication) { + if (authentication != null) { + BTUserDetails principal = (BTUserDetails) authentication.getPrincipal(); + return new ResponseEntity<>(userService.getUserById(principal.getId()), HttpStatus.OK); + } else { + return new ResponseEntity<>(null, HttpStatus.UNAUTHORIZED); + } + } + @GetMapping("/current/roles") - public ResponseEntity> getCurrentUser(Authentication authentication){ + public ResponseEntity> getCurrentUserRoles(Authentication authentication){ if (authentication != null) { BTUserDetails principal = (BTUserDetails) authentication.getPrincipal(); return new ResponseEntity<>(principal.getAuthorities().stream().map(t -> ((GrantedAuthority) t).getAuthority()).collect(toList()), HttpStatus.OK); diff --git a/bugtracker/src/main/java/bugtracker/user/UserService.java b/bugtracker/src/main/java/bugtracker/user/UserService.java index aa97216..448432b 100644 --- a/bugtracker/src/main/java/bugtracker/user/UserService.java +++ b/bugtracker/src/main/java/bugtracker/user/UserService.java @@ -34,13 +34,15 @@ public UserEntity createUser(UserEntity user){ } public UserEntity deleteUser(UserEntity user){ - user.setDeletedTs(LocalDateTime.now()); - return userRepository.save(user); + UserEntity userEntity = userRepository.findUserEntityById(user.getId()); + userEntity.setDeletedTs(LocalDateTime.now()); + return userRepository.save(userEntity); } public UserEntity undeleteUser(UserEntity user){ - user.setDeletedTs(null); - return userRepository.save(user); + UserEntity userEntity = userRepository.findUserEntityById(user.getId()); + userEntity.setDeletedTs(null); + return userRepository.save(userEntity); } public UserEntity modifyUser(UserEntity user){ diff --git a/bugtracker/src/main/resources/static/css/style.css b/bugtracker/src/main/resources/static/css/style.css index 2d94625..e3c5324 100644 --- a/bugtracker/src/main/resources/static/css/style.css +++ b/bugtracker/src/main/resources/static/css/style.css @@ -1,6 +1,23 @@ /* Based on ext-6.2.0-gpl/ext-6.2.0/build/examples/classic/responsive-app/resources/ResponsiveApp-all.css * © 2019 Sencha Inc. (under the terms of the GPLv3 license) */ +.x-dataview-item { + border : 1px solid #dadada; + border-radius : 2px; + padding : 2px; + margin : 2px; +} + +.comments .created{ + font-style:italic +} +.comments .user{ + font-weight:bold +} +.comments .content{ + margin-bottom:10px +} + .deleted-user .x-grid-cell { background-color: #ffe2e2; color: #900; @@ -729,7 +746,8 @@ td.x-frame-br { } .x-splitter { - font-size: 1px + font-size: 1px; + background-color: #3892D4 } .x-splitter-horizontal { diff --git a/bugtracker/src/main/resources/static/js/projects.js b/bugtracker/src/main/resources/static/js/projects.js index 40a02ea..a03a055 100644 --- a/bugtracker/src/main/resources/static/js/projects.js +++ b/bugtracker/src/main/resources/static/js/projects.js @@ -136,10 +136,11 @@ let projectdetails = function() { }; var projectdetailswindow = Ext.create('Ext.Window', { - width: 1000, - height: 500, + width: 560, + height: 380, padding: 15, modal: true, + title: "Project details", layout: { type: 'vbox', padding: 5 diff --git a/bugtracker/src/main/resources/static/js/tickets.js b/bugtracker/src/main/resources/static/js/tickets.js index f68c350..afac059 100644 --- a/bugtracker/src/main/resources/static/js/tickets.js +++ b/bugtracker/src/main/resources/static/js/tickets.js @@ -1,3 +1,16 @@ +let fetchUser = function(userId) { + Ext.Ajax.request({ + url: '/api/user/'+userId, + method: 'GET', + success: function (form, action) { + return JSON.parse(form.responseText) + }, + failure: function (form, action) { + alert(form.responseText); + } + }); +}; + let ticketdetails = function() { var ticket = {}; @@ -36,6 +49,10 @@ let ticketdetails = function() { fields: ['id', 'name'] }); + var commentStore = Ext.create('Ext.data.Store', { + fields: ['commentText', 'commentTime', 'commentUser'] + }); + var priorityStore = Ext.create('Ext.data.Store', { fields: ['priority'], data : [ @@ -168,7 +185,8 @@ let ticketdetails = function() { priority: ticketPrio.getValue(), type: ticketType.getValue(), reporterId: reporters.getValue(), - ownerId: owners.getValue() + ownerId: owners.getValue(), + statusId: statuses.getValue() } }; @@ -205,14 +223,11 @@ let ticketdetails = function() { handler: logTime }); - var ticketdetailswindow = Ext.create('Ext.Window', { - width: 1000, - height: 500, - padding: 15, - modal: true, + var ticketdetailspanel = Ext.create('Ext.panel.Panel',{ + width: 400, layout: { type: 'vbox', - padding: 5 + // padding: 5 }, items: [ name, @@ -228,6 +243,145 @@ let ticketdetails = function() { updateButton, logTimeButton ] + }); + + Ext.Ajax.request({ + url: '/api/comment/all/'+localStorage.getItem("ticketId"), + method: 'GET', + async: false, + success: function (form, action) { + let comments = JSON.parse(form.responseText); + for(let i=0; i{commentTime:date("Y-m-d H:i")} - {commentUser}', + '
{commentText}
' + ] + }); + + let createComment = function(user, comment) { + Ext.Ajax.request({ + url: '/api/comment/create', + method: 'POST', + jsonData: { + ticketId: localStorage.getItem("ticketId"), + userId: user.id, + commentText: comment.commentText, + commentTime: comment.commentTime, + userName: user.name + }, + success: function(form, action) {}, + failure: function (form, action) { + alert("Cannot save comment") + } + }); + } + + var commentButton = Ext.create('Ext.button.Button', { + text: 'Send', + handler: sendComment + }); + + var commentspanel = Ext.create('Ext.panel.Panel',{ + width: 580, + layout: { + type: 'vbox', + align: 'stretch', + padding: 10 + }, + items: [ + { + xtype: 'component', + html: '

Comments

' + }, + commentslist, + commentTextInput + ], + buttons: [ + commentButton + ] + }); + + var ticketdetailswindow = Ext.create('Ext.Window', { + width: 1030, + height: 500, + padding: 15, + modal: true, + title: "Ticket details", + layout: { + type: 'hbox', + align: 'stretch' + }, + items: [ + ticketdetailspanel, + { + xtype: 'splitter', + }, + commentspanel + ] }).show(); }; @@ -389,6 +543,10 @@ let newticket = function () { fields: ['id', 'name'] }); + var ownerStore = Ext.create('Ext.data.Store', { + fields: ['id', 'name'] + }); + var reporters = Ext.create('Ext.form.ComboBox', { fieldLabel: 'Reporter', store: reporterStore, @@ -397,9 +555,7 @@ let newticket = function () { valueField: 'id' }); - var ownerStore = Ext.create('Ext.data.Store', { - fields: ['id', 'name'] - }); + var owners = Ext.create('Ext.form.ComboBox', { fieldLabel: 'Owner', @@ -417,14 +573,13 @@ let newticket = function () { for (var i = 0; i < allUser.length; i++) { reporterStore.add({id: allUser[i].id, name: allUser[i].name}); } - reporters.setValue(allUser[0]); + reporters.setValue(allUser[0].id); }, failure: function (form, action) { alert(form.responseText); } }); - Ext.Ajax.request({ url: '/api/user/getAllOwner', method: 'GET', @@ -433,16 +588,20 @@ let newticket = function () { for (var i = 0; i < allUser.length; i++) { ownerStore.add({id: allUser[i].id, name: allUser[i].name}); } - owners.setValue(allUser[0]); + owners.setValue(allUser[0].id); }, failure: function (form, action) { alert(form.responseText); } }); + + + + var newticketwindow = Ext.create('Ext.Window', { - width: 1000, - height: 500, + width: 500, + height: 370, modal: true, title: "New ticket", layout: { diff --git a/bugtracker/src/main/resources/templates/historys.html b/bugtracker/src/main/resources/templates/historys.html new file mode 100644 index 0000000..566549b --- /dev/null +++ b/bugtracker/src/main/resources/templates/historys.html @@ -0,0 +1,10 @@ + + + + + Title + + + + + \ No newline at end of file diff --git a/bugtracker/src/main/resources/templates/main.html b/bugtracker/src/main/resources/templates/main.html index 883d159..b784891 100644 --- a/bugtracker/src/main/resources/templates/main.html +++ b/bugtracker/src/main/resources/templates/main.html @@ -2,7 +2,7 @@ - Hello World! + BugTracker