Skip to content

Commit

Permalink
Convert grading systems to object upon caching (#45)
Browse files Browse the repository at this point in the history
* Move sort state to query state.

* Save selected columns to localstorage.

* Added 'dodaj plezalisce' cta to the end of crags list.

* Align actions row to center.

* Add message when filters produce no results. Also hide table header in this case.

* Adjust nr label for routes/boulders/both cases.

* Adjust padding on table/compact and fix compact/table switching.

* Fix columns in localstorage.

* Fix range slider so that the thumbs do not exceed container at the far end positions.

* Export approach time filter to component and make it collapsible.

* Add filter chips and reorganize crag filters.

* make cached grading systems an object instead of array

* add types to generated grading systems

---------

Co-authored-by: salamca <[email protected]>
  • Loading branch information
demshy and salamca authored Oct 18, 2024
1 parent 74ad93c commit d30fceb
Show file tree
Hide file tree
Showing 13 changed files with 2,443 additions and 25 deletions.
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import { useEffect, useState } from "react";
import difficultyVotesAction from "./server-actions/difficulty-votes-action";
import { DifficultyVote, Route } from "@/graphql/generated";
import displayDate from "@/utils/display-date";
import Grade, { diffToGrade } from "@/components/grade";
import { pluralizeNoun } from "@/utils/text-helpers";
import { gradingSystems } from "@/utils/grading-systems";

interface Props {
route: Route;
Expand Down
20 changes: 20 additions & 0 deletions src/app/[lang]/crags/components/active-filters.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import FilterChip from "./filter-chip";
import { Filter } from "./filtersHelp";

type TActiveFiltersProps = {
filters: Record<string, Filter>;
};

function ActiveFilters({ filters }: TActiveFiltersProps) {
return (
<div className="flex flex-wrap gap-2">
{Object.values(filters)
.filter((filter) => filter.isActive())
.map((filter) => (
<FilterChip key={filter.label} filter={filter} />
))}
</div>
);
}

export default ActiveFilters;
22 changes: 22 additions & 0 deletions src/app/[lang]/crags/components/filter-chip.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import Button from "@/components/ui/button";
import IconClose from "@/components/ui/icons/close";
import { Filter } from "./filtersHelp";

type TFilterChipProps = {
filter: Filter;
};

function FilterChip({ filter }: TFilterChipProps) {
return (
<div className="inline-flex items-center rounded-lg bg-blue-50 py-px pl-3 pr-2">
<div>
{filter.label}: {filter.valueToString()}
</div>
<Button variant="quaternary" onClick={filter.handleReset}>
<IconClose />
</Button>
</div>
);
}

export default FilterChip;
65 changes: 65 additions & 0 deletions src/app/[lang]/crags/components/filters-pane.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import IconFilter from "@/components/ui/icons/filter";
import { Fragment } from "react";
import { Filter, MultiFilter, RangeFilter } from "./filtersHelp";
import Button from "@/components/ui/button";
import IconReset from "@/components/ui/icons/reset";
import { IconSize } from "@/components/ui/icons/icon-size";

type TFiltersPane = {
open: boolean;
filtersData: Record<string, Filter>;
onResetAll: () => void;
};

function FiltersPane({ open, filtersData, onResetAll }: TFiltersPane) {
/**
* on >=md filters pane is always visible and is displayed as a card
* on <md filters pane slides in from the side when filters are being changed
**/
return (
<div
className={`absolute left-0 w-80 shrink-0 rounded-r-lg bg-neutral-100 transition-transform md:relative md:block md:rounded-lg ${
open ? "translate-x-0" : "-translate-x-80 md:translate-x-0"
}`}
>
<div className="flex px-8 pb-1 pt-6">
<div>
<IconFilter />
</div>
<div className="ml-4">Filtriraj</div>
</div>

{Object.values(filtersData)
.filter((filter) => {
if (
filter instanceof MultiFilter &&
Object.keys(filter.options).length == 0
) {
// do not display a multi filter with no options
return false;
}

if (filter instanceof RangeFilter && filter.min == filter.max) {
// do not display a range filter with same min and max as it makes no sense then
return false;
}

return true;
})
.map((filter) => (
<Fragment key={filter.label}>{filter.renderFilterGroup()}</Fragment>
))}

<div className="mt-5 border-t border-neutral-200 px-8 pb-5 pt-4">
<Button variant="tertiary" onClick={onResetAll}>
<span className="flex gap-2">
<IconReset size={IconSize.regular} />
Ponastavi vse
</span>
</Button>
</div>
</div>
);
}

export default FiltersPane;
Loading

0 comments on commit d30fceb

Please sign in to comment.