diff --git a/packages/ui/src/components/date-input/date-input.test.tsx b/packages/ui/src/components/date-input/date-input.test.tsx index 7604d29e..9a660be8 100644 --- a/packages/ui/src/components/date-input/date-input.test.tsx +++ b/packages/ui/src/components/date-input/date-input.test.tsx @@ -6,9 +6,12 @@ import DateInput from "./date-input"; describe("Date Input", () => { const user = userEvent.setup(); beforeEach(async () => { + const onSelectMock = jest.fn(); render(
- From + + From +
, ); diff --git a/packages/ui/src/components/date-input/date-input.tsx b/packages/ui/src/components/date-input/date-input.tsx index 53c2af08..7ccf712f 100644 --- a/packages/ui/src/components/date-input/date-input.tsx +++ b/packages/ui/src/components/date-input/date-input.tsx @@ -1,3 +1,4 @@ +import type { onEvent, Nil } from "@lucky-parking/typings"; import { formatToMiddleEndian } from "@lucky-parking/utilities/dist/date"; import clsx from "clsx"; import { PropsWithChildren, useEffect, useRef, useState } from "react"; @@ -6,14 +7,20 @@ import Calendar from "../calendar"; interface DateInputProps { id: string; children: PropsWithChildren; + date?: Date | Nil; + onSelect: onEvent; } export default function DateInput({ children, ...props }: PropsWithChildren) { - const { id } = props; + const { id, date, onSelect } = props; const calendarRef = useRef(null); - const [value, setValue] = useState(); + const [value, setValue] = useState(date ? formatToMiddleEndian(date) : null); const [isCalendarVisible, setCalendarVisible] = useState(false); + useEffect(() => { + setValue(date ? formatToMiddleEndian(date) : null); + }, [date]); + useEffect(() => { function handleClickOutside(event: MouseEvent) { if (!calendarRef || calendarRef.current === null) return; @@ -27,6 +34,11 @@ export default function DateInput({ children, ...props }: PropsWithChildren { + setValue(formatToMiddleEndian(newValue)); + onSelect({ id: id, date: newValue }); + }; + return (
{isCalendarVisible && (
- setValue(formatToMiddleEndian(newValue))} /> +
)}
diff --git a/packages/website/src/entities/branding/ui/logo.tsx b/packages/website/src/entities/branding/ui/logo.tsx index 25f642e7..689cf7f0 100644 --- a/packages/website/src/entities/branding/ui/logo.tsx +++ b/packages/website/src/entities/branding/ui/logo.tsx @@ -1,19 +1,24 @@ import BrandMark from "./brandmark.svg"; import { Link } from "react-router-dom"; +import type { onEvent } from "@lucky-parking/typings"; + +type LogoProps = { + onClick?: onEvent; +}; + +export default function Logo(props: LogoProps) { + const { onClick } = props; -export default function Logo() { return ( - /** - * reloadDocument - A property used in the Link component from React Router to skip client side routing and let the browser handle the transition normally (as if it were an ). - * @typedef {boolean} reloadDocument - */ - - Parking Insights brand mark +
+ + Parking Insights brand mark -
-

Parking

-

Insights

-
- +
+

Parking

+

Insights

+
+ +
); } diff --git a/packages/website/src/features/citation/ui/citation-data-category-selection.tsx b/packages/website/src/features/citation/ui/citation-data-category-selection.tsx index 6da8a74b..b9e5000b 100644 --- a/packages/website/src/features/citation/ui/citation-data-category-selection.tsx +++ b/packages/website/src/features/citation/ui/citation-data-category-selection.tsx @@ -5,12 +5,13 @@ import { CitationDataCategories } from "../lib"; interface CitationDataCategorySelectionProps { onSelect: onEvent; + category?: string; } const CITATION_DATA_CATEGORIES = _.map(CitationDataCategories, (value) => ({ value, text: value })); export default function CitationDataCategorySelection(props: CitationDataCategorySelectionProps) { - const { onSelect } = props; + const { onSelect, category } = props; return ( ); } diff --git a/packages/website/src/features/citation/ui/citation-data-filter.test.tsx b/packages/website/src/features/citation/ui/citation-data-filter.test.tsx index 1de3bd1f..1a8c572c 100644 --- a/packages/website/src/features/citation/ui/citation-data-filter.test.tsx +++ b/packages/website/src/features/citation/ui/citation-data-filter.test.tsx @@ -7,7 +7,9 @@ import CitationDataFilter from "./citation-data-filter"; describe("Citation-data-filter", () => { describe("rendering", () => { beforeEach(() => { - render( {}} onDatePresetSelect={() => {}} />); + render( + {}} onDatePresetSelect={() => {}} onCustomDateSelect={() => {}} />, + ); }); it("renders citation data categories with initial placeholder", async () => { @@ -27,11 +29,16 @@ describe("Citation-data-filter", () => { describe("interactions", () => { const onCategorySelectMock = jest.fn(); const onDatePresetSelectMock = jest.fn(); + const onCustomDateSelectMock = jest.fn(); beforeEach(async () => { const user = userEvent.setup(); render( - , + , ); await user.click(screen.getByRole("combobox", { name: "citation-data-categories" })); diff --git a/packages/website/src/features/citation/ui/citation-data-filter.tsx b/packages/website/src/features/citation/ui/citation-data-filter.tsx index 921c6d3c..39c26d5a 100644 --- a/packages/website/src/features/citation/ui/citation-data-filter.tsx +++ b/packages/website/src/features/citation/ui/citation-data-filter.tsx @@ -1,4 +1,4 @@ -import type { onEvent } from "@lucky-parking/typings"; +import type { onEvent, Nil } from "@lucky-parking/typings"; import DateInput from "@lucky-parking/ui/src/components/date-input"; import CitationDataCategorySelection from "./citation-data-category-selection"; import CitationDatePresetsSelection from "./citation-date-presets-selection"; @@ -6,23 +6,37 @@ import CitationDatePresetsSelection from "./citation-date-presets-selection"; interface CitationDataFilterProps { onCategorySelect: onEvent; onDatePresetSelect: onEvent; + onCustomDateSelect: onEvent; + category?: string; + datePreset?: string; + customDateFromInput?: Date | Nil; + customDateToInput?: Date | Nil; } export default function CitationDataFilter(props: CitationDataFilterProps) { - const { onCategorySelect, onDatePresetSelect } = props; + const { + onCategorySelect, + onDatePresetSelect, + onCustomDateSelect, + category, + datePreset, + customDateFromInput, + customDateToInput, + } = props; return ( <> - - +
- - +

or

-
- From - To + + From + + + To +
diff --git a/packages/website/src/features/citation/ui/citation-date-presets-selection.tsx b/packages/website/src/features/citation/ui/citation-date-presets-selection.tsx index d0c8fb29..2dc61ff7 100644 --- a/packages/website/src/features/citation/ui/citation-date-presets-selection.tsx +++ b/packages/website/src/features/citation/ui/citation-date-presets-selection.tsx @@ -5,12 +5,13 @@ import { RelativeDatePresets } from "@/shared/lib/constants/date"; interface CitationDataPresetsSelection { onSelect: onEvent; + datePreset?: string; } const CITATION_DATE_PRESETS = _.map(RelativeDatePresets, (value) => ({ value, text: value })); export default function CitationDatePresetsSelection(props: CitationDataPresetsSelection) { - const { onSelect } = props; + const { onSelect, datePreset } = props; return ( ); } diff --git a/packages/website/src/features/citation/ui/use-citation-search-params.ts b/packages/website/src/features/citation/ui/use-citation-search-params.ts new file mode 100644 index 00000000..a92bcd5c --- /dev/null +++ b/packages/website/src/features/citation/ui/use-citation-search-params.ts @@ -0,0 +1,150 @@ +import { useSearchParams } from "react-router-dom"; +import { RelativeDatePresets } from "@/shared/lib/constants/date"; +import { formatToMiddleEndian } from "@lucky-parking/utilities/dist/date"; +import { calculateDateRange } from "@/shared/lib/utilities/date"; + +export default function useCitationSearchParams() { + const [searchParams, setSearchParams] = useSearchParams(); + + const getCategory = () => searchParams.get("category"); + const setCategory = (value: string) => + setSearchParams((prevParams) => { + prevParams.set("category", value); + return prevParams; + }); + + const getDatePreset = () => searchParams.get("date_preset"); + const setDatePreset = (value: RelativeDatePresets) => + setSearchParams((prevParams) => { + prevParams.set("date_preset", value); + return prevParams; + }); + + const getDateRange = () => { + const dateFromSearchQuery = searchParams.get("date_from"); + const dateToSearchQuery = searchParams.get("date_to"); + + if (dateFromSearchQuery && dateToSearchQuery) { + const dateFrom = new Date(dateFromSearchQuery); + const dateTo = new Date(dateToSearchQuery); + return [dateFrom, dateTo]; + } else { + return null; + } + }; + const setDateRange = (value: RelativeDatePresets | Date[]) => + setSearchParams((prevParams) => { + let dateRange: Date[] = []; + if (Array.isArray(value)) { + dateRange = value; + } else { + dateRange = calculateDateRange(value); + } + prevParams.set("date_from", formatToMiddleEndian(dateRange[0])); + prevParams.set("date_to", formatToMiddleEndian(dateRange[1])); + return prevParams; + }); + + const getPlaceName = () => searchParams.get("place_name"); + const setPlaceName = (value: string) => + setSearchParams((prevParams) => { + prevParams.set("place_name", value); + return prevParams; + }); + + const getPlaceType = () => searchParams.get("place_type"); + const setPlaceType = (value: string) => + setSearchParams((prevParams) => { + prevParams.set("place_type", value); + return prevParams; + }); + + const getRegion1 = () => searchParams.get("region1"); + const setRegion1 = (value: string) => + setSearchParams((prevParams) => { + prevParams.set("region1", value); + return prevParams; + }); + const deleteRegion1 = () => + setSearchParams((prevParams) => { + prevParams.delete("region1"); + return prevParams; + }); + + const getRegion2 = () => searchParams.get("region2"); + const setRegion2 = (value: string) => + setSearchParams((prevParams) => { + prevParams.set("region2", value); + return prevParams; + }); + const deleteRegion2 = () => + setSearchParams((prevParams) => { + prevParams.delete("region2"); + return prevParams; + }); + + const getCompareMode = () => searchParams.get("compare_mode"); + const setCompareMode = () => + setSearchParams((prevParams) => { + prevParams.set("compare_mode", "true"); + return prevParams; + }); + + const getVisualizationMode = () => searchParams.get("visualization_mode"); + const setVisualizationMode = () => + setSearchParams((prevParams) => { + prevParams.set("visualization_mode", "true"); + return prevParams; + }); + const deleteVisualizationMode = () => + setSearchParams((prevParams) => { + prevParams.delete("visualization_mode"); + return prevParams; + }); + + const clearSearchParams = () => setSearchParams({}); + + return { + category: { + get: getCategory, + set: setCategory, + }, + datePreset: { + get: getDatePreset, + set: setDatePreset, + }, + dateRange: { + get: getDateRange, + set: setDateRange, + }, + placeName: { + get: getPlaceName, + set: setPlaceName, + }, + placeType: { + get: getPlaceType, + set: setPlaceType, + }, + region1: { + get: getRegion1, + set: setRegion1, + delete: deleteRegion1, + }, + region2: { + get: getRegion2, + set: setRegion2, + delete: deleteRegion2, + }, + compareMode: { + get: getCompareMode, + set: setCompareMode, + }, + visualizationMode: { + get: getVisualizationMode, + set: setVisualizationMode, + delete: deleteVisualizationMode, + }, + clearSearchParams: clearSearchParams, + searchParams: searchParams, + }; +} diff --git a/packages/website/src/features/geocoder/ui/geocoder.tsx b/packages/website/src/features/geocoder/ui/geocoder.tsx index fe16d0b2..50157279 100644 --- a/packages/website/src/features/geocoder/ui/geocoder.tsx +++ b/packages/website/src/features/geocoder/ui/geocoder.tsx @@ -14,26 +14,40 @@ import GeocoderResultHeader from "./geocoder-result-header"; const DEFAULT_PLACEHOLDER = "Neighborhood Council, Zip Code, Address"; interface GeocoderProps { + id?: string; filters?: PlaceType[] | Nil; isDisabled?: boolean; onSelect: onEvent; placeholder?: string; - savedQuery?: GeocodeResult; + savedQuery?: string | Nil; + onClearRegion?: onEvent; } export default function Geocoder(props: GeocoderProps) { - const { filters = [], isDisabled = false, onSelect, placeholder = DEFAULT_PLACEHOLDER, savedQuery } = props; + const { + id, + filters = [], + isDisabled = false, + onSelect, + placeholder = DEFAULT_PLACEHOLDER, + savedQuery, + onClearRegion, + } = props; const dispatch = useDispatch(); const [isSuggestionsVisible, setSuggestionsVisible] = useState(false); - const [query, setQuery] = useState(savedQuery?.place_name || ""); + const [query, setQuery] = useState(savedQuery || ""); const [results, setResults] = useState([]); + const hasSavedQuery = useMemo(() => savedQuery !== null, [savedQuery]); const hasQuery = useMemo(() => query && !_.isEmpty(query), [query]); const hasResults = useMemo(() => results && !_.isEmpty(results), [results]); const onInputChange = (value: string) => { + if (value === "" && onClearRegion) { + onClearRegion({ id: id, value: value }); + } setQuery(value); setSuggestionsVisible(true); }; @@ -68,6 +82,10 @@ export default function Geocoder(props: GeocoderProps) { .slice(0, 5) .value(); setResults(results); + + if (hasSavedQuery) { + onSuggestionClick(results[0]); + } })(); }, [query]); diff --git a/packages/website/src/widgets/citation/ui/explorer/citation-explorer.tsx b/packages/website/src/widgets/citation/ui/explorer/citation-explorer.tsx index 8ec81172..b5cce083 100644 --- a/packages/website/src/widgets/citation/ui/explorer/citation-explorer.tsx +++ b/packages/website/src/widgets/citation/ui/explorer/citation-explorer.tsx @@ -1,5 +1,5 @@ import _ from "lodash"; -import { useState } from "react"; +import { useEffect, useState } from "react"; import { PlaceType, REGION_TYPE_BY_PLACE_TYPE, RegionType } from "@/features/geocoder/lib/constants"; import { selectors } from "@/shared/data/store/ui-slice"; import { GeocodeResult } from "@/shared/lib/types"; @@ -9,12 +9,14 @@ import DrawSearch from "../search/draw-search"; import SingleSearch from "../search/single-seach"; import ComparativeSearchVisualization from "../visualization/comparative-search-visualization"; import SingleSearchVisualization from "../visualization/single-search-visualization"; +import useCitationSearchParams from "@/features/citation/ui/use-citation-search-params"; export default function CitationExplorer() { const ui = useSelector(selectors.selectUi); - const [isSingleSearchMode, setSingleSearchMode] = useState(true); + const citationSearchParams = useCitationSearchParams(); + const [isSingleSearchMode, setSingleSearchMode] = useState(citationSearchParams.compareMode.get() === null); const [isVisualizationMode, setVisualizationMode] = useState(false); - const [regionType, setRegionType] = useState(null); + const [regionType, setRegionType] = useState(citationSearchParams.placeType.get() as RegionType); const [region1, setRegion1] = useState(null); const [region2, setRegion2] = useState(null); @@ -23,18 +25,81 @@ export default function CitationExplorer() { setRegionType(null); setRegion1(null); setRegion2(null); + citationSearchParams.clearSearchParams(); }; - const onVisualizationModeToggle = () => setVisualizationMode((prevState) => !prevState); + const onVisualizationModeToggle = () => { + setVisualizationMode((prevState) => !prevState); + if (isSingleSearchMode) { + citationSearchParams.clearSearchParams(); + } else { + if (citationSearchParams.visualizationMode.get()) { + citationSearchParams.visualizationMode.delete(); + } else { + citationSearchParams.visualizationMode.set(); + } + } + }; + + // For single search mode const onRegionSelect = (feature: GeocodeResult) => { if (_.isNil(feature)) return; setRegion1(feature); setRegionType(REGION_TYPE_BY_PLACE_TYPE[_.first(feature.place_type) as PlaceType]); setVisualizationMode(true); + + if (citationSearchParams.searchParams.size == 0) { + citationSearchParams.placeName.set(feature.place_name); + citationSearchParams.placeType.set(feature.place_type[0]); + } + }; + + // For comparative search mode + const onRegionTypeSelect = (regionType: RegionType) => { + setRegionType(regionType); + citationSearchParams.placeType.set(regionType); + }; + + const onComparativeRegionSelect = (value: { id: string; feature: GeocodeResult }) => { + if (value.id === "region1") { + if (_.isNil(value.feature)) return; + + setRegion1(value.feature); + citationSearchParams.region1.set(value.feature.place_name); + } + if (value.id === "region2") { + if (_.isNil(value.feature)) return; + + setRegion2(value.feature); + citationSearchParams.region2.set(value.feature.place_name); + } + }; + + const onClearRegion = (value: { id: string; value: string }) => { + if (value.id === "region1") { + setRegion1(null); + citationSearchParams.region1.delete(); + } + if (value.id === "region2") { + setRegion2(null); + citationSearchParams.region2.delete(); + } }; + useEffect(() => { + if (citationSearchParams.visualizationMode.get() && region1 && region2) { + setVisualizationMode(true); + } + }, [region1, region2]); + + useEffect(() => { + if (!isSingleSearchMode) { + citationSearchParams.compareMode.set(); + } + }, [isSingleSearchMode]); + if (ui.isMapInstructionsVisible) { return ; } @@ -61,17 +126,23 @@ export default function CitationExplorer() { } return isSingleSearchMode ? ( - + ) : ( ); } diff --git a/packages/website/src/widgets/citation/ui/search/comparative-search.tsx b/packages/website/src/widgets/citation/ui/search/comparative-search.tsx index fb00dc38..8fe02cd7 100644 --- a/packages/website/src/widgets/citation/ui/search/comparative-search.tsx +++ b/packages/website/src/widgets/citation/ui/search/comparative-search.tsx @@ -21,18 +21,30 @@ const GEOCODER_PLACEHOLDERS = { interface ComparativeSearchProps { onClose: onEvent; - onRegion1Select: onEvent; - onRegion2Select: onEvent; + onComparativeRegionSelect: onEvent; onRegionTypeSelect: onEvent; + onClearRegion: onEvent; onSubmit: onEvent; region1: GeocodeResult; region2: GeocodeResult; regionType: RegionType; + region1SavedQuery: string | null; + region2SavedQuery: string | null; } export default function ComparativeSearch(props: ComparativeSearchProps) { - const { onClose, onRegion1Select, onRegion2Select, onRegionTypeSelect, onSubmit, region1, region2, regionType } = - props; + const { + onClose, + onComparativeRegionSelect, + onRegionTypeSelect, + onClearRegion, + onSubmit, + region1, + region2, + regionType, + region1SavedQuery, + region2SavedQuery, + } = props; const [dataFocus, setDataFocus] = useState(null); @@ -98,11 +110,13 @@ export default function ComparativeSearch(props: ComparativeSearchProps) {
onComparativeRegionSelect({ id: "region1", feature: value })} placeholder={GEOCODER_PLACEHOLDERS[regionType] || ""} filters={PLACE_TYPE_BY_REGION_TYPE[regionType]} isDisabled={_.isNil(regionType)} - savedQuery={region1} + savedQuery={region1SavedQuery} + onClearRegion={onClearRegion} />
@@ -110,17 +124,19 @@ export default function ComparativeSearch(props: ComparativeSearchProps) {
onComparativeRegionSelect({ id: "region2", feature: value })} placeholder={GEOCODER_PLACEHOLDERS[regionType] || ""} filters={regionType && PLACE_TYPE_BY_REGION_TYPE[regionType]} isDisabled={_.isNil(regionType)} - savedQuery={region2} + savedQuery={region2SavedQuery} + onClearRegion={onClearRegion} />
- diff --git a/packages/website/src/widgets/citation/ui/search/single-seach.tsx b/packages/website/src/widgets/citation/ui/search/single-seach.tsx index c36de838..b9b6dec1 100644 --- a/packages/website/src/widgets/citation/ui/search/single-seach.tsx +++ b/packages/website/src/widgets/citation/ui/search/single-seach.tsx @@ -1,4 +1,4 @@ -import type { onEvent } from "@lucky-parking/typings"; +import type { onEvent, Nil } from "@lucky-parking/typings"; import Geocoder, { SearchModeToggleButton } from "@/features/geocoder"; import { MapDrawButton } from "@/features/map"; import CitationExplorerDivider from "../explorer/citation-explorer-divider"; @@ -8,17 +8,18 @@ import CitationExplorerSectionTitle from "../explorer/citation-explorer-section- interface SingleSearchProps { onSelect: onEvent; onToggle: onEvent; + savedQuery: string | Nil; } export default function SingleSearch(props: SingleSearchProps) { - const { onSelect, onToggle } = props; + const { onSelect, onToggle, savedQuery } = props; return ( <> Get Citation Data for One Area - +
diff --git a/packages/website/src/widgets/citation/ui/visualization/comparative-search-visualization-focused.tsx b/packages/website/src/widgets/citation/ui/visualization/comparative-search-visualization-focused.tsx index 0a910531..d05ed2b9 100644 --- a/packages/website/src/widgets/citation/ui/visualization/comparative-search-visualization-focused.tsx +++ b/packages/website/src/widgets/citation/ui/visualization/comparative-search-visualization-focused.tsx @@ -35,7 +35,12 @@ export default function ComparativeSearchVisualizationFocused(props: Comparative - + {/* TODO: implement onCustomDateSelect based on date selection approach chosen */} + {}} + /> diff --git a/packages/website/src/widgets/citation/ui/visualization/comparative-search-visualization.tsx b/packages/website/src/widgets/citation/ui/visualization/comparative-search-visualization.tsx index 40d84ce1..2057bc49 100644 --- a/packages/website/src/widgets/citation/ui/visualization/comparative-search-visualization.tsx +++ b/packages/website/src/widgets/citation/ui/visualization/comparative-search-visualization.tsx @@ -9,6 +9,7 @@ import { calculateDateRange } from "@/shared/lib/utilities/date"; import CitationExplorerDivider from "../explorer/citation-explorer-divider"; import CitationExplorerSection from "../explorer/citation-explorer-section"; import CitationExplorerTitle from "../explorer/citation-explorer-title"; +import useCitationSearchParams from "@/features/citation/ui/use-citation-search-params"; interface ComparativeSearchVisualizationProps { onClose: onEvent; @@ -20,7 +21,8 @@ interface ComparativeSearchVisualizationProps { export default function ComparativeSearchVisualization(props: ComparativeSearchVisualizationProps) { const { onClose, region1, region2, regionType } = props; - const [category, setCategory] = useState(getFirst(CitationDataCategories)); + const citationSearchParams = useCitationSearchParams(); + const [category, setCategory] = useState(citationSearchParams.category.get() || getFirst(CitationDataCategories)); const [dates, setDates] = useState(calculateDateRange(getFirst(RelativeDatePresets))); const mockDatasets = [ @@ -38,6 +40,11 @@ export default function ComparativeSearchVisualization(props: ComparativeSearchV setDates(calculateDateRange(preset)); }; + const onCategorySelect = (value: string) => { + setCategory(value); + citationSearchParams.category.set(value); + }; + return ( <> Comparison Data @@ -45,7 +52,13 @@ export default function ComparativeSearchVisualization(props: ComparativeSearchV - + {}} + category={category} + /> diff --git a/packages/website/src/widgets/citation/ui/visualization/single-search-visualization.tsx b/packages/website/src/widgets/citation/ui/visualization/single-search-visualization.tsx index be418b16..5f6c721b 100644 --- a/packages/website/src/widgets/citation/ui/visualization/single-search-visualization.tsx +++ b/packages/website/src/widgets/citation/ui/visualization/single-search-visualization.tsx @@ -1,4 +1,4 @@ -import { useState } from "react"; +import { useEffect, useState } from "react"; import type { onEvent } from "@lucky-parking/typings"; import { formatToRangeString } from "@lucky-parking/utilities/dist/date"; import { getFirst } from "@lucky-parking/utilities/dist/enum"; @@ -12,6 +12,7 @@ import type { RegionType } from "@/features/geocoder"; import CitationExplorerDivider from "../explorer/citation-explorer-divider"; import CitationExplorerSection from "../explorer/citation-explorer-section"; import CitationExplorerSectionTitle from "../explorer/citation-explorer-section-title"; +import useCitationSearchParams from "@/features/citation/ui/use-citation-search-params"; interface SingleSearchVisualizationProps { onClose: onEvent; @@ -22,13 +23,56 @@ interface SingleSearchVisualizationProps { export default function SingleSearchVisualization(props: SingleSearchVisualizationProps) { const { onClose, region, regionType } = props; - const [category, setCategory] = useState(getFirst(CitationDataCategories)); - const [dates, setDates] = useState(calculateDateRange(getFirst(RelativeDatePresets))); + const citationSearchParams = useCitationSearchParams(); + const hasSavedDateRange = citationSearchParams.dateRange.get() !== null; + const [category, setCategory] = useState(citationSearchParams.category.get() || getFirst(CitationDataCategories)); + const [datePreset, setDatePreset] = useState(citationSearchParams.datePreset.get() || getFirst(RelativeDatePresets)); + const [dateRange, setDateRange] = useState( + citationSearchParams.dateRange.get() || calculateDateRange(datePreset), + ); + const [customDateFromInput, setCustomDateFromInput] = useState(hasSavedDateRange ? dateRange[0] : null); + const [customDateToInput, setCustomDateToInput] = useState(hasSavedDateRange ? dateRange[1] : null); + const [customDateToggle, setCustomDateToggle] = useState(false); const onDatePresetSelect = (preset: RelativeDatePresets) => { - setDates(calculateDateRange(preset)); + setCustomDateToggle(false); + setCustomDateFromInput(null); + setCustomDateToInput(null); + + setDateRange(calculateDateRange(preset)); + setDatePreset(preset); + citationSearchParams.dateRange.set(preset); + }; + + const onCategorySelect = (value: string) => { + setCategory(value); + citationSearchParams.category.set(value); + }; + + const onCustomDateSelect = (value: { id: string; date: Date }) => { + if (value.id === "From") { + setCustomDateFromInput(value.date); + } + if (value.id === "To") { + setCustomDateToInput(value.date); + } }; + useEffect(() => { + if (customDateFromInput && customDateToInput) { + setCustomDateToggle(true); + } + }, [customDateFromInput, customDateToInput]); + + useEffect(() => { + if (customDateToggle && customDateFromInput && customDateToInput) { + const a = new Date(customDateFromInput); + const b = new Date(customDateToInput); + setDateRange([a, b]); + citationSearchParams.dateRange.set([a, b]); + } + }, [customDateToggle, customDateFromInput, customDateToInput]); + const mockDataset = [{ data: [] }]; return ( @@ -47,7 +91,15 @@ export default function SingleSearchVisualization(props: SingleSearchVisualizati - + @@ -56,7 +108,7 @@ export default function SingleSearchVisualization(props: SingleSearchVisualizati {}} stat="112,338" title={region.place_name} diff --git a/packages/website/src/widgets/header/header.tsx b/packages/website/src/widgets/header/header.tsx index 280521fd..729ac03c 100644 --- a/packages/website/src/widgets/header/header.tsx +++ b/packages/website/src/widgets/header/header.tsx @@ -2,14 +2,22 @@ import { Link } from "react-router-dom"; import { Logo } from "@/entities/branding"; import { PATHS } from "@/entities/navigation"; import HowItWorksButton from "@/features/instructions/how-it-works-button"; +import useCitationSearchParams from "@/features/citation/ui/use-citation-search-params"; export default function Header() { + const citationSearchParams = useCitationSearchParams(); + + const handleLogoClick = () => { + citationSearchParams.clearSearchParams(); + window.location.reload(); + }; + return (
{/* Branding */}
- + {/* Description */}