Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
766b86e
Update changes to cypress tests
ari7946 Aug 28, 2022
852df49
Changes
ari7946 Aug 28, 2022
0dfbf57
Merge branch 'main' of https://github.com/profydev/prolog-app-ari7946…
ari7946 Aug 28, 2022
60f50fd
Merge branch 'main' of https://github.com/profydev/prolog-app-ari7946…
ari7946 Aug 29, 2022
f2a8c6b
Merge branch 'main' of https://github.com/profydev/prolog-app-ari7946…
ari7946 Sep 1, 2022
5e75ea1
Merge branch 'main' of https://github.com/profydev/prolog-app-ari7946…
ari7946 Sep 3, 2022
8f3e6a3
Merge branch 'main' of https://github.com/profydev/prolog-app-ari7946…
ari7946 Sep 3, 2022
cc150a5
Merge branch 'main' of https://github.com/profydev/prolog-app-ari7946…
ari7946 Sep 4, 2022
7493f60
Merge branch 'main' of https://github.com/profydev/prolog-app-ari7946…
ari7946 Sep 7, 2022
44b4219
Merge branch 'main' of https://github.com/profydev/prolog-app-ari7946…
ari7946 Sep 7, 2022
a1a984e
Merge branch 'main' of https://github.com/profydev/prolog-app-ari7946…
ari7946 Sep 7, 2022
3fb2a2e
Merge branch 'main' of https://github.com/profydev/prolog-app-ari7946…
ari7946 Sep 10, 2022
111b8e6
Merge branch 'main' of https://github.com/profydev/prolog-app-ari7946…
ari7946 Sep 10, 2022
86cb66c
Merge branch 'main' of https://github.com/profydev/prolog-app-ari7946…
ari7946 Sep 14, 2022
c470cd8
Merge branch 'main' of https://github.com/profydev/prolog-app-ari7946…
ari7946 Sep 15, 2022
88a4d54
Merge branch 'main' of https://github.com/profydev/prolog-app-ari7946…
ari7946 Sep 15, 2022
a1be63b
Merge branch 'main' of https://github.com/profydev/prolog-app-ari7946…
ari7946 Sep 16, 2022
c8556d6
Merge branch 'main' of https://github.com/profydev/prolog-app-ari7946…
ari7946 Sep 16, 2022
ff03ec2
Merge branch 'main' of https://github.com/profydev/prolog-app-ari7946…
ari7946 Sep 16, 2022
64b895b
Merge branch 'main' of https://github.com/profydev/prolog-app-ari7946…
ari7946 Sep 18, 2022
65729df
Merge branch 'main' of https://github.com/profydev/prolog-app-ari7946…
ari7946 Sep 20, 2022
689a8ff
Merge branch 'main' of https://github.com/profydev/prolog-app-ari7946…
ari7946 Sep 21, 2022
50b3e35
Set hardcoded filters, testing out API
ari7946 Sep 23, 2022
2be4fea
Merge branch 'main' of https://github.com/profydev/prolog-app-ari7946…
ari7946 Oct 11, 2022
109ae2d
Merge branch 'main' of https://github.com/profydev/prolog-app-ari7946…
ari7946 Oct 15, 2022
592c26e
Merge branch 'main' of https://github.com/profydev/prolog-app-ari7946…
ari7946 Nov 3, 2022
2ba6d4b
Refactor to use filter values from the URL instead of keeping them in…
ari7946 Jan 10, 2023
af24c44
Introduce functions for getting default Level and Status values
ari7946 Jan 10, 2023
3013505
Replace autocomplete with debounce for project name input. Remove com…
ari7946 Jan 11, 2023
f005efa
Remove programatic styles and fix CSS zindex bug
ari7946 Jan 11, 2023
4e8146e
Update Input and Select stlyes for Filters component. Remove unused f…
ari7946 Jan 11, 2023
c7808f3
Refactor use-issues to clean up params, signal, and add relolve issue…
ari7946 Jan 12, 2023
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
18 changes: 8 additions & 10 deletions cypress/integration/issue-list.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ describe("Issue List", () => {

// type 'back', which is partial for "backend"
cy.get("@filter-input").type("back");
cy.wait(2000);
cy.dataCy("filter-by-level").click();

// set level to warning
Expand All @@ -154,38 +155,35 @@ describe("Issue List", () => {
cy.validateIssues(mockIssuesByErrorLevel);
});

it("should update URL with the correct project filters/url parameters", () => {
it.only("should update URL with the correct project filters/url parameters", () => {
// Click select component
cy.dataCy("filter-by-status").click();
// Select Resolved
cy.contains("Resolved").click();
cy.url().should("include", "/dashboard/issues?page=1&status=resolved");
cy.url().should("include", "/dashboard/issues?status=resolved");
cy.dataCy("filter-by-level").click();

// Error query param is added to the URL
cy.contains("Error").click();
cy.url().should(
"include",
"/dashboard/issues?page=1&status=resolved&level=error"
"/dashboard/issues?status=resolved&level=error"
);

cy.dataCy("filter-by-status").click();
// Removes status filter
cy.contains("--None--").click();
cy.url().should("include", "/dashboard/issues?page=1&level=error");
cy.url().should("include", "/dashboard/issues?level=error");
cy.dataCy("filter-by-level").click();
cy.contains("Warning").click();
cy.url().should("include", "/issues?page=1&level=warning");
cy.url().should("include", "/issues?level=warning");

cy.dataCy("filter-by-project").within(() => {
cy.get("input").type("Back");
});
cy.url().should(
"include",
"/issues?page=1&level=warning&project=backend"
);
cy.url().should("include", "/issues?level=warning&project=backend");

// Adds page 2 too the url
// Adds page 2 to the url
cy.get("@next-button").click();
cy.url().should(
"include",
Expand Down
53 changes: 38 additions & 15 deletions features/issues/api/use-issues.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,57 @@ import type { Issue } from "../types/issue.types";
import { useFilters } from "@features/issues";
import { IssueFilters } from "@features/issues";

async function getIssues(page: number, filters: IssueFilters) {
const { data } = await axios.get(
`https://prolog-api.profy.dev/issue?page=${page}`,
{ params: filters }
export async function getIssues(
page: number,
filters: IssueFilters,
options?: { signal?: AbortSignal }
) {
const { data } = await axios.get<Page<Issue>>(
"https://prolog-api.profy.dev/issue",
{
params: { page, ...filters },
signal: options?.signal,
}
);
return data;
}

export async function resolveIssue(issueId: string) {
const { data } = await axios.patch(
`https://prolog-api.profy.dev/issue/${issueId}`,
{
status: "resolved",
}
);
return data;
}

const QUERY_KEY = "issues";

export function getQueryKey(page?: number, filters?: IssueFilters) {
if (page === undefined) {
return [QUERY_KEY];
}
return [QUERY_KEY, page, filters];
}

export function useIssues(page: number) {
const { filters } = useFilters();

// console.log('filters', filters);
const query = useQuery<Page<Issue>, Error>(
["issues", page, filters],
() => getIssues(page, filters),
{
keepPreviousData: true,
staleTime: 60000,
}
getQueryKey(page, filters),
({ signal }) => getIssues(page, filters, { signal }),
{ keepPreviousData: true }
);

// Prefetch the next page!
const queryClient = useQueryClient();
useEffect(() => {
if (query.data?.meta.hasNextPage) {
queryClient.prefetchQuery(["projects", page + 1, filters], () =>
getIssues(page + 1, filters)
queryClient.prefetchQuery(getQueryKey(page + 1, filters), ({ signal }) =>
getIssues(page + 1, filters, { signal })
);
}
}, [query.data, page, queryClient, filters]);

}, [query.data, page, filters, queryClient]);
return query;
}
162 changes: 60 additions & 102 deletions features/issues/components/filters/filters.tsx
Original file line number Diff line number Diff line change
@@ -1,41 +1,49 @@
import React, {
useState,
useEffect,
useCallback,
useRef,
useContext,
} from "react";
import React, { useState, useCallback } from "react";
import styled from "styled-components";
import { capitalize } from "lodash";

import { Select, Option, Input, Button, IconOptions } from "@features/ui";
import {
Select,
Option,
Input,
Button,
IconOptions,
NavigationContext,
} from "@features/ui";
import { useFilters, IssueLevel, IssueStatus } from "@features/issues";
import { useProjects } from "@features/projects";
useFilters,
IssueLevel,
IssueStatus,
IssueFilters,
} from "@features/issues";
import { useDebouncedCallback } from "use-debounce";

import { breakpoint } from "@styles/theme";
import { useWindowSize } from "react-use";

import { useRouter } from "next/router";

const Container = styled.div`
display: flex;
flex-direction: column;
justify-content: center;
margin-block: 1rem;
gap: 1rem;
width: 100%;
@media (min-width: ${breakpoint("desktop")}) {
flex-direction: row;
justify-content: space-between;
order: initial;
gap: 1rem;
gap: 3rem;
flex-wrap: wrap;
}
`;

export const FilterSelect = styled(Select)`
width: 100%;
@media (min-width: ${breakpoint("desktop")}) {
width: 10rem;
}
`;

export const FilterInput = styled(Input)`
width: 100%;
@media (min-width: ${breakpoint("desktop")}) {
width: 17.5rem;
}
`;

const RightContainer = styled.div`
margin-bottom: 1rem;
display: flex;
Expand All @@ -49,34 +57,33 @@ const RightContainer = styled.div`
}
`;

const getStatusDefaultValue = (filters: IssueFilters) => {
if (!filters.status) {
return "Status";
}
if (filters.status === IssueStatus.open) {
return "Unresolved";
}
return "Resolved";
};

const getLevelDefaultValue = (filters: IssueFilters) => {
if (!filters.level) {
return "Level";
}
return capitalize(filters.level);
};

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 debouncedHandleFilters = useDebouncedCallback(handleFilters, 300);
const [inputValue, setInputValue] = useState(filters.project || "");
const { width } = useWindowSize();
const isMobileScreen = width <= 1023;
const { isMobileMenuOpen } = useContext(NavigationContext);

const handleChange = (input: string) => {
setInputValue(input);

if (inputValue?.length < 2) {
handleProjectName(undefined);
return;
}

const name = projectNames?.find((name) =>
name?.toLowerCase().includes(inputValue.toLowerCase())
);

if (name) {
handleProjectName(name);
}
const handleChange = (project: string) => {
setInputValue(project);
debouncedHandleFilters({ project });
};

const handleLevel = (level?: string) => {
Expand All @@ -96,64 +103,26 @@ export function Filters() {
handleFilters({ 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]);
// const handleProjectName = useCallback(
// (projectName) => handleFilters({ project: projectName?.toLowerCase() }),
// [handleFilters]
// );

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
<FilterSelect
placeholder="Status"
defaultValue="Status"
defaultValue={getStatusDefaultValue(filters)}
width={isMobileScreen ? "97%" : "8rem"}
data-cy="filter-by-status"
style={{
...(isMobileMenuOpen && {
opacity: 0,
}),
}}
>
<Option value={undefined} handleCallback={handleStatus}>
--None--
Expand All @@ -164,18 +133,13 @@ export function Filters() {
<Option value="Resolved" handleCallback={handleStatus}>
Resolved
</Option>
</Select>
</FilterSelect>

<Select
<FilterSelect
placeholder="Level"
defaultValue="Level"
defaultValue={getLevelDefaultValue(filters)}
width={isMobileScreen ? "97%" : "8rem"}
data-cy="filter-by-level"
style={{
...(isMobileMenuOpen && {
opacity: 0,
}),
}}
>
<Option value={undefined} handleCallback={handleLevel}>
--None--
Expand All @@ -189,21 +153,15 @@ export function Filters() {
<Option value="Info" handleCallback={handleLevel}>
Info
</Option>
</Select>
</FilterSelect>

<Input
<FilterInput
handleChange={handleChange}
value={inputValue}
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
21 changes: 18 additions & 3 deletions features/issues/hooks/use-filters.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,19 @@
import { useContext } from "react";
import { FiltersContext } from "../context/filters-context";
import { useRouter } from "next/router";
import { IssueFilters } from "../types/issue.types";

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

const filters = {
status: router.query.status,
level: router.query.level,
project: router.query.project,
} as IssueFilters;

const handleFilters = (newFilters: IssueFilters) => {
const query = { ...router.query, ...newFilters };
router.push({ query });
};

return { filters, handleFilters };
};
Loading