Skip to content

Commit

Permalink
Project filters
Browse files Browse the repository at this point in the history
  • Loading branch information
mbarrenechea committed Nov 16, 2023
1 parent a22e913 commit e712eb1
Show file tree
Hide file tree
Showing 18 changed files with 1,724 additions and 558 deletions.
2 changes: 1 addition & 1 deletion client/orval.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ module.exports = {
input: {
target: "../cms/src/extensions/documentation/documentation/1.0.0/full_documentation.json",
filters: {
tags: ["Project", "Dataset", "Layer", "Country", "Category"],
tags: ["Project", "Dataset", "Layer", "Country", "Category", "Pillar", "Sdg"],
},
},
},
Expand Down
7 changes: 5 additions & 2 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,16 @@
"@dnd-kit/modifiers": "^6.0.1",
"@dnd-kit/sortable": "^7.0.2",
"@dnd-kit/utilities": "^3.2.1",
"@hookform/resolvers": "^3.3.2",
"@radix-ui/react-accordion": "^1.1.2",
"@radix-ui/react-checkbox": "1.0.4",
"@radix-ui/react-dialog": "^1.0.5",
"@radix-ui/react-label": "2.0.2",
"@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-popover": "1.0.7",
"@radix-ui/react-radio-group": "1.1.3",
"@radix-ui/react-scroll-area": "^1.0.5",
"@radix-ui/react-slider": "^1.1.2",
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-switch": "^1.0.3",
"@radix-ui/react-tooltip": "1.0.7",
"@svgr/webpack": "^8.1.0",
Expand Down Expand Up @@ -57,6 +59,7 @@
"postcss": "8.4.30",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-hook-form": "^7.48.2",
"react-icons": "4.11.0",
"react-map-gl": "7.1.6",
"react-markdown": "^9.0.0",
Expand All @@ -65,7 +68,7 @@
"tailwindcss": "3.3.3",
"tailwindcss-animate": "1.0.7",
"typescript": "5.2.2",
"zod": "3.22.2"
"zod": "^3.22.4"
},
"devDependencies": {
"@playwright/test": "1.38.1",
Expand Down
7 changes: 5 additions & 2 deletions client/src/app/(app)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,12 @@ export default async function AppLayout({ children }: PropsWithChildren) {
}

// Prefetch projects

await queryClient.prefetchQuery({
...getGetProjectsQueryOptions(GET_PROJECTS_OPTIONS("")),
...getGetProjectsQueryOptions(
GET_PROJECTS_OPTIONS("", {
pillars: [],
}),
),
});

const dehydratedState = dehydrate(queryClient);
Expand Down
4 changes: 2 additions & 2 deletions client/src/app/(app)/projects/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Projects from "@/containers/projects";
import ProjectsFilters from "@/containers/projects/filters";
import ProjectsHeader from "@/containers/projects/header";
import ProjectPopup from "@/containers/projects/popup";
import ProjectsSearch from "@/containers/projects/search";

export default function ProjectsPage() {
return (
Expand All @@ -12,7 +12,7 @@ export default function ProjectsPage() {
<h1 className="font-metropolis text-3xl tracking-tight">Projects</h1>

<div className="space-y-5">
<ProjectsSearch />
<ProjectsFilters />
<ProjectsHeader />
<Projects />
</div>
Expand Down
17 changes: 13 additions & 4 deletions client/src/app/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ export const useSyncCountriesComparison = () => {
export const useSyncProject = () => {
return useQueryState("project", parseAsInteger);
};
const pillarsParser = parseAsArrayOf(parseAsInteger).withDefault([]);
export const useSyncPillars = () => {
return useQueryState("pillars", pillarsParser);
};

export const useSyncSearchParams = () => {
const [datasets] = useSyncDatasets();
Expand All @@ -61,15 +65,20 @@ export const useSyncSearchParams = () => {
const [country] = useSyncCountry();
const [countriesComparison] = useSyncCountriesComparison();
const [project] = useSyncProject();
const [pillars] = useSyncPillars();

const sp = new URLSearchParams();

// Default
// Datatsets
if (datasetsParser.defaultValue !== datasets)
sp.set("datasets", datasetsParser.serialize(datasets));

// Layers
if (layersParser.defaultValue !== layers) sp.set("layers", layersParser.serialize(layers));
if (bboxParser.defaultValue !== bbox) sp.set("bbox", bboxParser.serialize(bbox));
if (layersSettings) sp.set("layers-settings", layersSettingsParser.serialize(layersSettings));

// Map
if (bboxParser.defaultValue !== bbox) sp.set("bbox", bboxParser.serialize(bbox));
if (mapSettingsParser.defaultValue !== mapSettings)
sp.set("map-settings", mapSettingsParser.serialize(mapSettings));

Expand All @@ -78,9 +87,9 @@ export const useSyncSearchParams = () => {
if (countriesComparisonParser.defaultValue !== countriesComparison)
sp.set("countries-comparison", countriesComparisonParser.serialize(countriesComparison));

// No default values
if (layersSettings) sp.set("layers-settings", layersSettingsParser.serialize(layersSettings));
// Project
if (project) sp.set("project", parseAsInteger.serialize(project));
if (pillarsParser.defaultValue !== pillars) sp.set("pillars", pillarsParser.serialize(pillars));

return sp;
};
Expand Down
9 changes: 7 additions & 2 deletions client/src/components/map/layers/projects-layer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { useGetCountries } from "@/types/generated/country";
import { useGetProjects } from "@/types/generated/project";
import { Config, LayerProps } from "@/types/layers";

import { projectSearchAtom } from "@/app/store";
import { projectSearchAtom, useSyncPillars } from "@/app/store";

import { GET_PROJECTS_OPTIONS } from "@/constants/projects";

Expand All @@ -23,7 +23,12 @@ export type ProjectsLayerProps = LayerProps & {

const ProjectsLayer = ({ id, beforeId, config, onAdd, onRemove }: ProjectsLayerProps) => {
const projectSearch = useAtomValue(projectSearchAtom);
const { data: projectsData } = useGetProjects(GET_PROJECTS_OPTIONS(projectSearch));
const [pillars] = useSyncPillars();
const { data: projectsData } = useGetProjects(
GET_PROJECTS_OPTIONS(projectSearch, {
pillars,
}),
);
const { data: countriesData } = useGetCountries({
"pagination[pageSize]": 100,
sort: "name:asc",
Expand Down
170 changes: 170 additions & 0 deletions client/src/components/ui/form.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
import * as React from "react";

import {
Controller,
ControllerProps,
FieldPath,
FieldValues,
FormProvider,
useFormContext,
} from "react-hook-form";

import * as LabelPrimitive from "@radix-ui/react-label";
import { Slot } from "@radix-ui/react-slot";

import { cn } from "@/lib/classnames";

import { Label } from "@/components/ui/label";

const Form = FormProvider;

type FormFieldContextValue<
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
> = {
name: TName;
};

const FormFieldContext = React.createContext<FormFieldContextValue>({} as FormFieldContextValue);

const FormField = <
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
>({
...props
}: ControllerProps<TFieldValues, TName>) => {
return (
<FormFieldContext.Provider value={{ name: props.name }}>
<Controller {...props} />
</FormFieldContext.Provider>
);
};

const useFormField = () => {
const fieldContext = React.useContext(FormFieldContext);
const itemContext = React.useContext(FormItemContext);
const { getFieldState, formState } = useFormContext();

const fieldState = getFieldState(fieldContext.name, formState);

if (!fieldContext) {
throw new Error("useFormField should be used within <FormField>");
}

const { id } = itemContext;

return {
id,
name: fieldContext.name,
formItemId: `${id}-form-item`,
formDescriptionId: `${id}-form-item-description`,
formMessageId: `${id}-form-item-message`,
...fieldState,
};
};

type FormItemContextValue = {
id: string;
};

const FormItemContext = React.createContext<FormItemContextValue>({} as FormItemContextValue);

const FormItem = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
({ className, ...props }, ref) => {
const id = React.useId();

return (
<FormItemContext.Provider value={{ id }}>
<div ref={ref} className={cn("space-y-2", className)} {...props} />
</FormItemContext.Provider>
);
},
);
FormItem.displayName = "FormItem";

const FormLabel = React.forwardRef<
React.ElementRef<typeof LabelPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
>(({ className, ...props }, ref) => {
const { error, formItemId } = useFormField();

return (
<Label
ref={ref}
className={cn(error && "text-destructive", className)}
htmlFor={formItemId}
{...props}
/>
);
});
FormLabel.displayName = "FormLabel";

const FormControl = React.forwardRef<
React.ElementRef<typeof Slot>,
React.ComponentPropsWithoutRef<typeof Slot>
>(({ ...props }, ref) => {
const { error, formItemId, formDescriptionId, formMessageId } = useFormField();

return (
<Slot
ref={ref}
id={formItemId}
aria-describedby={!error ? `${formDescriptionId}` : `${formDescriptionId} ${formMessageId}`}
aria-invalid={!!error}
{...props}
/>
);
});
FormControl.displayName = "FormControl";

const FormDescription = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => {
const { formDescriptionId } = useFormField();

return (
<p
ref={ref}
id={formDescriptionId}
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
);
});
FormDescription.displayName = "FormDescription";

const FormMessage = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, children, ...props }, ref) => {
const { error, formMessageId } = useFormField();
const body = error ? String(error?.message) : children;

if (!body) {
return null;
}

return (
<p
ref={ref}
id={formMessageId}
className={cn("text-sm font-medium text-destructive", className)}
{...props}
>
{body}
</p>
);
});
FormMessage.displayName = "FormMessage";

export {
useFormField,
Form,
FormItem,
FormLabel,
FormControl,
FormDescription,
FormMessage,
FormField,
};
24 changes: 24 additions & 0 deletions client/src/components/ui/input.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import * as React from "react";

import { cn } from "@/lib/classnames";

export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {}

const Input = React.forwardRef<HTMLInputElement, InputProps>(
({ className, type, ...props }, ref) => {
return (
<input
type={type}
className={cn(
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
className,
)}
ref={ref}
{...props}
/>
);
},
);
Input.displayName = "Input";

export { Input };
8 changes: 5 additions & 3 deletions client/src/components/ui/search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { LuSearch, LuX } from "react-icons/lu";

import { cn } from "@/lib/classnames";

import { Input } from "@/components/ui/input";

export interface SearchProps extends React.InputHTMLAttributes<HTMLInputElement> {}

const Search = React.forwardRef<HTMLInputElement, SearchProps>(
Expand All @@ -25,12 +27,12 @@ const Search = React.forwardRef<HTMLInputElement, SearchProps>(
};

return (
<div className="relative flex ">
<div className="relative flex w-full">
<LuSearch className="absolute left-2.5 top-1/2 h-4 w-4 -translate-y-1/2" />
<input
<Input
type={type}
className={cn({
"flex w-full rounded-lg border border-input bg-gray-100 p-3 pl-8 text-sm ring-offset-background":
"flex w-full rounded-lg border-none bg-secondary p-3 pl-8 text-sm ring-offset-background":
true,
"file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:border-primary focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50":
true,
Expand Down
10 changes: 9 additions & 1 deletion client/src/constants/projects.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
export const GET_PROJECTS_OPTIONS = (projectSearch: string | undefined) => ({
export const GET_PROJECTS_OPTIONS = (
projectSearch: string | undefined,
filters: {
pillars: number[];
},
) => ({
"pagination[pageSize]": 200,
populate: {
pillar: {
Expand All @@ -18,6 +23,9 @@ export const GET_PROJECTS_OPTIONS = (projectSearch: string | undefined) => ({
$containsi: projectSearch,
},
}),
...(!!filters?.pillars.length && {
pillar: filters?.pillars,
}),
},
});

Expand Down
Loading

0 comments on commit e712eb1

Please sign in to comment.