Skip to content

Commit

Permalink
feat: my tasks filters
Browse files Browse the repository at this point in the history
  • Loading branch information
stepan662 committed Jul 29, 2024
1 parent d3e2bb4 commit 7bf8c80
Show file tree
Hide file tree
Showing 17 changed files with 744 additions and 71 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import io.tolgee.activity.data.ActivityType
import io.tolgee.constants.Message
import io.tolgee.dtos.request.project.CreateProjectRequest
import io.tolgee.dtos.request.project.EditProjectRequest
import io.tolgee.dtos.request.project.ProjectFilters
import io.tolgee.dtos.request.project.SetPermissionLanguageParams
import io.tolgee.exceptions.BadRequestException
import io.tolgee.facade.ProjectPermissionFacade
Expand Down Expand Up @@ -114,10 +115,13 @@ class ProjectsController(
@AllowApiAccess(tokenType = AuthTokenType.ONLY_PAT)
@OpenApiOrderExtension(3)
fun getAll(
@ParameterObject pageable: Pageable,
@ParameterObject
filters: ProjectFilters,
@ParameterObject
pageable: Pageable,
@RequestParam("search") search: String?,
): PagedModel<ProjectModel> {
val projects = projectService.findPermittedInOrganizationPaged(pageable, search)
val projects = projectService.findPermittedInOrganizationPaged(pageable, search, filters=filters)
return arrayResourcesAssembler.toModel(projects, projectModelAssembler)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package io.tolgee.dtos.request.project

import io.swagger.v3.oas.annotations.Parameter

open class ProjectFilters {
@field:Parameter(
description = """Filter projects by id""",
)
var filterId: List<Long>? = null

@field:Parameter(
description = """Filter projects without id""",
)
var filterNotId: List<Long>? = null
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ interface LanguageRepository : JpaRepository<Language, Long> {
fun findAllByProjectId(
projectId: Long?,
pageable: Pageable,
filters: LanguageFilters? = null
filters: LanguageFilters
): Page<LanguageDto>

fun findAllByTagInAndProjectId(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.tolgee.repository

import io.tolgee.dtos.request.project.ProjectFilters
import io.tolgee.model.Organization
import io.tolgee.model.Project
import io.tolgee.model.views.ProjectView
Expand All @@ -25,6 +26,17 @@ interface ProjectRepository : JpaRepository<Project, Long> {
left join fetch o.basePermission
left join OrganizationRole role on role.organization = o and role.user.id = :userAccountId
"""

const val FILTERS = """
(
:#{#filters.filterId} is null
or r.id in :#{#filters.filterId}
)
and (
:#{#filters.filterNotId} is null
or r.id not in :#{#filters.filterNotId}
)
"""
}

@Query(
Expand Down Expand Up @@ -52,13 +64,22 @@ interface ProjectRepository : JpaRepository<Project, Long> {
or lower(o.name) like lower(concat('%', cast(:search as text),'%')))
)
and (:organizationId is null or o.id = :organizationId) and r.deletedAt is null
and (
:#{#filters.filterId} is null
or r.id in :#{#filters.filterId}
)
and (
:#{#filters.filterNotId} is null
or r.id not in :#{#filters.filterNotId}
)
""",
)
fun findAllPermitted(
userAccountId: Long,
pageable: Pageable,
@Param("search") search: String? = null,
organizationId: Long? = null,
filters: ProjectFilters
): Page<ProjectView>

fun findAllByOrganizationOwnerId(organizationOwnerId: Long): List<Project>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -339,9 +339,9 @@ class LanguageService(
fun getPaged(
projectId: Long,
pageable: Pageable,
filters: LanguageFilters? = null
filters: LanguageFilters?
): Page<LanguageDto> {
return this.languageRepository.findAllByProjectId(projectId, pageable, filters)
return this.languageRepository.findAllByProjectId(projectId, pageable, filters ?: LanguageFilters())
}

fun findByIdIn(ids: Iterable<Long>): List<Language> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import io.tolgee.dtos.cacheable.LanguageDto
import io.tolgee.dtos.cacheable.ProjectDto
import io.tolgee.dtos.request.project.CreateProjectRequest
import io.tolgee.dtos.request.project.EditProjectRequest
import io.tolgee.dtos.request.project.ProjectFilters
import io.tolgee.dtos.response.ProjectDTO
import io.tolgee.dtos.response.ProjectDTO.Companion.fromEntityAndPermission
import io.tolgee.exceptions.BadRequestException
Expand Down Expand Up @@ -364,12 +365,14 @@ class ProjectService(
pageable: Pageable,
search: String?,
organizationId: Long? = null,
filters: ProjectFilters? = null,
): Page<ProjectWithLanguagesView> {
return findPermittedInOrganizationPaged(
pageable = pageable,
search = search,
organizationId = organizationId,
userAccountId = authenticationFacade.authenticatedUser.id,
filters = filters
)
}

Expand All @@ -378,13 +381,15 @@ class ProjectService(
search: String?,
organizationId: Long? = null,
userAccountId: Long,
filters: ProjectFilters? = ProjectFilters()
): Page<ProjectWithLanguagesView> {
val withoutPermittedLanguages =
projectRepository.findAllPermitted(
userAccountId,
pageable,
search,
organizationId,
filters ?: ProjectFilters()
)
return addPermittedLanguagesToProjects(withoutPermittedLanguages, userAccountId)
}
Expand Down
103 changes: 103 additions & 0 deletions webapp/src/component/projectSearchSelect/ProjectSearchSelect.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { useRef, useState } from 'react';
import { ArrowDropDown, Clear } from '@mui/icons-material';
import { Box, styled, IconButton, SxProps } from '@mui/material';

import { stopAndPrevent } from 'tg.fixtures/eventHandler';
import { TextField } from 'tg.component/common/TextField';
import { ProjectSearchSelectPopover } from './ProjectSearchSelectPopover';
import { Project } from './types';
import { FakeInput } from 'tg.component/FakeInput';

const StyledClearButton = styled(IconButton)`
margin: ${({ theme }) => theme.spacing(-1, -0.5, -1, -0.25)};
`;

type Props = {
value: Project[];
onChange?: (projects: Project[]) => void;
label?: React.ReactNode;
sx?: SxProps;
className?: string;
};

export const ProjectSearchSelect: React.FC<Props> = ({
value,
onChange,
label,
sx,
className,
}) => {
const anchorEl = useRef<HTMLAnchorElement>(null);
const [isOpen, setIsOpen] = useState(false);

const handleClose = () => {
setIsOpen(false);
};

const handleClick = () => {
setIsOpen(true);
};

const handleSelectOrganization = async (projects: Project[]) => {
onChange?.(projects);
setIsOpen(false);
};

const handleClear = () => {
onChange?.([]);
};

return (
<>
<Box display="grid" {...{ sx, className }}>
<TextField
variant="outlined"
value={value.map((u) => u.name).join(', ')}
data-cy="assignee-select"
minHeight={false}
label={label}
InputProps={{
onClick: handleClick,
ref: anchorEl,
fullWidth: true,
sx: {
cursor: 'pointer',
},
readOnly: true,
inputComponent: FakeInput,
margin: 'dense',
endAdornment: (
<Box sx={{ display: 'flex', marginRight: -0.5 }}>
{Boolean(value.length) && (
<StyledClearButton
size="small"
onClick={stopAndPrevent(handleClear)}
tabIndex={-1}
>
<Clear fontSize="small" />
</StyledClearButton>
)}
<StyledClearButton
size="small"
onClick={handleClick}
tabIndex={-1}
sx={{ pointerEvents: 'none' }}
>
<ArrowDropDown />
</StyledClearButton>
</Box>
),
}}
/>

<ProjectSearchSelectPopover
open={isOpen}
onClose={handleClose}
selected={value}
onSelect={handleSelectOrganization}
anchorEl={anchorEl.current!}
/>
</Box>
</>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React from 'react';
import { Box, styled } from '@mui/material';

import { AvatarImg } from 'tg.component/common/avatar/AvatarImg';
import { Project } from './types';

const StyledOrgItem = styled('div')`
display: grid;
grid-auto-flow: column;
gap: 6px;
align-items: center;
text: ${({ theme }) => theme.palette.primaryText};
`;

type Props = {
data: Project;
size?: number;
};

export const ProjectSearchSelectItem: React.FC<Props> = ({
data,
size = 24,
}) => {
return (
<StyledOrgItem>
<Box>
<AvatarImg
key={0}
owner={{
name: data.name,
avatar: data.avatar,
type: 'PROJECT',
id: data.id,
}}
size={size}
/>
</Box>
<Box>{data.name}</Box>
</StyledOrgItem>
);
};
Loading

0 comments on commit 7bf8c80

Please sign in to comment.