diff --git a/backend/svalyn-studio-application/src/main/java/com/svalyn/studio/application/controllers/history/ChangeProposalController.java b/backend/svalyn-studio-application/src/main/java/com/svalyn/studio/application/controllers/history/ChangeProposalController.java index a2e4867..effa004 100644 --- a/backend/svalyn-studio-application/src/main/java/com/svalyn/studio/application/controllers/history/ChangeProposalController.java +++ b/backend/svalyn-studio-application/src/main/java/com/svalyn/studio/application/controllers/history/ChangeProposalController.java @@ -32,6 +32,7 @@ import com.svalyn.studio.application.controllers.history.dto.UpdateChangeProposalStatusInput; import com.svalyn.studio.application.controllers.project.dto.ProjectDTO; import com.svalyn.studio.application.services.history.api.IChangeProposalService; +import com.svalyn.studio.domain.history.ChangeProposalStatus; import graphql.relay.Connection; import graphql.relay.DefaultConnection; import graphql.relay.DefaultConnectionCursor; @@ -44,6 +45,7 @@ import org.springframework.graphql.data.method.annotation.SchemaMapping; import org.springframework.stereotype.Controller; +import java.util.List; import java.util.Objects; import java.util.UUID; @@ -62,8 +64,8 @@ public ChangeProposalController(IChangeProposalService changeProposalService) { } @SchemaMapping(typeName = "Project") - public Connection changeProposals(ProjectDTO project, @Argument int page, @Argument int rowsPerPage) { - var pageData = this.changeProposalService.findAllByProjectId(project.id(), page, rowsPerPage); + public Connection changeProposals(ProjectDTO project, @Argument List status, @Argument int page, @Argument int rowsPerPage) { + var pageData = this.changeProposalService.findAllByProjectIdAndStatus(project.id(), status, page, rowsPerPage); var edges = pageData.stream().map(changeProposal -> { var value = new Relay().toGlobalId("ChangeProposal", changeProposal.id().toString()); var cursor = new DefaultConnectionCursor(value); diff --git a/backend/svalyn-studio-application/src/main/java/com/svalyn/studio/application/services/history/ChangeProposalService.java b/backend/svalyn-studio-application/src/main/java/com/svalyn/studio/application/services/history/ChangeProposalService.java index 20a063a..613eb62 100644 --- a/backend/svalyn-studio-application/src/main/java/com/svalyn/studio/application/services/history/ChangeProposalService.java +++ b/backend/svalyn-studio-application/src/main/java/com/svalyn/studio/application/services/history/ChangeProposalService.java @@ -38,6 +38,7 @@ import com.svalyn.studio.domain.Success; import com.svalyn.studio.domain.account.repositories.IAccountRepository; import com.svalyn.studio.domain.history.ChangeProposal; +import com.svalyn.studio.domain.history.ChangeProposalStatus; import com.svalyn.studio.domain.history.Review; import com.svalyn.studio.domain.history.repositories.IChangeProposalRepository; import com.svalyn.studio.domain.history.services.api.IChangeProposalCreationService; @@ -116,12 +117,13 @@ public Optional findById(UUID id) { @Override @Transactional(readOnly = true) - public Page findAllByProjectId(UUID projectId, int page, int rowsPerPage) { - var changesProposals = this.changeProposalRepository.findAllByProjectId(projectId, page * rowsPerPage, rowsPerPage) + public Page findAllByProjectIdAndStatus(UUID projectId, List status, int page, int rowsPerPage) { + var stringStatus = status.stream().map(Object::toString).toList(); + var changesProposals = this.changeProposalRepository.findAllByProjectIdAndStatus(projectId, stringStatus, page * rowsPerPage, rowsPerPage) .stream() .flatMap(changeProposal -> this.toDTO(changeProposal).stream()) .toList(); - var count = this.changeProposalRepository.countAllByProjectId(projectId); + var count = this.changeProposalRepository.countAllByProjectIdAndStatus(projectId, stringStatus); return new PageImpl<>(changesProposals, PageRequest.of(page, rowsPerPage), count); } diff --git a/backend/svalyn-studio-application/src/main/java/com/svalyn/studio/application/services/history/api/IChangeProposalService.java b/backend/svalyn-studio-application/src/main/java/com/svalyn/studio/application/services/history/api/IChangeProposalService.java index c5bc7e7..b813070 100644 --- a/backend/svalyn-studio-application/src/main/java/com/svalyn/studio/application/services/history/api/IChangeProposalService.java +++ b/backend/svalyn-studio-application/src/main/java/com/svalyn/studio/application/services/history/api/IChangeProposalService.java @@ -29,6 +29,7 @@ import com.svalyn.studio.application.controllers.history.dto.UpdateChangeProposalReadMeInput; import com.svalyn.studio.application.controllers.history.dto.UpdateChangeProposalStatusInput; import com.svalyn.studio.application.controllers.dto.IPayload; +import com.svalyn.studio.domain.history.ChangeProposalStatus; import org.springframework.data.domain.Page; import java.util.List; @@ -41,7 +42,7 @@ * @author sbegaudeau */ public interface IChangeProposalService { - Page findAllByProjectId(UUID projectId, int page, int rowsPerPage); + Page findAllByProjectIdAndStatus(UUID projectId, List status, int page, int rowsPerPage); IPayload createChangeProposal(CreateChangeProposalInput input); diff --git a/backend/svalyn-studio-application/src/main/resources/graphql/svalyn-studio.graphqls b/backend/svalyn-studio-application/src/main/resources/graphql/svalyn-studio.graphqls index 0879074..7a9034a 100644 --- a/backend/svalyn-studio-application/src/main/resources/graphql/svalyn-studio.graphqls +++ b/backend/svalyn-studio-application/src/main/resources/graphql/svalyn-studio.graphqls @@ -203,7 +203,7 @@ type Project { activityEntries(page: Int!, rowsPerPage: Int!): ProjectActivityEntriesConnection! branch(name: String!): Branch branches(page: Int!, rowsPerPage: Int!): ProjectBranchesConnection! - changeProposals(page: Int!, rowsPerPage: Int!): ProjectChangeProposalsConnection! + changeProposals(status: [ChangeProposalStatus!]!, page: Int!, rowsPerPage: Int!): ProjectChangeProposalsConnection! } type ProjectTagsConnection { diff --git a/backend/svalyn-studio-domain/src/main/java/com/svalyn/studio/domain/history/repositories/IChangeProposalRepository.java b/backend/svalyn-studio-domain/src/main/java/com/svalyn/studio/domain/history/repositories/IChangeProposalRepository.java index 77edfcb..848901c 100644 --- a/backend/svalyn-studio-domain/src/main/java/com/svalyn/studio/domain/history/repositories/IChangeProposalRepository.java +++ b/backend/svalyn-studio-domain/src/main/java/com/svalyn/studio/domain/history/repositories/IChangeProposalRepository.java @@ -37,16 +37,16 @@ public interface IChangeProposalRepository extends PagingAndSortingRepository, ListCrudRepository { @Query(""" SELECT * FROM change_proposal changeProposal - WHERE changeProposal.project_id = :projectId + WHERE changeProposal.project_id = :projectId AND changeProposal.status IN (:status) ORDER BY changeProposal.created_on DESC LIMIT :limit OFFSET :offset """) - List findAllByProjectId(UUID projectId, long offset, int limit); + List findAllByProjectIdAndStatus(UUID projectId, List status, long offset, int limit); @Query(""" SELECT count(*) FROM change_proposal changeProposal - WHERE changeProposal.project_id = :projectId + WHERE changeProposal.project_id = :projectId AND changeProposal.status IN (:status) """) - long countAllByProjectId(UUID projectId); + long countAllByProjectIdAndStatus(UUID projectId, List status); } diff --git a/frontend/svalyn-studio-app/src/views/changeproposal/overview/ChangeProposalStatus.tsx b/frontend/svalyn-studio-app/src/views/changeproposal/overview/ChangeProposalStatus.tsx index 01fff89..b2bb73c 100644 --- a/frontend/svalyn-studio-app/src/views/changeproposal/overview/ChangeProposalStatus.tsx +++ b/frontend/svalyn-studio-app/src/views/changeproposal/overview/ChangeProposalStatus.tsx @@ -170,7 +170,12 @@ export const ChangeProposalStatus = ({ changeProposal, onStatusUpdated }: Change gap: (theme) => theme.spacing(2), }} > - { const hasReviews = changeProposal.reviews.edges.length > 0; if (!hasReviews) { - return No reviews have been performed yet; + return theme.spacing(2) }}>No reviews have been performed yet; } const approvedReviews = changeProposal.reviews.edges diff --git a/frontend/svalyn-studio-app/src/views/home/HomeViewLeftPanel.tsx b/frontend/svalyn-studio-app/src/views/home/HomeViewLeftPanel.tsx index 4457f5d..f0df523 100644 --- a/frontend/svalyn-studio-app/src/views/home/HomeViewLeftPanel.tsx +++ b/frontend/svalyn-studio-app/src/views/home/HomeViewLeftPanel.tsx @@ -115,7 +115,9 @@ export const HomeViewLeftPanel = ({}: HomeViewLeftPanelProps) => { })} ) : ( - No organization found + theme.spacing(2) }}> + No organization found + )} diff --git a/frontend/svalyn-studio-app/src/views/notifications/NotificationsView.tsx b/frontend/svalyn-studio-app/src/views/notifications/NotificationsView.tsx index aa23a5e..92b67a4 100644 --- a/frontend/svalyn-studio-app/src/views/notifications/NotificationsView.tsx +++ b/frontend/svalyn-studio-app/src/views/notifications/NotificationsView.tsx @@ -292,7 +292,12 @@ export const NotificationsView = () => { /> - + {notification.title} diff --git a/frontend/svalyn-studio-app/src/views/project/changeproposals/ChangeProposalsTableHead.tsx b/frontend/svalyn-studio-app/src/views/project/changeproposals/ChangeProposalsTableHead.tsx index 924a6bf..8459cf8 100644 --- a/frontend/svalyn-studio-app/src/views/project/changeproposals/ChangeProposalsTableHead.tsx +++ b/frontend/svalyn-studio-app/src/views/project/changeproposals/ChangeProposalsTableHead.tsx @@ -22,13 +22,28 @@ import TableCell from '@mui/material/TableCell'; import TableHead from '@mui/material/TableHead'; import TableRow from '@mui/material/TableRow'; +import FormControl from '@mui/material/FormControl'; +import InputLabel from '@mui/material/InputLabel'; +import MenuItem from '@mui/material/MenuItem'; +import Select, { SelectChangeEvent } from '@mui/material/Select'; import { ChangeProposalsTableHeadProps } from './ChangeProposalsTableHead.types'; export const ChangeProposalsTableHead = ({ + filter, + onFilterChange, changeProposalsCount, selectedChangeProposalsCount, onSelectAll, }: ChangeProposalsTableHeadProps) => { + const handleChange = (event: SelectChangeEvent<'OPEN' | 'CLOSED'>) => { + const { + target: { value }, + } = event; + if (value === 'OPEN' || value === 'CLOSED') { + onFilterChange(value); + } + }; + return ( @@ -39,7 +54,15 @@ export const ChangeProposalsTableHead = ({ indeterminate={selectedChangeProposalsCount > 0 && selectedChangeProposalsCount < changeProposalsCount} /> - Name + + theme.spacing(15) }} size="small"> + Status + + + ); diff --git a/frontend/svalyn-studio-app/src/views/project/changeproposals/ChangeProposalsTableHead.types.ts b/frontend/svalyn-studio-app/src/views/project/changeproposals/ChangeProposalsTableHead.types.ts index ae2fada..75774e0 100644 --- a/frontend/svalyn-studio-app/src/views/project/changeproposals/ChangeProposalsTableHead.types.ts +++ b/frontend/svalyn-studio-app/src/views/project/changeproposals/ChangeProposalsTableHead.types.ts @@ -18,7 +18,11 @@ */ export interface ChangeProposalsTableHeadProps { + filter: ChangeProposalStatusFilter; + onFilterChange: (filter: ChangeProposalStatusFilter) => void; changeProposalsCount: number; selectedChangeProposalsCount: number; onSelectAll: (event: React.ChangeEvent, checked: boolean) => void; } + +export type ChangeProposalStatusFilter = 'OPEN' | 'CLOSED'; diff --git a/frontend/svalyn-studio-app/src/views/project/changeproposals/ProjectChangeProposal.tsx b/frontend/svalyn-studio-app/src/views/project/changeproposals/ProjectChangeProposal.tsx index f343165..db0a962 100644 --- a/frontend/svalyn-studio-app/src/views/project/changeproposals/ProjectChangeProposal.tsx +++ b/frontend/svalyn-studio-app/src/views/project/changeproposals/ProjectChangeProposal.tsx @@ -40,6 +40,7 @@ import { ChangeProposalsTableHead } from './ChangeProposalsTableHead'; import { ChangeProposalsTableToolbar } from './ChangeProposalsTableToolbar'; import { ChangeProposal, + ChangeProposalStatusFilter, DeleteChangeProposalsData, DeleteChangeProposalsVariables, ErrorPayload, @@ -50,10 +51,10 @@ import { } from './ProjectChangeProposal.types'; const getChangeProposalsQuery = gql` - query getChangeProposals($identifier: ID!, $page: Int!, $rowsPerPage: Int!) { + query getChangeProposals($identifier: ID!, $status: [ChangeProposalStatus!]!, $page: Int!, $rowsPerPage: Int!) { viewer { project(identifier: $identifier) { - changeProposals(page: $page, rowsPerPage: $rowsPerPage) { + changeProposals(status: $status, page: $page, rowsPerPage: $rowsPerPage) { edges { node { id @@ -83,6 +84,7 @@ export const ProjectChangeProposal = ({ projectIdentifier, role }: ProjectChange const [state, setState] = useState({ project: null, selectedChangeProposalIds: [], + filter: 'OPEN', page: 0, rowsPerPage: 10, message: null, @@ -90,6 +92,7 @@ export const ProjectChangeProposal = ({ projectIdentifier, role }: ProjectChange const variables: GetChangeProposalsVariables = { identifier: projectIdentifier, + status: state.filter === 'OPEN' ? ['OPEN'] : ['INTEGRATED', 'CLOSED'], page: state.page, rowsPerPage: state.rowsPerPage, }; @@ -187,6 +190,9 @@ export const ProjectChangeProposal = ({ projectIdentifier, role }: ProjectChange setState((prevState) => ({ ...prevState, page })); }; + const handleFilterChange = (filter: ChangeProposalStatusFilter) => + setState((prevState) => ({ ...prevState, filter })); + const handleCloseSnackbar = () => setState((prevState) => ({ ...prevState, message: null })); const changeProposals = state.project?.changeProposals.edges.map((edge) => edge.node) ?? []; @@ -214,42 +220,47 @@ export const ProjectChangeProposal = ({ projectIdentifier, role }: ProjectChange New Change Proposal - {state.project && changeProposals.length > 0 ? ( - theme.spacing(4) }}> - - - - - - {changeProposals.map((changeProposal) => { - const isChangeProposalSelected = state.selectedChangeProposalIds.includes(changeProposal.id); - return ( - selectChangeProposal(event, changeProposal)} - > - - - - - - {changeProposal.name} - - - - ); - })} - -
-
+ + theme.spacing(4) }}> + + + + + + {changeProposals.map((changeProposal) => { + const isChangeProposalSelected = state.selectedChangeProposalIds.includes(changeProposal.id); + return ( + selectChangeProposal(event, changeProposal)}> + + + + + + {changeProposal.name} + + + + ); + })} + +
+
+ {state.project && changeProposals.length > 0 ? ( -
- ) : ( - theme.spacing(12) }}> - - No change proposals found - - - )} + ) : ( + theme.spacing(12) }}> + + No change proposals found + + + )} +
diff --git a/frontend/svalyn-studio-app/src/views/project/changeproposals/ProjectChangeProposal.types.ts b/frontend/svalyn-studio-app/src/views/project/changeproposals/ProjectChangeProposal.types.ts index 861a20b..87388ac 100644 --- a/frontend/svalyn-studio-app/src/views/project/changeproposals/ProjectChangeProposal.types.ts +++ b/frontend/svalyn-studio-app/src/views/project/changeproposals/ProjectChangeProposal.types.ts @@ -26,12 +26,15 @@ export type MembershipRole = 'ADMIN' | 'MEMBER' | 'NONE'; export interface ProjectChangeProposalState { project: Project | null; + filter: ChangeProposalStatusFilter; selectedChangeProposalIds: string[]; page: number; rowsPerPage: number; message: string | null; } +export type ChangeProposalStatusFilter = 'OPEN' | 'CLOSED'; + export interface GetChangeProposalsData { viewer: Viewer; } @@ -64,10 +67,13 @@ export interface ChangeProposal { export interface GetChangeProposalsVariables { identifier: string; + status: ChangeProposalStatus[]; page: number; rowsPerPage: number; } +export type ChangeProposalStatus = 'OPEN' | 'CLOSED' | 'INTEGRATED'; + export interface DeleteChangeProposalsData { deleteChangeProposals: DeleteChangeProposalsPayload; }