fix(project): hide mentor-only controls
This commit is contained in:
+5
-1
@@ -1,5 +1,6 @@
|
|||||||
package com.lab.labtimesheet.feature.project.controller;
|
package com.lab.labtimesheet.feature.project.controller;
|
||||||
|
|
||||||
|
import com.lab.labtimesheet.feature.project.exception.ProjectAccessDeniedException;
|
||||||
import com.lab.labtimesheet.feature.project.model.dto.ProjectCreateForm;
|
import com.lab.labtimesheet.feature.project.model.dto.ProjectCreateForm;
|
||||||
import com.lab.labtimesheet.feature.project.model.dto.ProjectMemberForm;
|
import com.lab.labtimesheet.feature.project.model.dto.ProjectMemberForm;
|
||||||
import com.lab.labtimesheet.feature.project.service.ProjectQueryService;
|
import com.lab.labtimesheet.feature.project.service.ProjectQueryService;
|
||||||
@@ -34,7 +35,10 @@ public class ProjectController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/new")
|
@GetMapping("/new")
|
||||||
public String createForm(Model model) {
|
public String createForm(Principal principal, Model model) {
|
||||||
|
if (!"MENTOR".equals(pages.authenticatedActor(principal.getName()).role())) {
|
||||||
|
throw new ProjectAccessDeniedException();
|
||||||
|
}
|
||||||
model.addAttribute("projectForm", new ProjectCreateForm());
|
model.addAttribute("projectForm", new ProjectCreateForm());
|
||||||
return "projects/form";
|
return "projects/form";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,5 +10,6 @@ public record ProjectDetail(
|
|||||||
LocalDate startDate,
|
LocalDate startDate,
|
||||||
LocalDate endDate,
|
LocalDate endDate,
|
||||||
String mentorName,
|
String mentorName,
|
||||||
String leaderName) {
|
String leaderName,
|
||||||
|
boolean canManage) {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,7 +64,8 @@ public class ProjectQueryService {
|
|||||||
project.startDate(),
|
project.startDate(),
|
||||||
project.endDate(),
|
project.endDate(),
|
||||||
displayName(project.mentorUserId()),
|
displayName(project.mentorUserId()),
|
||||||
displayName(project.currentLeader().internUserId()));
|
displayName(project.currentLeader().internUserId()),
|
||||||
|
project.mentorUserId() == actorUserId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<table><caption>Leadership history</caption><thead><tr><th scope="col">Leader</th><th scope="col">Started</th><th scope="col">Ended</th></tr></thead>
|
<table><caption>Leadership history</caption><thead><tr><th scope="col">Leader</th><th scope="col">Started</th><th scope="col">Ended</th></tr></thead>
|
||||||
<tbody><tr th:each="term : ${leadership}"><td th:text="${term.leaderName}"></td><td th:text="${term.startedAt}"></td><td th:text="${term.endedAt}"></td></tr></tbody>
|
<tbody><tr th:each="term : ${leadership}"><td th:text="${term.leaderName}"></td><td th:text="${term.startedAt}"></td><td th:text="${term.endedAt}"></td></tr></tbody>
|
||||||
</table>
|
</table>
|
||||||
<form method="post" th:action="@{/projects/{id}/leadership(id=${project.id})}"><label for="leader">New Leader user ID</label><input id="leader" name="internUserId" type="number" min="1" required><button type="submit">Change Leader</button></form>
|
<form th:if="${project.canManage}" method="post" th:action="@{/projects/{id}/leadership(id=${project.id})}"><label for="leader">New Leader user ID</label><input id="leader" name="internUserId" type="number" min="1" required><button type="submit">Change Leader</button></form>
|
||||||
</main>
|
</main>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<table><caption>Membership history</caption><thead><tr><th scope="col">Intern</th><th scope="col">Joined</th><th scope="col">Left</th><th scope="col">Role</th></tr></thead>
|
<table><caption>Membership history</caption><thead><tr><th scope="col">Intern</th><th scope="col">Joined</th><th scope="col">Left</th><th scope="col">Role</th></tr></thead>
|
||||||
<tbody><tr th:each="member : ${members}"><td th:text="${member.displayName}"></td><td th:text="${member.joinedAt}"></td><td th:text="${member.leftAt}"></td><td th:text="${member.currentLeader} ? 'Leader' : 'Member'"></td></tr></tbody>
|
<tbody><tr th:each="member : ${members}"><td th:text="${member.displayName}"></td><td th:text="${member.joinedAt}"></td><td th:text="${member.leftAt}"></td><td th:text="${member.currentLeader} ? 'Leader' : 'Member'"></td></tr></tbody>
|
||||||
</table>
|
</table>
|
||||||
<form method="post" th:action="@{/projects/{id}/members(id=${project.id})}"><label for="intern">Intern user ID</label><input id="intern" name="internUserId" type="number" min="1" required><button type="submit">Add member</button></form>
|
<form th:if="${project.canManage}" method="post" th:action="@{/projects/{id}/members(id=${project.id})}"><label for="intern">Intern user ID</label><input id="intern" name="internUserId" type="number" min="1" required><button type="submit">Add member</button></form>
|
||||||
</main>
|
</main>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+21
-3
@@ -3,6 +3,8 @@ package com.lab.labtimesheet.feature.project.controller;
|
|||||||
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.never;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
import static org.hamcrest.Matchers.containsString;
|
||||||
|
import static org.hamcrest.Matchers.not;
|
||||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
|
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
|
||||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||||
@@ -13,6 +15,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
|||||||
|
|
||||||
import com.lab.labtimesheet.feature.project.exception.ProjectAccessDeniedException;
|
import com.lab.labtimesheet.feature.project.exception.ProjectAccessDeniedException;
|
||||||
import com.lab.labtimesheet.feature.project.model.dto.ProjectCreateCommand;
|
import com.lab.labtimesheet.feature.project.model.dto.ProjectCreateCommand;
|
||||||
|
import com.lab.labtimesheet.feature.project.model.dto.ProjectActorView;
|
||||||
import com.lab.labtimesheet.feature.project.model.dto.ProjectDetail;
|
import com.lab.labtimesheet.feature.project.model.dto.ProjectDetail;
|
||||||
import com.lab.labtimesheet.feature.project.model.dto.ProjectSummary;
|
import com.lab.labtimesheet.feature.project.model.dto.ProjectSummary;
|
||||||
import com.lab.labtimesheet.feature.project.service.ProjectQueryService;
|
import com.lab.labtimesheet.feature.project.service.ProjectQueryService;
|
||||||
@@ -79,16 +82,31 @@ class ProjectControllerTest {
|
|||||||
LocalDate.of(2026, 8, 15),
|
LocalDate.of(2026, 8, 15),
|
||||||
LocalDate.of(2026, 9, 30),
|
LocalDate.of(2026, 9, 30),
|
||||||
"Mentor",
|
"Mentor",
|
||||||
"Leader"));
|
"Leader",
|
||||||
|
false));
|
||||||
when(pages.members(20L, 30L)).thenReturn(List.of());
|
when(pages.members(20L, 30L)).thenReturn(List.of());
|
||||||
when(pages.leadership(20L, 30L)).thenReturn(List.of());
|
when(pages.leadership(20L, 30L)).thenReturn(List.of());
|
||||||
|
|
||||||
mvc.perform(get("/projects/30/members"))
|
mvc.perform(get("/projects/30/members"))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andExpect(view().name("projects/members"));
|
.andExpect(view().name("projects/members"))
|
||||||
|
.andExpect(org.springframework.test.web.servlet.result.MockMvcResultMatchers.content()
|
||||||
|
.string(not(containsString("Add member"))));
|
||||||
mvc.perform(get("/projects/30/leadership"))
|
mvc.perform(get("/projects/30/leadership"))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andExpect(view().name("projects/leadership"));
|
.andExpect(view().name("projects/leadership"))
|
||||||
|
.andExpect(org.springframework.test.web.servlet.result.MockMvcResultMatchers.content()
|
||||||
|
.string(not(containsString("Change Leader"))));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockUser(username = "member@example.test")
|
||||||
|
void nonMentorCannotOpenProjectCreationForm() throws Exception {
|
||||||
|
when(pages.authenticatedActor("member@example.test"))
|
||||||
|
.thenReturn(new ProjectActorView(20L, "INTERN"));
|
||||||
|
|
||||||
|
mvc.perform(get("/projects/new"))
|
||||||
|
.andExpect(status().isNotFound());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
Reference in New Issue
Block a user