Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
199 changes: 66 additions & 133 deletions features/issues/components/filters/filters.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,40 @@
import React, {
useState,
useEffect,
useCallback,
useRef,
useContext,
} from "react";
import React, { useState } from "react";
import styled from "styled-components";
import { useDebouncedCallback } from "use-debounce";
import {
Select,
Select as UnstyledSelect,
Option,
Input,
Button,
Button as UnstyledButton,
IconOptions,
NavigationContext,
Input as UnstyledInput,
} from "@features/ui";
import { useFilters, IssueLevel, IssueStatus } from "@features/issues";
import { useProjects } from "@features/projects";
import { useFilters } from "../../hooks";
import { IssueLevel, IssueStatus } from "../../types/issue.types";
import { breakpoint } from "@styles/theme";
import { useWindowSize } from "react-use";
import { OptionType } from "@features/ui/form/select/select";

const statusOptions = [
{ value: undefined, text: "--None--" },
{ value: IssueStatus.open, text: "Unresolved" },
{ value: IssueStatus.resolved, text: "Resolved" },
] as OptionType[];

const levelOptions = [
{ value: undefined, text: "--None--" },
{ value: IssueLevel.error, text: "Error" },
{ value: IssueLevel.warning, text: "Warning" },
{ value: IssueLevel.info, text: "Info" },
] as OptionType[];

const Button = styled(UnstyledButton)`
height: 2.5rem;
min-width: 8rem;
width: 100%;

import { useRouter } from "next/router";
@media (min-width: ${breakpoint("desktop")}) {
width: auto;
}
`;

const Container = styled.div`
display: flex;
Expand Down Expand Up @@ -49,161 +64,79 @@ const RightContainer = styled.div`
}
`;

export function Filters() {
const { handleFilters, filters } = useFilters();
const { data: projects } = useProjects();
const router = useRouter();
const routerQueryProjectName =
(router.query.projectName as string)?.toLowerCase() || undefined;
const [inputValue, setInputValue] = useState<string>("");
const projectNames = projects?.map((project) => project.name.toLowerCase());
const isFirst = useRef(true);
const { width } = useWindowSize();
const isMobileScreen = width <= 1023;
const { isMobileMenuOpen } = useContext(NavigationContext);
const Select = styled(UnstyledSelect)`
width: 100%;

const handleChange = (input: string) => {
setInputValue(input);
@media (min-width: ${breakpoint("desktop")}) {
width: 8rem;
}
`;

if (inputValue?.length < 2) {
handleProjectName(undefined);
return;
}
const Input = styled(UnstyledInput)`
width: 100%;
box-sizing: border-box;
`;

const name = projectNames?.find((name) =>
name?.toLowerCase().includes(inputValue.toLowerCase())
);
export function Filters() {
const { updateFilters, filters } = useFilters();
const [project, setProject] = useState(filters.project);
const debouncedUpdateFilters = useDebouncedCallback(updateFilters, 300);

if (name) {
handleProjectName(name);
}
const handleChange = (input: string) => {
setProject(input);
debouncedUpdateFilters({ project: input });
};

const handleLevel = (level?: string) => {
if (level) {
level = level.toLowerCase();
}
handleFilters({ level: level as IssueLevel });
updateFilters({ level: level as IssueLevel });
};

const handleStatus = (status?: string) => {
if (status === "Unresolved") {
status = "open";
}
if (status) {
status = status.toLowerCase();
}
handleFilters({ status: status as IssueStatus });
updateFilters({ status: status as IssueStatus });
};

const handleProjectName = useCallback(
(projectName) => handleFilters({ project: projectName?.toLowerCase() }),
[handleFilters]
);

useEffect(() => {
const newObj: { [key: string]: string } = {
...filters,
};

Object.keys(newObj).forEach((key) => {
if (newObj[key] === undefined) {
delete newObj[key];
}
});

const url = {
pathname: router.pathname,
query: {
page: router.query.page || 1,
...newObj,
},
};

if (routerQueryProjectName && isFirst) {
handleProjectName(routerQueryProjectName);
setInputValue(routerQueryProjectName || "");
isFirst.current = false;
}

router.push(url, undefined, { shallow: false });
}, [filters.level, filters.status, filters.project, router.query.page]);

return (
<Container>
<Button
iconSrc="/icons/select-icon-white.svg"
iconOptions={IconOptions.leading}
style={{
height: "2.5rem",
minWidth: "8rem",
...(isMobileScreen && { width: "100%" }),
}}
>
Resolve selected issues
</Button>

<RightContainer>
<Select
placeholder="Status"
defaultValue="Status"
width={isMobileScreen ? "97%" : "8rem"}
data-cy="filter-by-status"
style={{
...(isMobileMenuOpen && {
opacity: 0,
}),
}}
value={filters.status}
onChange={handleStatus}
options={statusOptions}
>
<Option value={undefined} handleCallback={handleStatus}>
--None--
</Option>
<Option value="Unresolved" handleCallback={handleStatus}>
Unresolved
</Option>
<Option value="Resolved" handleCallback={handleStatus}>
Resolved
</Option>
{statusOptions.map((option) => (
<Option key={option.value} value={option.value}>
{option.text}
</Option>
))}
</Select>

<Select
placeholder="Level"
defaultValue="Level"
width={isMobileScreen ? "97%" : "8rem"}
data-cy="filter-by-level"
style={{
...(isMobileMenuOpen && {
opacity: 0,
}),
}}
value={filters.level}
onChange={handleLevel}
options={levelOptions}
>
<Option value={undefined} handleCallback={handleLevel}>
--None--
</Option>
<Option value="Error" handleCallback={handleLevel}>
Error
</Option>
<Option value="Warning" handleCallback={handleLevel}>
Warning
</Option>
<Option value="Info" handleCallback={handleLevel}>
Info
</Option>
{levelOptions.map((option) => (
<Option key={option.value} value={option.value}>
{option.text}
</Option>
))}
</Select>

<Input
handleChange={handleChange}
value={inputValue}
value={project || ""}
label="project name"
placeholder="Project Name"
iconSrc="/icons/search-icon.svg"
data-cy="filter-by-project"
style={{
...(isMobileScreen && { width: "94%", marginRight: "3rem" }),
...(isMobileMenuOpen && {
opacity: 0,
}),
}}
/>
</RightContainer>
</Container>
Expand Down
45 changes: 0 additions & 45 deletions features/issues/context/filters-context.tsx

This file was deleted.

1 change: 0 additions & 1 deletion features/issues/context/index.ts

This file was deleted.

27 changes: 24 additions & 3 deletions features/issues/hooks/use-filters.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,25 @@
import { useContext } from "react";
import { FiltersContext } from "../context/filters-context";
import { useRouter } from "next/router";
import { IssueLevel, IssueStatus } from "../types/issue.types";

export const useFilters = () => useContext(FiltersContext);
type Filters = {
status: IssueStatus;
level: IssueLevel;
project: string;
};

export const useFilters = () => {
const router = useRouter();

const filters = {
status: router.query.status as IssueStatus,
level: router.query.level as IssueLevel,
project: router.query.project,
} as Filters;

const updateFilters = (newFilters: Partial<Filters>) => {
const query = { ...router.query, ...newFilters };
router.push({ query });
};

return { filters, updateFilters };
};
1 change: 0 additions & 1 deletion features/issues/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
export * from "./api/use-issues";
export * from "./components/IssueList";
export * from "./types/issue.types";
export * from "./context";
export * from "./hooks";
1 change: 0 additions & 1 deletion features/ui/form/input/input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ const InputContainer = styled.input<{
border-color: ${({ errorMessage, error }) =>
errorMessage || error ? color("error", 300) : color("gray", 300)};
border-radius: 7px;
width: calc(${space(20)} * 4 - ${space(6)});
padding: ${space(2, 3)};
letter-spacing: 0.05rem;
color: ${color("gray", 900)};
Expand Down
3 changes: 2 additions & 1 deletion features/ui/form/select/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { Option } from "./option";
export { Select } from "./select";
export { SelectContext, useSelectContext } from "./selectContext";
export { useSelectContext } from "./selectContext";
export type { OptionType } from "./select";
Loading